diff --git a/.gitignore b/.gitignore index 3b59ff2f5..3c51c1db4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ dist dist/coral-admin/bundle.js .DS_Store *.iml +dump.rdb .env gaba.cfg .idea/ diff --git a/client/coral-embed-stream/src/CommentStream.js b/client/coral-embed-stream/src/CommentStream.js index 40ad72a41..753455d8d 100644 --- a/client/coral-embed-stream/src/CommentStream.js +++ b/client/coral-embed-stream/src/CommentStream.js @@ -7,6 +7,7 @@ import { Notification, notificationActions, authActions, + configActions } from '../../coral-framework'; import CommentBox from '../../coral-plugin-commentbox/CommentBox'; @@ -21,14 +22,17 @@ import LikeButton from '../../coral-plugin-likes/LikeButton'; import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton'; import SignInContainer from '../../coral-sign-in/containers/SignInContainer'; import UserBox from '../../coral-sign-in/components/UserBox'; + import {TabBar, Tab, TabContent, Spinner} from '../../coral-ui'; import SettingsContainer from '../../coral-settings/containers/SettingsContainer'; import RestrictedContent from '../../coral-framework/components/RestrictedContent'; import SuspendedAccount from '../../coral-framework/components/SuspendedAccount'; +import CloseCommentsInfo from '../../coral-framework/components/CloseCommentsInfo'; const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions; const {addNotification, clearNotification} = notificationActions; const {logout, showSignInDialog} = authActions; +const {updateOpenStatus} = configActions; class CommentStream extends Component { @@ -40,6 +44,7 @@ class CommentStream extends Component { }; this.changeTab = this.changeTab.bind(this); + this.toggleStatus = this.toggleStatus.bind(this); } changeTab (tab) { @@ -48,6 +53,10 @@ class CommentStream extends Component { }); } + toggleStatus () { + this.props.updateStatus(this.props.config.status === 'open' ? 'closed' : 'open'); + } + static propTypes = { items: PropTypes.object.isRequired, addItem: PropTypes.func.isRequired, @@ -81,6 +90,7 @@ class CommentStream extends Component { const rootItem = this.props.items.assets && this.props.items.assets[rootItemId]; const {actions, users, comments} = this.props.items; const {loggedIn, user, showSignInDialog} = this.props.auth; + const {status} = this.props.config; const {activeTab} = this.state; return
@@ -90,135 +100,145 @@ class CommentStream extends Component { Settings + Configure Stream - {loggedIn && } + {loggedIn && } {/* Add to the restricted param a boolean if the user is suspended*/} }> -
- - + { + status === 'open' + ?
+ + +
+ :

Comments are closed for this thread.

+ } {!loggedIn && } -
- { - rootItem.comments && rootItem.comments.map((commentId) => { - const comment = comments[commentId]; - return
-
- - - -
- { + const comment = comments[commentId]; + return
+
+ + + +
+ + +
+
+ + +
+ - -
-
- - -
- - { - comment.children && - comment.children.map((replyId) => { - let reply = this.props.items.comments[replyId]; - return
-
- - - -
- { + let reply = this.props.items.comments[replyId]; + return
+
+ + + +
+ + +
+
+ + +
+ - -
-
- - -
- -
; - }) +
; + }) }
; - }) - } + }) + } + - {!loggedIn && } + + +

{status === 'open' ? 'Close' : 'Open'} Comment Stream

