diff --git a/app.js b/app.js index 29817c970..04e4222cd 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,12 @@ const path = require('path'); const app = express(); // Middleware declarations. -app.use(morgan('dev')); + +// Add the logging middleware only if we aren't testing. +if (app.get('env') !== 'test') { + app.use(morgan('dev')); +} + app.use(bodyParser.json()); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); diff --git a/bin/cli b/bin/cli new file mode 100755 index 000000000..bec5ea84f --- /dev/null +++ b/bin/cli @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); +const pkg = require('../package.json'); + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .version(pkg.version) + .command('settings', 'work with the application settings') + .command('users', 'work with the application auth') + .parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/cli-settings b/bin/cli-settings new file mode 100755 index 000000000..0920473da --- /dev/null +++ b/bin/cli-settings @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .command('init') + .description('initilizes the talk settings') + .action(() => { + const mongoose = require('../mongoose'); + const Setting = require('../models/setting'); + const defaults = {id: '1', moderation: 'pre'}; + + Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true}) + .then(() => { + console.log('Created settings object.'); + mongoose.disconnect(); + }).catch((err) => { + console.error(`failed to create the settings object ${JSON.stringify(err)}`); + throw new Error(err); // just to be safe + }); + }); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/cli-users b/bin/cli-users new file mode 100755 index 000000000..74aa902eb --- /dev/null +++ b/bin/cli-users @@ -0,0 +1,408 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); +const pkg = require('../package.json'); +const prompt = require('prompt'); + +/** + * Prompts for input and registers a user based on those. + */ +function createUser(options) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + return new Promise((resolve, reject) => { + + if (options.flag_mode) { + return resolve({ + email: options.email, + password: options.password, + displayName: options.name, + }); + } + + prompt.start(); + + prompt.get([ + { + name: 'email', + description: 'Email', + format: 'email', + required: true + }, + { + name: 'password', + description: 'Password', + hidden: true, + required: true + }, + { + name: 'confirmPassword', + description: 'Confirm Password', + hidden: true, + required: true + }, + { + name: 'displayName', + description: 'Display Name', + required: true + } + ], (err, result) => { + if (err) { + return reject(err); + } + + if (result.password !== result.confirmPassword) { + return reject(new Error('Passwords do not match')); + } + + resolve(result); + }); + }) + .then((result) => { + return User.createLocalUser(result.email.trim(), result.password.trim(), result.displayName.trim()); + }).then((user) => { + console.log(`Created user ${user.id}.`); + mongoose.disconnect(); + }).catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Deletes a user. + */ +function deleteUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .findOneAndRemove({ + id: userID + }) + .then(() => { + console.log('Deleted user'); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Changes the password for a user. + */ +function passwd(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + prompt.start(); + + prompt.get([ + { + name: 'password', + description: 'Password', + hidden: true, + required: true + }, + { + name: 'confirmPassword', + description: 'Confirm Password', + hidden: true, + required: true + } + ], (err, result) => { + if (err) { + console.error(err); + mongoose.disconnect(); + return; + } + + if (result.password !== result.confirmPassword) { + console.error(new Error('Password mismatch')); + mongoose.disconnect(); + return; + } + + User + .changePassword(userID, result.password) + .then(() => { + console.log('Password changed.'); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); + }); +} + +/** + * Updates the user from the options array. + */ +function updateUser(userID, options) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + const updates = []; + + if (options.email && typeof options.email === 'string' && options.email.length > 0) { + let q = User.update({ + 'id': userID, + 'profiles.provider': 'local' + }, { + $set: { + 'profiles.$.id': options.email + } + }); + + updates.push(q); + } + + if (options.name && typeof options.name === 'string' && options.name.length > 0) { + let q = User.update({ + 'id': userID + }, { + $set: { + displayName: options.name + } + }); + + updates.push(q); + } + + Promise + .all(updates.map((q) => q.exec())) + .then(() => { + console.log(`User ${userID} updated.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Lists all the users registered in the database. + */ +function listUsers() { + const Table = require('cli-table'); + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .find() + .then((users) => { + let table = new Table({ + head: [ + 'ID', + 'Display Name', + 'Profiles', + 'Roles', + 'State' + ] + }); + + users.forEach((user) => { + table.push([ + user.id, + user.displayName, + user.profiles.map((p) => p.provider).join(', '), + user.roles.join(', '), + user.disabled ? 'Disabled' : 'Enabled' + ]); + }); + + console.log(table.toString()); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Merges two users using the specified ID's. + * @param {String} dstUserID id of the user to which is the target of the merge + * @param {String} srcUserID id of the user to which is the source of the merge + */ +function mergeUsers(dstUserID, srcUserID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .mergeUsers(dstUserID, srcUserID) + .then(() => { + console.log(`User ${srcUserID} was merged into user ${dstUserID}.`); + mongoose.disconnect(); + }).catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Adds a role to a user + * @param {String} userUD id of the user to add the role to + * @param {String} role the role to add + */ +function addRole(userID, role) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .addRoleToUser(userID, role) + .then(() => { + console.log(`Added the ${role} role to User ${userID}.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Removes a role from a user + * @param {String} userUD id of the user to remove the role from + * @param {String} role the role to remove + */ +function removeRole(userID, role) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .removeRoleFromUser(userID, role) + .then(() => { + console.log(`Removed the ${role} role from User ${userID}.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Disable a given user. + * @param {String} userID the ID of a user to disable + */ +function disableUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .disableUser(userID) + .then(() => { + console.log(`User ${userID} was disabled.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Enabled a given user. + * @param {String} userID the ID of a user to enable + */ +function enableUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .enableUser(userID) + .then(() => { + console.log(`User ${userID} was enabled.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .version(pkg.version); + +program + .command('create') + .option('--email [email]', 'Email to use') + .option('--password [password]', 'Password to use') + .option('--name [name]', 'Name to use') + .option('-f, --flag_mode', 'Source from flags instead of prompting') + .description('create a new user') + .action(createUser); + +program + .command('delete ') + .description('delete a user') + .action(deleteUser); + +program + .command('passwd ') + .description('change a password for a user') + .action(passwd); + +program + .command('update ') + .option('--email [email]', 'Email to use') + .option('--name [name]', 'Name to use') + .description('update a user') + .action(updateUser); + +program + .command('list') + .description('list all the users in the database') + .action(listUsers); + +program + .command('merge ') + .description('merge srcUser into the dstUser') + .action(mergeUsers); + +program + .command('addrole ') + .description('adds a role to a given user') + .action(addRole); + +program + .command('removerole ') + .description('removes a role from a given user') + .action(removeRole); + +program + .command('disable ') + .description('disable a given user from logging in') + .action(disableUser); + +program + .command('enable ') + .description('enable a given user from logging in') + .action(enableUser); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/init.js b/bin/init.js deleted file mode 100644 index d8be5a6fa..000000000 --- a/bin/init.js +++ /dev/null @@ -1,12 +0,0 @@ -const mongoose = require('../mongoose'); -const Setting = require('../models/setting'); -const defaults = {id: '1', moderation: 'pre'}; - -Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true}) - .then(() => { - console.log('Created settings object.'); - mongoose.disconnect(); - }).catch((err) => { - console.error(`failed to create the settings object ${JSON.stringify(err)}`); - throw new Error(err); // just to be safe - }); diff --git a/bin/www b/bin/www index 94408f5e8..af91c2c40 100755 --- a/bin/www +++ b/bin/www @@ -40,7 +40,7 @@ server.on('listening', onListening); */ function normalizePort(val) { - var port = parseInt(val, 10); + let port = parseInt(val, 10); if (isNaN(port)) { // named pipe @@ -64,23 +64,21 @@ function onError(error) { throw error; } - var bind = typeof port === 'string' - ? 'Pipe ' + port - : 'Port ' + port; + let bind = typeof port === 'string' + ? `Pipe ${ port}` + : `Port ${ port}`; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': - console.error(bind + ' requires elevated privileges'); - process.exit(1); + console.error(`${bind} requires elevated privileges`); break; case 'EADDRINUSE': - console.error(bind + ' is already in use'); - process.exit(1); + console.error(`${bind} is already in use`); break; - default: - throw error; } + + throw error; } /** @@ -88,9 +86,9 @@ function onError(error) { */ function onListening() { - var addr = server.address(); - var bind = typeof addr === 'string' - ? 'pipe ' + addr - : 'port ' + addr.port; - debug('Listening on ' + bind); + let addr = server.address(); + let bind = typeof addr === 'string' + ? `pipe ${ addr}` + : `port ${ addr.port}`; + debug(`Listening on ${ bind}`); } diff --git a/client/coral-admin/src/services/talk-adapter.js b/client/coral-admin/src/services/talk-adapter.js index ba8ec369b..9f4dd4715 100644 --- a/client/coral-admin/src/services/talk-adapter.js +++ b/client/coral-admin/src/services/talk-adapter.js @@ -45,15 +45,16 @@ Promise.all([fetch('/api/v1/comments/status/pending'), fetch('/api/v1/comments/s .catch(error => store.dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH_FAILED', error})); // Update a comment. Now to update a comment we need to send back the whole object + const updateComment = (store, comment) => { fetch(`/api/v1/comments/${comment.get('id')}/status`, { method: 'POST', headers: jsonHeader, body: JSON.stringify({status: comment.get('status')}) }) -.then(res => res.json()) -.then(res => store.dispatch({type: 'COMMENT_UPDATE_SUCCESS', res})) -.catch(error => store.dispatch({type: 'COMMENT_UPDATE_FAILED', error})); + .then(res => res.json()) + .then(res => store.dispatch({type: 'COMMENT_UPDATE_SUCCESS', res})) + .catch(error => store.dispatch({type: 'COMMENT_UPDATE_FAILED', error})); }; // Create a new comment diff --git a/client/coral-embed-stream/src/CommentStream.js b/client/coral-embed-stream/src/CommentStream.js index 76d5a0e9b..28b70841f 100644 --- a/client/coral-embed-stream/src/CommentStream.js +++ b/client/coral-embed-stream/src/CommentStream.js @@ -13,52 +13,52 @@ import Count from '../../coral-plugin-comment-count/CommentCount'; import AuthorName from '../../coral-plugin-author-name/AuthorName'; import {ReplyBox, ReplyButton} from '../../coral-plugin-replies'; import Pym from 'pym.js'; +import FlagButton from '../../coral-plugin-flags/FlagButton'; const {addItem, updateItem, postItem, getStream, postAction, appendItemArray} = itemActions; const {addNotification, clearNotification} = notificationActions; const {setLoggedInUser} = authActions; -@connect( - (state) => { - return { - config: state.config.toJS(), - items: state.items.toJS(), - notification: state.notification.toJS(), - auth: state.auth.toJS() - }; - }, - (dispatch) => { - return { - addItem: (item) => { - return dispatch(addItem(item)); - }, - updateItem: (id, property, value) => { - return dispatch(updateItem(id, property, value)); - }, - postItem: (data, type, id) => { - return dispatch(postItem(data, type, id)); - }, - getStream: (rootId) => { - return dispatch(getStream(rootId)); - }, - addNotification: (type, text) => { - return dispatch(addNotification(type, text)); - }, - clearNotification: () => { - return dispatch(clearNotification()); - }, - setLoggedInUser: (user_id) => { - return dispatch(setLoggedInUser(user_id)); - }, - postAction: (item, action, user) => { - return dispatch(postAction(item, action, user)); - }, - appendItemArray: (item, property, value, addToFront) => { - return dispatch(appendItemArray(item, property, value, addToFront)); - } - }; - } -) +const mapStateToProps = (state) => { + return { + config: state.config.toJS(), + items: state.items.toJS(), + notification: state.notification.toJS(), + auth: state.auth.toJS() + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + addItem: (item, itemType) => { + return dispatch(addItem(item, itemType)); + }, + updateItem: (id, property, value, itemType) => { + return dispatch(updateItem(id, property, value, itemType)); + }, + postItem: (data, type, id) => { + return dispatch(postItem(data, type, id)); + }, + getStream: (rootId) => { + return dispatch(getStream(rootId)); + }, + addNotification: (type, text) => { + return dispatch(addNotification(type, text)); + }, + clearNotification: () => { + return dispatch(clearNotification()); + }, + setLoggedInUser: (user_id) => { + return dispatch(setLoggedInUser(user_id)); + }, + postAction: (item, action, user, itemType) => { + return dispatch(postAction(item, action, user, itemType)); + }, + appendItemArray: (item, property, value, addToFront, itemType) => { + return dispatch(appendItemArray(item, property, value, addToFront, itemType)); + } + }; +}; class CommentStream extends Component { @@ -90,93 +90,92 @@ class CommentStream extends Component { }); } - // TODO: Replace teststream id with id from params + // TODO: Replace teststream id with id from params const rootItemId = 'assetTest'; - const rootItem = this.props.items[rootItemId]; + const rootItem = this.props.items.assets && this.props.items.assets[rootItemId]; return
- { - rootItem ? -
-
- - -
- { - rootItem.comments.map((commentId) => { - const comment = this.props.items[commentId]; - return
-
- - - -
- { - // - } - -
- - { - comment.children && - comment.children.map((replyId) => { - let reply = this.props.items[replyId]; - return
+ { + rootItem + ?
+
+ + +
+ { + rootItem.comments.map((commentId) => { + const comment = this.props.items.comments[commentId]; + return
+
+ + + +
+ + +
+ + { + comment.children && + comment.children.map((replyId) => { + let reply = this.props.items.comments[replyId]; + return

- { - // - } +
; - }) - } -
; - }) - } - -
- : 'Loading' - } -
; - + }) + } +
; + }) + } + +
+ : 'Loading' + } +
; } } -export default CommentStream; +export default connect(mapStateToProps, mapDispatchToProps)(CommentStream); diff --git a/client/coral-framework/__tests__/store/itemReducer.spec.js b/client/coral-framework/__tests__/store/itemReducer.spec.js deleted file mode 100644 index 0e8106bae..000000000 --- a/client/coral-framework/__tests__/store/itemReducer.spec.js +++ /dev/null @@ -1,148 +0,0 @@ -import {Map, fromJS} from 'immutable'; -import {expect} from 'chai'; -import itemsReducer from '../../store/reducers/items'; - -describe ('itemsReducer', () => { - describe('ADD_ITEM', () => { - it('should add an item', () => { - const action = { - type: 'ADD_ITEM', - item: { - type: 'comment', - data: { - content: 'stuff' - }, - item_id: '123' - }, - item_id: '123' - }; - const store = new Map({}); - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - type: 'comment', - data: { - content: 'stuff' - }, - item_id: '123' - }); - }); - }); - - describe ('UPDATE_ITEM', () => { - it ('should update an item', () => { - const action = { - type: 'UPDATE_ITEM', - property: 'stuff', - value: 'things', - item_id: '123' - }; - const store = fromJS({ - '123': { - item_id: '123', - data: { - stuff: 'morestuff' - } - } - }); - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - item_id: '123', - data: { - stuff: 'things' - } - }); - }); - }); - - describe('APPEND_ITEM_ARRAY', () => { - let action; - let store; - - beforeEach (() => { - action = { - type: 'APPEND_ITEM_ARRAY', - property: 'stuff', - value: 'things', - item_id: '123' - }; - store = fromJS({ - '123': { - item_id: '123', - data: { - stuff: ['morestuff'] - } - } - }); - }); - it ('should append to an existing array', () => { - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - item_id: '123', - data: { - stuff: ['morestuff', 'things'] - } - }); - }); - it ('should create a new array', () => { - store = fromJS({ - '123': { - item_id: '123', - data: {} - } - }); - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - item_id: '123', - data: { - stuff: ['things'] - } - }); - }); - }); - - describe('APPEND_ITEM_RELATED', () => { - let action; - let store; - - beforeEach (() => { - action = { - type: 'APPEND_ITEM_RELATED', - property: 'stuff', - value: 'things', - item_id: '123' - }; - store = fromJS({ - '123': { - item_id: '123', - related: { - stuff: ['morestuff'] - } - } - }); - }); - it ('should append to an existing array', () => { - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - item_id: '123', - related: { - stuff: ['morestuff', 'things'] - } - }); - }); - it ('should create a new array', () => { - store = fromJS({ - '123': { - item_id: '123', - related: {} - } - }); - const result = itemsReducer(store, action); - expect(result.get('123').toJS()).to.deep.equal({ - item_id: '123', - related: { - stuff: ['things'] - } - }); - }); - }); -}); diff --git a/client/coral-framework/store/actions/config.js b/client/coral-framework/store/actions/config.js index b529fc313..7eb82d428 100644 --- a/client/coral-framework/store/actions/config.js +++ b/client/coral-framework/store/actions/config.js @@ -21,7 +21,9 @@ export const fetchConfig = () => async (dispatch) => { //TODO: Replace with fetching config from backend // const response = await fetch(`./talk.config.json`) // const json = await response.json() - dispatch({type: FETCH_CONFIG_SUCCESS, config: fromJS({})}); + dispatch({type: FETCH_CONFIG_SUCCESS, config: fromJS({ + notifLength: 4500 + })}); } catch (error) { dispatch({type: FETCH_CONFIG_FAILED}); } diff --git a/client/coral-framework/store/actions/items.js b/client/coral-framework/store/actions/items.js index 56e0aafc3..b690c6f0a 100644 --- a/client/coral-framework/store/actions/items.js +++ b/client/coral-framework/store/actions/items.js @@ -21,13 +21,14 @@ export const APPEND_ITEM_ARRAY = 'APPEND_ITEM_ARRAY'; * */ -export const addItem = (item) => { +export const addItem = (item, item_type) => { if (!item.id) { console.warn('addItem called without an item id.'); } return { type: ADD_ITEM, - item: item, + item, + item_type, id: item.id }; }; @@ -42,23 +43,24 @@ export const addItem = (item) => { * value - the value that the property should be set to * */ - -export const updateItem = (id, property, value) => { +export const updateItem = (id, property, value, item_type) => { return { type: UPDATE_ITEM, id, property, - value + value, + item_type }; }; -export const appendItemArray = (id, property, value, addToFront) => { +export const appendItemArray = (id, property, value, add_to_front, item_type) => { return { type: APPEND_ITEM_ARRAY, id, property, value, - addToFront + add_to_front, + item_type }; }; @@ -80,39 +82,49 @@ export function getStream (assetId) { return fetch(`/api/v1/stream?asset_id=${assetId}`) .then( response => { - return response.ok ? response.json() : Promise.reject(`${response.status } ${ response.statusText}`); + return response.ok ? response.json() : Promise.reject(`${response.status} ${response.statusText}`); } ) .then((json) => { - /* Sort comments by date*/ - let rootComments = []; - let childComments = {}; - json.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - json.forEach(item => { - dispatch(addItem(item)); + /* Add items to the store */ + const itemTypes = Object.keys(json); + for (let i = 0; i < itemTypes.length; i++ ) { + for (let j = 0; j < json[itemTypes[i]].length; j++ ) { + dispatch(addItem(json[itemTypes[i]][j], itemTypes[i])); + } + } + /* Sort comments by date*/ + json.comments.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); + const rels = json.comments.reduce((h, item) => { /* Check for root and child comments. */ if ( item.asset_id === assetId && !item.parent_id) { - rootComments.push(item.id); + h.rootComments.push(item.id); } else if ( item.asset_id === assetId ) { - let children = childComments[item.parent_id] || []; - childComments[item.parent_id] = children.concat(item.id); + let children = h.childComments[item.parent_id] || []; + h.childComments[item.parent_id] = children.concat(item.id); } - }, {}); + return h; + }, {rootComments: [], childComments: {}}); dispatch(addItem({ id: assetId, - comments: rootComments - })); + comments: rels.rootComments, + }, 'assets')); - const keys = Object.keys(childComments); - for (let i = 0; i < keys.length; i++ ) { - dispatch(updateItem(keys[i], 'children', childComments[keys[i]].reverse())); + const childKeys = Object.keys(rels.childComments); + for (let i = 0; i < childKeys.length; i++ ) { + dispatch(updateItem(childKeys[i], 'children', rels.childComments[childKeys[i]].reverse(), 'comments')); + } + + /* Hydrate actions on comments */ + for (let i = 0; i < json.actions.length; i++ ) { + dispatch(updateItem(json.actions[i].item_id, json.actions[i].action_type, json.actions[i].id, 'comments')); } return (json); @@ -178,16 +190,15 @@ export function postItem (item, type, id) { 'Content-Type':'application/json' } }; - console.log('postItem', options); - return fetch(`/api/v1/${ type}`, options) + return fetch(`/api/v1/${type}`, options) .then( response => { return response.ok ? response.json() - : Promise.reject(`${response.status } ${ response.statusText}`); + : Promise.reject(`${response.status} ${response.statusText}`); } ) .then((json) => { - dispatch(addItem({...item, id:json.id})); + dispatch(addItem({...item, id:json.id}, type)); return json.id; }); }; @@ -210,24 +221,28 @@ export function postItem (item, type, id) { * */ -export function postAction (id, type, user_id) { - return (dispatch) => { +export function postAction (item_id, action_type, user_id, item_type) { + return () => { const action = { - type, + action_type, user_id }; const options = { method: 'POST', + headers: { + 'Content-Type':'application/json' + }, body: JSON.stringify(action) }; - dispatch(appendItemArray(id, type, user_id)); - return fetch(`/api/v1/comments/${ id }/actions`, options) + return fetch(`/api/v1/${item_type}/${item_id}/actions`, options) .then( response => { - return response.ok ? response.text() - : Promise.reject(`${response.status } ${ response.statusText}`); + return response.ok ? response.json() + : Promise.reject(`${response.status} ${response.statusText}`); } - ); + ).then((json)=>{ + return json; + }); }; } diff --git a/client/coral-framework/store/reducers/items.js b/client/coral-framework/store/reducers/items.js index 77ee44130..17569c545 100644 --- a/client/coral-framework/store/reducers/items.js +++ b/client/coral-framework/store/reducers/items.js @@ -3,26 +3,27 @@ import {fromJS} from 'immutable'; import * as actions from '../actions/items'; -const initialState = fromJS({}); +const initialState = fromJS({ + comments: {}, + users: {}, + actions: {} +}); export default (state = initialState, action) => { switch (action.type) { case actions.ADD_ITEM: - return state.set(action.id, fromJS(action.item)); + return state.setIn([action.item_type, action.id], fromJS(action.item)); case actions.UPDATE_ITEM: - return state.updateIn([action.id, action.property], () => - fromJS(action.value) - ); + return state.setIn([action.item_type, action.id, action.property], fromJS(action.value)); case actions.APPEND_ITEM_ARRAY: - return state.updateIn([action.id, action.property], (prop) => { - if (action.addToFront) { - return prop ? prop.unshift(action.value) : fromJS([action.value]); + return state.updateIn([action.item_type, action.id, action.property], (prop) => { + console.log(prop); + if (action.add_to_front) { + return prop ? prop.unshift(fromJS(action.value)) : fromJS([action.value]); } else { - return prop ? prop.push(action.value) : fromJS([action.value]); + return prop ? prop.push(fromJS(action.value)) : fromJS([action.value]); } - - } - ); + }); default: return state; } diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index cf428fac2..a4e32d73b 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -26,16 +26,19 @@ class CommentBox extends Component { username: this.state.username }; let related; + let parent_type; if (parent_id) { comment.parent_id = parent_id; related = 'children'; + parent_type = 'comments'; } else { related = 'comments'; + parent_type = 'assets'; } - updateItem(parent_id, 'showReply', false); + updateItem(parent_id, 'showReply', false, 'comments'); postItem(comment, 'comments') .then((comment_id) => { - appendItemArray((parent_id || id, related, comment_id, parent_id)); + appendItemArray(parent_id || id, related, comment_id, !parent_id, parent_type); addNotification('success', 'Your comment has been posted.'); }).catch((err) => console.error(err)); this.setState({body: ''}); @@ -45,9 +48,9 @@ class CommentBox extends Component { const {styles, reply} = this.props; // How to handle language in plugins? Should we have a dependency on our central translation file? return
-
+
this.setState({username: e.target.value})}/>
+ className={`${name}-container`}>