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();
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..c7df573e8 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 (
);
};
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')}
{user.username}
Copy
- {profile && this.profile = ref} value={profile} />}
+ {profile && this.profile = ref} value={profile} />}
Member since {new Date(user.created_at).toLocaleString()}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 023270f14..2cb228dd4 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -106,6 +106,9 @@
"include-text": "Include your text here.",
"enable-premod-links": "Pre-Moderate Comments Containing Links",
"enable-premod-links-text": "Moderators must approve any comment containing a link before its published.",
+ "edit-comment-timeframe-heading": "Edit Comment Timeframe",
+ "edit-comment-timeframe-text-pre": "Commenters will have",
+ "edit-comment-timeframe-text-post": "seconds to edit their comments.",
"comment-settings": "Settings",
"embed-comment-stream": "Embed Stream",
"banned-word-text": "Comments which contain these words or phrases (not case-sensitive) will be automatically removed from the comment stream. Type a word and press Enter or Tab to add. Optionally paste a comma-separated list.",
@@ -310,6 +313,9 @@
"embed-comment-stream": "Colocar Hilo de Comentarios",
"enable-premod-links": "Pre-Moderar Commentarios que contienen Enlaces",
"enable-premod-links-text": "Los y las Moderadoras deben aprobar cualquier comentario que contengan links antes de su publicación.",
+ "edit-comment-timeframe-heading": "Editar Tiempo de Comentario",
+ "edit-comment-timeframe-text-pre": "Los comentaristas tendrán",
+ "edit-comment-timeframe-text-post": "segundos para editar sus comentarios.",
"wordlist": "Palabras Suspendidas y Sospechosas",
"banned-word-text": "Comentarios que contengan estas palabras o frases, no separadas por comas y en mayusculas o minusuculas, serán automaticamente marcadas para separar los comentarios publicados.",
"suspect-word-text": "Comentarios que contengan estas palabras o frases, considerando mayusculas y minusculas, serán automaticamente destacadas en los comentarios publicados. Escribir una palabra y apretar Enter o Tabulador para agergarla. Opcionalmente pegar una lista separada por coma.",
diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js
index 89723f0c0..93bc516c4 100644
--- a/client/coral-embed-stream/src/components/Stream.js
+++ b/client/coral-embed-stream/src/components/Stream.js
@@ -30,7 +30,7 @@ class Stream extends React.Component {
render() {
const {
- root: {asset, asset: {comments}, comment, myIgnoredUsers},
+ root: {asset, asset: {comments}, comment, me},
postComment,
addNotification,
postFlag,
@@ -64,8 +64,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
@@ -162,8 +163,8 @@ class Stream extends React.Component {
/>
{comments.map(
- (comment) =>
- (commentIsIgnored(comment)
+ (comment) => {
+ return (commentIsIgnored(comment)
?
: )
+ />
+ );
+ }
)}
{
+ return this.props.setCommentCountCache(commentCount);
+ });
}
this.countPoll = setInterval(() => {
this.getCounts(this.props.data.variables);
@@ -205,6 +208,9 @@ const fragments = {
}
me {
status
+ ignoredUsers {
+ id
+ }
}
settings {
organizationName
diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js
index 5ab1f0c37..ccf6195f6 100644
--- a/client/coral-embed-stream/src/graphql/index.js
+++ b/client/coral-embed-stream/src/graphql/index.js
@@ -122,19 +122,40 @@ 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: () => ({
+ StopIgnoringUser: ({variables}) => ({
+ updateQueries: {
+ EmbedStreamProfileQuery: (previousData, {mutationResult}) => {
+ const noLongerIgnoredUserId = variables.id;
+ const response = mutationResult.data.stopIgnoringUser;
+ if (noLongerIgnoredUserId && !response.errors) {
- // TODO: don't rely on refetching.
- refetchQueries: [
- 'EmbedQuery', 'EmbedStreamProfileQuery',
- ],
+ // 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 = []}},
@@ -200,7 +221,13 @@ const extension = {
variables: {id, edit},
}) => ({
updateQueries: {
- EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment: {status}}}}}) => {
+ EmbedQuery: (previousData, {mutationResult: {data: {editComment: {comment, errors}}}}) => {
+
+ // @TODO (kiwi) revisit after streamlining error handling
+ if (errors && errors.length) {
+ return previousData;
+ }
+ const {status} = comment;
const updateCommentWithEdit = (comment, edit) => {
const {body} = edit;
const editedComment = update(comment, {
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;
}
diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js
index 8074abe67..7273bf58a 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..def557c96 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 {editCommentWindowLength: editWindowMs} = await SettingsService.retrieve();
+ lastEditableCommentCreatedAt = new Date((new Date()).getTime() - editWindowMs);
query.created_at = {
$gt: lastEditableCommentCreatedAt,
};
diff --git a/services/users.js b/services/users.js
index 2608e24b0..8c193cd5c 100644
--- a/services/users.js
+++ b/services/users.js
@@ -903,6 +903,5 @@ module.exports = class UsersService {
ignoresUsers: usersToStopIgnoring
}
});
- console.log('Mongo wrote stopIgnoringUsers', usersToStopIgnoring);
}
};