+ + +
: - + }
; } @@ -263,6 +288,7 @@ const mapDispatchToProps = (dispatch) => ({ appendItemArray: (item, property, value, addToFront, itemType) => dispatch(appendItemArray(item, property, value, addToFront, itemType)), handleSignInDialog: () => dispatch(authActions.showSignInDialog()), logout: () => dispatch(logout()), + updateStatus: status => dispatch(updateOpenStatus(status)) }); export default connect(mapStateToProps, mapDispatchToProps)(CommentStream); diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 21e40ce98..59efc3dc8 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -197,3 +197,42 @@ hr { float: right; margin: 8px; } + +/* Close comments */ + +.close-comments-intro-wrapper { + display: flex; + justify-content: space-between; + align-items: center; +} + +.close-comments-intro-wrapper button { + width: 300px; + margin-left: 20px; +} + +.close-comments-intro-wrapper button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.close-comments-message { + box-sizing: border-box; + width: 100%; + height: 100px; +} + +.close-comments-confirm-wrapper { + float: right; +} + +.close-comments-alert { + background-color: #d65344; + color: white; + font-size: 16px; + padding: 5px; +} + +.close-comments-alert i.material-icons { + font-size: 16px !important; +} diff --git a/client/coral-framework/actions/config.js b/client/coral-framework/actions/config.js new file mode 100644 index 000000000..6dc880434 --- /dev/null +++ b/client/coral-framework/actions/config.js @@ -0,0 +1,18 @@ +import coralApi from '../helpers/response'; +/* Config Actions */ + +/** + * Action name constants + */ + +export const OPEN_COMMENTS = 'OPEN_COMMENTS'; +export const CLOSE_COMMENTS = 'CLOSE_COMMENTS'; +export const ADD_ITEM = 'ADD_ITEM'; + +export const updateOpenStatus = status => (dispatch, getState) => { + const assetId = getState().items.get('assets') + .keySeq() + .toArray()[0]; + return coralApi(`/asset/${assetId}/status?status=${status}`, {method: 'PUT'}) + .then(() => dispatch({type: status === 'open' ? OPEN_COMMENTS : CLOSE_COMMENTS})); +}; diff --git a/client/coral-framework/components/CloseCommentsInfo.js b/client/coral-framework/components/CloseCommentsInfo.js new file mode 100644 index 000000000..627328f9b --- /dev/null +++ b/client/coral-framework/components/CloseCommentsInfo.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {Button} from 'coral-ui'; + +export default ({status, onClick}) => ( + status === 'open' ? ( +
+

+ This comment stream is currently open. By closing this comment stream, + no new comments may be submitted and all previous comments will still + be displayed. +

+ +
+ ) : ( +
+

+ This comment stream is currently closed. By opening this comment stream, + new comments may be submitted and displayed +

+ +
+ ) +); diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js index 7b721badc..9a679d969 100644 --- a/client/coral-framework/index.js +++ b/client/coral-framework/index.js @@ -4,6 +4,7 @@ import * as itemActions from './actions/items'; import I18n from './modules/i18n/i18n'; import * as notificationActions from './actions/notification'; import * as authActions from './actions/auth'; +import * as configActions from './actions/config'; export { Notification, @@ -12,4 +13,5 @@ export { I18n, notificationActions, authActions, + configActions }; diff --git a/client/coral-framework/reducers/config.js b/client/coral-framework/reducers/config.js index 6521d92a3..9620f5b97 100644 --- a/client/coral-framework/reducers/config.js +++ b/client/coral-framework/reducers/config.js @@ -1,19 +1,30 @@ /* @flow */ import {Map} from 'immutable'; -import * as actions from '../actions/items'; +import * as actions from '../actions/config'; const initialState = Map({ - features: Map({}) + features: Map({}), + status: 'open' }); export default (state = initialState, action) => { switch(action.type) { - // Override config if worked case actions.UPDATE_SETTINGS: return action.config; + case actions.OPEN_COMMENTS: + return state.set('status', 'open'); + + case actions.CLOSE_COMMENTS: + return state.set('status', 'closed'); + + case actions.ADD_ITEM: + return action.item_type === 'assets' ? + state.set('status', action.item.status) + : state; + default: return state; } diff --git a/models/asset.js b/models/asset.js index 00251985a..165ebbc6c 100644 --- a/models/asset.js +++ b/models/asset.js @@ -34,7 +34,11 @@ const AssetSchema = new Schema({ subsection: String, author: String, publication_date: Date, - modified_date: Date + modified_date: Date, + status: { + type: String, + default: 'open' + } }, { versionKey: false, timestamps: { diff --git a/routes/api/asset/index.js b/routes/api/asset/index.js index 96b83a969..05926bc15 100644 --- a/routes/api/asset/index.js +++ b/routes/api/asset/index.js @@ -96,4 +96,12 @@ router.put('/:asset_id/settings', (req, res, next) => { }); +router.put('/:asset_id/status', (req, res, next) => { + // Update the asset status + Asset + .update({id: req.params.asset_id}, {status: req.query.status}) + .then(() => res.status(204).end()) + .catch((err) => next(err)); +}); + module.exports = router;