From 9eac05c8fb1117434cabfc087e507012dc47eed2 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Wed, 17 May 2017 13:18:59 -0700 Subject: [PATCH 1/9] Ignored user tombstones work again --- .../src/components/Stream.js | 15 ++++--- .../src/containers/Stream.js | 4 ++ .../coral-embed-stream/src/graphql/index.js | 44 ++++++++++++++----- client/coral-framework/reducers/user.js | 11 +---- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index 2297a295b..90fa0aca8 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -25,7 +25,7 @@ class Stream extends React.Component { render() { const { - root: {asset, asset: {comments}, comment, myIgnoredUsers}, + root: {asset, asset: {comments}, comment, me}, postComment, addNotification, postFlag, @@ -58,8 +58,9 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); - const commentIsIgnored = (comment) => - myIgnoredUsers && myIgnoredUsers.includes(comment.user.id); + const commentIsIgnored = (comment) => { + return me && me.ignoredUsers && me.ignoredUsers.find((u) => u.id === comment.user.id); + }; return (
{open @@ -150,8 +151,8 @@ class Stream extends React.Component { />
{comments.map( - (comment) => - (commentIsIgnored(comment) + (comment) => { + return (commentIsIgnored(comment) ? : ) + /> + ); + } )}
{ this.getCounts(this.props.data.variables); @@ -205,6 +206,9 @@ const fragments = { } me { status + ignoredUsers { + id + } } ...${getDefinitionName(Comment.fragments.root)} } diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 5ab1f0c37..09756fcd1 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -122,19 +122,43 @@ const extension = { `, }, mutations: { - IgnoreUser: () => ({ - - // TODO: don't rely on refetching. - refetchQueries: [ - 'EmbedQuery', 'EmbedStreamProfileQuery', - ], + IgnoreUser: ({variables}) => ({ + updateQueries: { + EmbedQuery: (previousData, {mutationResult}) => { + const ignoredUserId = variables.id; + const response = mutationResult.data.ignoreUser; + if (ignoredUserId && !response.errors) { + const updated = update(previousData, {me: {ignoredUsers: {$push: [{ + id: ignoredUserId, + __typename: 'User', + }]}}}); + return updated; + } + return previousData; + } + } }), - StopIgnoringUser: () => ({ - - // TODO: don't rely on refetching. + StopIgnoringUser: ({variables}) => ({ refetchQueries: [ - 'EmbedQuery', 'EmbedStreamProfileQuery', + 'EmbedQuery', ], + updateQueries: { + EmbedStreamProfileQuery: (previousData, {mutationResult}) => { + const noLongerIgnoredUserId = variables.id; + const response = mutationResult.data.stopIgnoringUser; + if (noLongerIgnoredUserId && !response.errors) { + + // remove noLongerIgnoredUserId from ignoredUsers + const updated = update(previousData, {me: {ignoredUsers: { + $apply: (ignoredUsers) => { + return ignoredUsers.filter((u) => u.id !== noLongerIgnoredUserId); + } + }}}); + return updated; + } + return previousData; + } + } }), PostComment: ({ variables: {comment: {asset_id, body, parent_id, tags = []}}, diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index 2969665e3..bc40cf3ae 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -1,4 +1,4 @@ -import {Map, Set} from 'immutable'; +import {Map} from 'immutable'; import * as authActions from '../constants/auth'; import * as actions from '../constants/user'; import * as assetActions from '../constants/assets'; @@ -9,7 +9,6 @@ const initialState = Map({ settings: {}, myComments: [], myAssets: [], // the assets from which myComments (above) originated - ignoredUsers: Set(), }); const purge = (user) => { @@ -39,14 +38,6 @@ export default function user (state = initialState, action) { return state.set('myAssets', action.assets); case actions.LOGOUT_SUCCESS: return initialState; - case 'APOLLO_MUTATION_RESULT': - switch (action.operationName) { - case 'ignoreUser': - return state.updateIn(['ignoredUsers'], (i) => i.add(action.variables.id)); - case 'stopIgnoringUser': - return state.updateIn(['ignoredUsers'], (i) => i.delete(action.variables.id)); - } - break; } return state; } From d93ad38dd18af0af0e97860f0b5d92bb6d4c7d6e Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Wed, 17 May 2017 15:39:19 -0700 Subject: [PATCH 2/9] Admins can change Edit Comment Window length in Moderation Settings --- .../src/containers/Configure/Configure.css | 21 ++++++++------- .../Configure/ModerationSettings.js | 27 +++++++++++++++++++ .../containers/Configure/StreamSettings.js | 2 +- client/coral-admin/src/translations.json | 6 +++++ .../coral-embed-stream/src/graphql/index.js | 6 ++++- graph/resolvers/comment.js | 6 +++-- models/comment.js | 7 ----- models/setting.js | 7 +++++ services/comments.js | 6 +++-- 9 files changed, 66 insertions(+), 22 deletions(-) diff --git a/client/coral-admin/src/containers/Configure/Configure.css b/client/coral-admin/src/containers/Configure/Configure.css index 04ef96dd2..3d5750693 100644 --- a/client/coral-admin/src/containers/Configure/Configure.css +++ b/client/coral-admin/src/containers/Configure/Configure.css @@ -96,24 +96,27 @@ } } +.inlineTextfield { + border-color: #ccc; + border-style: solid; + border-width: 0px 0px 1px 0px; + text-align: center; + font-size: inherit; +} + +.inlineTextfield:focus { + outline: none; +} + .charCountTexfield { width: 4em; padding: 0px; - border-color: #ccc; - border-style: solid; - border-width: 0px 0px 1px 0px; - font-size: 14px; - text-align: center; } .charCountTexfieldEnabled { border-color: #00796b; } -.charCountTexfield:focus { - outline: none; -} - .changedSave { background-color: #00796B; color: white; diff --git a/client/coral-admin/src/containers/Configure/ModerationSettings.js b/client/coral-admin/src/containers/Configure/ModerationSettings.js index 5b7286b98..56be58c02 100644 --- a/client/coral-admin/src/containers/Configure/ModerationSettings.js +++ b/client/coral-admin/src/containers/Configure/ModerationSettings.js @@ -27,6 +27,12 @@ const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => { const on = styles.enabledSetting; const off = styles.disabledSetting; + const onChangeEditCommentWindowLength = (e) => { + const value = e.target.value; + const valueAsNumber = parseFloat(value); + const milliseconds = (!isNaN(valueAsNumber)) && (valueAsNumber * 1000); + updateSettings({editCommentWindowLength: milliseconds || value}); + }; return (
@@ -72,6 +78,27 @@ const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => { bannedWords={settings.wordlist.banned} suspectWords={settings.wordlist.suspect} onChangeWordlist={onChangeWordlist} /> + + {/* Edit Comment Timeframe */} + +
{lang.t('configure.edit-comment-timeframe-heading')}
+

+ {lang.t('configure.edit-comment-timeframe-text-pre')} +   + +   + {lang.t('configure.edit-comment-timeframe-text-post')} +

+
); }; diff --git a/client/coral-admin/src/containers/Configure/StreamSettings.js b/client/coral-admin/src/containers/Configure/StreamSettings.js index 646eb9d9e..341c47ae9 100644 --- a/client/coral-admin/src/containers/Configure/StreamSettings.js +++ b/client/coral-admin/src/containers/Configure/StreamSettings.js @@ -81,7 +81,7 @@ const StreamSettings = ({updateSettings, settingsError, settings, errors}) => {

{lang.t('configure.comment-count-text-pre')} ({ updateQueries: { - EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment: {status}}}}}) => { + EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment, errors}}}}) => { + if (errors && errors.length) { + return previousData; + } + const {status} = comment; const updateCommentWithEdit = (comment, edit) => { const {body} = edit; const editedComment = update(comment, { diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index 8d0473900..6f3313356 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -48,10 +48,12 @@ const Comment = { asset({asset_id}, _, {loaders: {Assets}}) { return Assets.getByID.load(asset_id); }, - editing(comment) { + async editing(comment, _, {loaders: {Settings}}) { + const settings = await Settings.load(); + const editableUntil = new Date(Number(comment.created_at) + settings.editCommentWindowLength); return { edited: comment.edited, - editableUntil: comment.editableUntil + editableUntil: editableUntil }; } }; diff --git a/models/comment.js b/models/comment.js index ddd39c68e..27a2bfae3 100644 --- a/models/comment.js +++ b/models/comment.js @@ -2,8 +2,6 @@ const mongoose = require('../services/mongoose'); const Schema = mongoose.Schema; const uuid = require('uuid'); -const EDIT_WINDOW_MS = 30 * 1000; // 30 seconds - const STATUSES = [ 'ACCEPTED', 'REJECTED', @@ -113,12 +111,7 @@ CommentSchema.virtual('edited').get(function() { return this.body_history.length > 1; }); -CommentSchema.virtual('editableUntil').get(function() { - return new Date(Number(this.created_at) + EDIT_WINDOW_MS); -}); - // Comment model. const Comment = mongoose.model('Comment', CommentSchema); module.exports = Comment; -module.exports.EDIT_WINDOW_MS = EDIT_WINDOW_MS; diff --git a/models/setting.js b/models/setting.js index e63fa8f3e..ecd9dfd26 100644 --- a/models/setting.js +++ b/models/setting.js @@ -88,6 +88,13 @@ const SettingSchema = new Schema({ type: Array, default: ['localhost'] } + }, + + // Length of time (in milliseconds) after a comment is posted that it can still be edited by the author + editCommentWindowLength: { + type: Number, + min: [0, 'Edit Comment Window length must be greater than zero'], + default: 30 * 1000, } }, { timestamps: { diff --git a/services/comments.js b/services/comments.js index 272b19cd0..f5defb935 100644 --- a/services/comments.js +++ b/services/comments.js @@ -1,8 +1,8 @@ const CommentModel = require('../models/comment'); -const EDIT_WINDOW_MS = CommentModel.EDIT_WINDOW_MS; const ActionModel = require('../models/action'); const ActionsService = require('./actions'); +const SettingsService = require('./settings'); const errors = require('../errors'); @@ -53,8 +53,10 @@ module.exports = class CommentsService { // Establish the edit window (if it exists) and add the condition to the // original query. - const lastEditableCommentCreatedAt = new Date((new Date()).getTime() - EDIT_WINDOW_MS); + let lastEditableCommentCreatedAt; if (!ignoreEditWindow) { + const editWindowMs = (await SettingsService.retrieve()).editCommentWindowLength; + lastEditableCommentCreatedAt = new Date((new Date()).getTime() - editWindowMs); query.created_at = { $gt: lastEditableCommentCreatedAt, }; From cb1a4ee1cc2820749e766dea03805b02b80801b7 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Wed, 17 May 2017 15:40:32 -0700 Subject: [PATCH 3/9] remove extraneous console.log --- services/users.js | 1 - 1 file changed, 1 deletion(-) diff --git a/services/users.js b/services/users.js index 9d15e129f..0c83832ab 100644 --- a/services/users.js +++ b/services/users.js @@ -867,6 +867,5 @@ module.exports = class UsersService { ignoresUsers: usersToStopIgnoring } }); - console.log('Mongo wrote stopIgnoringUsers', usersToStopIgnoring); } }; From 340d3cea62e3d9df4a3d110b88a6fdac16867cce Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Thu, 18 May 2017 08:10:20 -0700 Subject: [PATCH 4/9] contains/Stream setCommentCountCache waits for refetch promise --- client/coral-embed-stream/src/containers/Stream.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index b4b800d3c..b54e69dd6 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -112,8 +112,8 @@ class StreamContainer extends React.Component { componentDidMount() { if (this.props.previousTab) { - this.props.data.refetch(); - this.props.setCommentCountCache(0); + this.props.data.refetch() + .then(() => this.props.setCommentCountCache(0)); } this.countPoll = setInterval(() => { this.getCounts(this.props.data.variables); From 6bbeee3a6b5fa942ae5a29079071c54aed25b3a0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Thu, 18 May 2017 08:11:39 -0700 Subject: [PATCH 5/9] StopIgnoringUser does not refetch EmbedQuery --- client/coral-embed-stream/src/graphql/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 09756fcd1..079f0697b 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -139,9 +139,6 @@ const extension = { } }), StopIgnoringUser: ({variables}) => ({ - refetchQueries: [ - 'EmbedQuery', - ], updateQueries: { EmbedStreamProfileQuery: (previousData, {mutationResult}) => { const noLongerIgnoredUserId = variables.id; From fb33eefe8e2da22b6f023c6929ad9a7c0fe41750 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Thu, 18 May 2017 08:19:52 -0700 Subject: [PATCH 6/9] trivial changes requested by review --- .../coral-admin/src/containers/Configure/ModerationSettings.js | 2 +- client/coral-embed-stream/src/graphql/index.js | 2 ++ services/comments.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/containers/Configure/ModerationSettings.js b/client/coral-admin/src/containers/Configure/ModerationSettings.js index 56be58c02..c7df573e8 100644 --- a/client/coral-admin/src/containers/Configure/ModerationSettings.js +++ b/client/coral-admin/src/containers/Configure/ModerationSettings.js @@ -80,7 +80,7 @@ const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => { onChangeWordlist={onChangeWordlist} /> {/* Edit Comment Timeframe */} - +

{lang.t('configure.edit-comment-timeframe-heading')}

{lang.t('configure.edit-comment-timeframe-text-pre')} diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index deb604b5c..145098672 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -201,6 +201,8 @@ const extension = { }) => ({ updateQueries: { EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment, errors}}}}) => { + + // @TODO (kiwi) revisit after streamlining error handling if (errors && errors.length) { return previousData; } diff --git a/services/comments.js b/services/comments.js index f5defb935..def557c96 100644 --- a/services/comments.js +++ b/services/comments.js @@ -55,7 +55,7 @@ module.exports = class CommentsService { // original query. let lastEditableCommentCreatedAt; if (!ignoreEditWindow) { - const editWindowMs = (await SettingsService.retrieve()).editCommentWindowLength; + const {editCommentWindowLength: editWindowMs} = await SettingsService.retrieve(); lastEditableCommentCreatedAt = new Date((new Date()).getTime() - editWindowMs); query.created_at = { $gt: lastEditableCommentCreatedAt, From aaa2da6c237185a7e4dc2b03dcdf107aadf56a26 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Thu, 18 May 2017 10:43:38 -0700 Subject: [PATCH 7/9] Stream container refetch then setCommentCountCache to asset.commentCount instead of silly zero --- client/coral-embed-stream/src/containers/Stream.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index b54e69dd6..467242592 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -113,7 +113,9 @@ class StreamContainer extends React.Component { componentDidMount() { if (this.props.previousTab) { this.props.data.refetch() - .then(() => this.props.setCommentCountCache(0)); + .then(({data: {asset: {commentCount}}}) => { + return this.props.setCommentCountCache(commentCount); + }); } this.countPoll = setInterval(() => { this.getCounts(this.props.data.variables); From c772cffc92708aa72c8f8ecfed80fed0a12dac85 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Thu, 18 May 2017 13:51:48 -0600 Subject: [PATCH 8/9] handle no-interaction input the correct way --- .../coral-admin/src/containers/ModerationQueue/UserDetail.css | 1 - client/coral-admin/src/containers/ModerationQueue/UserDetail.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/coral-admin/src/containers/ModerationQueue/UserDetail.css b/client/coral-admin/src/containers/ModerationQueue/UserDetail.css index 9c735d72b..e37971ada 100644 --- a/client/coral-admin/src/containers/ModerationQueue/UserDetail.css +++ b/client/coral-admin/src/containers/ModerationQueue/UserDetail.css @@ -34,7 +34,6 @@ .profileEmail { border: none; background-color: transparent; - pointer-events: none; font-size: 16px; position: absolute; width: 100%; diff --git a/client/coral-admin/src/containers/ModerationQueue/UserDetail.js b/client/coral-admin/src/containers/ModerationQueue/UserDetail.js index e05741889..d9b85a060 100644 --- a/client/coral-admin/src/containers/ModerationQueue/UserDetail.js +++ b/client/coral-admin/src/containers/ModerationQueue/UserDetail.js @@ -46,7 +46,7 @@ class UserDetail extends React.Component {

{user.username}

- {profile && this.profile = ref} value={profile} />} + {profile && this.profile = ref} value={profile} />}

Member since {new Date(user.created_at).toLocaleString()}


From 25230f2e9d1e2aedcb1663ce731986da275acd4f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 18 May 2017 16:30:28 -0600 Subject: [PATCH 9/9] added fix for chrome + content type negotiation --- app.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 868ceee4c..16840f965 100644 --- a/app.js +++ b/app.js @@ -43,8 +43,15 @@ if (process.env.NODE_ENV === 'production') { app.get('*.js', (req, res, next) => { const accept = accepts(req); if (accept.encoding(['gzip']) === 'gzip') { - req.url = `${req.url}.gz`; + + // Adjsut the headers on the request by adding a content type header + // because express won't be able to detect the mime-type with the .gz + // extension and we need to decalre support for the gzip encoding. + res.set('Content-Type', 'application/javascript'); res.set('Content-Encoding', 'gzip'); + + // Rewrite the url so that the gzip version will be served instead. + req.url = `${req.url}.gz`; } next();