From 8be4300ff4449cfdfa6126cdd4a031afb0b0ad17 Mon Sep 17 00:00:00 2001 From: Clint Brown Date: Wed, 9 May 2018 17:41:16 +1000 Subject: [PATCH 01/41] Fix My Profile story title link behavior --- .../coral-embed-stream/src/tabs/profile/components/Comment.css | 1 + client/coral-embed-stream/src/tabs/profile/components/Comment.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/tabs/profile/components/Comment.css b/client/coral-embed-stream/src/tabs/profile/components/Comment.css index 9004a1e6c..c2bf841d5 100644 --- a/client/coral-embed-stream/src/tabs/profile/components/Comment.css +++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.css @@ -31,6 +31,7 @@ font-weight: bold; font-size: 12px; color: #757575; + cursor: pointer; } .commentSummary { diff --git a/client/coral-embed-stream/src/tabs/profile/components/Comment.js b/client/coral-embed-stream/src/tabs/profile/components/Comment.js index 171172dab..84293096b 100644 --- a/client/coral-embed-stream/src/tabs/profile/components/Comment.js +++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.js @@ -76,7 +76,6 @@ class Comment extends React.Component {
{t('common.story')}:{' '} From 1517611c9ed4a36c47211effdae0f0a2a5894c13 Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Wed, 9 May 2018 11:00:02 -0400 Subject: [PATCH 02/41] Update tombstone copy --- locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.yml b/locales/en.yml index e293fb40b..9a26fbdb6 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -274,7 +274,7 @@ en: comment: comment comment_is_ignored: "This comment is hidden because you ignored this user." comment_is_rejected: "You have rejected this comment." - comment_is_deleted: "This comment was deleted." + comment_is_deleted: "This commenter has deleted their account." comment_is_hidden: "This comment is not available." comments: comments configure_stream: "Configure" From 0ded5c72e98799a4cba0959985e7a8e529593776 Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 9 May 2018 14:23:22 -0300 Subject: [PATCH 03/41] adding media --- .../client/components/DeleteMyAccountDialog.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/talk-plugin-profile-data/client/components/DeleteMyAccountDialog.css b/plugins/talk-plugin-profile-data/client/components/DeleteMyAccountDialog.css index 2735474da..26087c534 100644 --- a/plugins/talk-plugin-profile-data/client/components/DeleteMyAccountDialog.css +++ b/plugins/talk-plugin-profile-data/client/components/DeleteMyAccountDialog.css @@ -1,12 +1,19 @@ +@custom-media --small-viewport (min-width: 425px); + .dialog { + width: calc(100% - 50px); border: none; box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); - width: 380px; + top: 10px; font-family: Helvetica, 'Helvetica Neue', Verdana, sans-serif; font-size: 14px; border-radius: 4px; padding: 20px; + + @media (--small-viewport) { + width: 380px; + } } .close { From 4426be5f397d6529f6dd94cb5915bebc5c37041d Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 13:56:05 -0600 Subject: [PATCH 04/41] improved dev experience, fixed css order --- client/coral-admin/src/components/App.css | 9 +++ client/coral-admin/src/components/App.js | 1 + middleware/staticTemplate.js | 8 ++- package.json | 1 + public/css/dev.css | 5 ++ routes/dev/assets.js | 45 ++++++++----- routes/dev/index.js | 2 - services/assets.js | 6 -- views/admin.ejs | 9 +-- views/dev/article.ejs | 80 ++++++++++------------- views/dev/articles.ejs | 50 ++++++++++---- views/embed/stream.ejs | 1 + views/login.ejs | 1 + views/partials/account.ejs | 3 +- views/partials/custom-css.ejs | 1 + views/partials/dev-nav.ejs | 8 +++ views/partials/dev.ejs | 5 ++ views/partials/head.ejs | 3 - yarn.lock | 15 +++++ 19 files changed, 158 insertions(+), 95 deletions(-) create mode 100644 client/coral-admin/src/components/App.css create mode 100644 public/css/dev.css create mode 100644 views/partials/custom-css.ejs create mode 100644 views/partials/dev-nav.ejs create mode 100644 views/partials/dev.ejs diff --git a/client/coral-admin/src/components/App.css b/client/coral-admin/src/components/App.css new file mode 100644 index 000000000..1404a9293 --- /dev/null +++ b/client/coral-admin/src/components/App.css @@ -0,0 +1,9 @@ +html, body, #root, #root > div { + min-height: 100%; +} + +body { + margin: 0; + background-color: #FAFAFA; + font-family: 'Roboto', sans-serif; +} diff --git a/client/coral-admin/src/components/App.js b/client/coral-admin/src/components/App.js index 52535f91a..a7ab1e336 100644 --- a/client/coral-admin/src/components/App.js +++ b/client/coral-admin/src/components/App.js @@ -1,5 +1,6 @@ import React from 'react'; import ToastContainer from './ToastContainer'; +import './App.css'; import 'material-design-lite'; import AppRouter from '../AppRouter'; diff --git a/middleware/staticTemplate.js b/middleware/staticTemplate.js index f16caaea9..d8137693e 100644 --- a/middleware/staticTemplate.js +++ b/middleware/staticTemplate.js @@ -94,9 +94,13 @@ const createResolveFactory = (() => { module.exports = async (req, res, next) => { try { - // Attach the custom css url. - const { customCssUrl } = await SettingsService.select('customCssUrl'); + // Attach the custom css url and organization name. + const { customCssUrl, organizationName } = await SettingsService.select( + 'customCssUrl', + 'organizationName' + ); res.locals.customCssUrl = customCssUrl; + res.locals.organizationName = organizationName; } catch (err) { console.warn(err); } diff --git a/package.json b/package.json index f6ff46a59..01b671dde 100644 --- a/package.json +++ b/package.json @@ -219,6 +219,7 @@ "babel-plugin-dynamic-import-node": "^1.1.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", "browserstack-local": "^1.3.0", + "casual": "^1.5.19", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", "chai-datetime": "^1.5.0", diff --git a/public/css/dev.css b/public/css/dev.css new file mode 100644 index 000000000..73ccaed2c --- /dev/null +++ b/public/css/dev.css @@ -0,0 +1,5 @@ +.container { + width: auto; + max-width: 680px; + padding: 0 15px; +} diff --git a/routes/dev/assets.js b/routes/dev/assets.js index cb1f27e43..aea0628e3 100644 --- a/routes/dev/assets.js +++ b/routes/dev/assets.js @@ -1,49 +1,58 @@ const express = require('express'); const router = express.Router(); - -const errors = require('../../errors'); -const Assets = require('../../services/assets'); - -const body = - 'Lorem ipsum dolor sponge amet, consectetur adipiscing clam. Ut lobortis sollicitudin pillar a ornare. Curabitur dignissim vestibulum cay non rhoncus. Cras laoreet ante vel nunc hendrerit, shelf imperdiet neque egestas. Suspendisse aliquet iaculis fermentum. Talk volutpat, tellus posuere laoreet consequat, mi lacus laoreet massa, sed vehicula mauris velit non lectus. Integer non trust nec neque congue faucibus porttitor sit amet elkhorn.'; +const casual = require('casual'); +const { ErrNotFound } = require('../../errors'); +const Asset = require('../../models/asset'); router.get('/id/:asset_id', async (req, res, next) => { try { - const asset = await Assets.findById(req.params.asset_id); + const asset = await Asset.findOne({ id: req.params.asset_id }); if (asset === null) { - return next(errors.ErrNotFound); + throw new ErrNotFound(); } res.render('dev/article', { title: asset.title, asset_id: asset.id, asset_url: asset.url, - body: '', - basePath: '/client/embed/stream', }); } catch (err) { return next(err); } }); +router.get('/random', (req, res) => { + const title = casual.title; + + res.redirect(`./title/${title.replace(/ /g, '-')}`); +}); + router.get('/title/:asset_title', (req, res) => { - return res.render('dev/article', { + res.render('dev/article', { title: req.params.asset_title.split('-').join(' '), asset_url: '', asset_id: null, - body: body, - basePath: '/client/embed/stream', }); }); router.get('/', async (req, res, next) => { - let skip = req.query.skip ? parseInt(req.query.skip) : 0; - let limit = req.query.limit ? parseInt(req.query.limit) : 25; - try { - const assets = await Assets.all(skip, limit); + const skip = req.query.skip ? parseInt(req.query.skip) : 0; + const limit = req.query.limit ? parseInt(req.query.limit) : 6; + + const [assets, count] = await Promise.all([ + Asset.find({}) + .sort({ created_at: 1 }) + .limit(limit) + .skip(skip), + Asset.count(), + ]); + res.render('dev/articles', { - assets: assets, + skip, + limit, + count, + assets, }); } catch (err) { return next(err); diff --git a/routes/dev/index.js b/routes/dev/index.js index ac72db254..58daca583 100644 --- a/routes/dev/index.js +++ b/routes/dev/index.js @@ -16,8 +16,6 @@ router.get('/', staticTemplate, async (req, res) => { title: 'Coral Talk', asset_url: '', asset_id: '', - body: '', - basePath: '/static/embed/stream', }); } }); diff --git a/services/assets.js b/services/assets.js index 794145eb3..5f1fa989d 100644 --- a/services/assets.js +++ b/services/assets.js @@ -232,11 +232,5 @@ module.exports = class AssetsService { await AssetModel.remove({ id: srcAssetID, }); - - // That's it! - } - - static all(limit = undefined) { - return AssetModel.find({}).limit(limit); } }; diff --git a/views/admin.ejs b/views/admin.ejs index b8f775d79..89cc150f7 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -3,16 +3,11 @@ Talk - Coral Admin - + <%- include partials/head %> - - - <%- include partials/head %> + <%- include partials/custom-css %>
diff --git a/views/dev/article.ejs b/views/dev/article.ejs index 8abfc34af..008d3989e 100644 --- a/views/dev/article.ejs +++ b/views/dev/article.ejs @@ -8,53 +8,45 @@ - - <%= title %> + <%- include ../partials/dev %> -
-

<%= title %>

-

<%= body %>

-

Admin - All Assets

-
- + -
+ * You can listen to events using the example below. + * The argument passed is the event emitter from + * https://github.com/asyncly/EventEmitter2 + * + * events: function(events) { + * events.onAny(function(eventName, data) { + * console.log(eventName, data); + * }); + * }, + */ + plugins_config: { + /** + * You can disable rendering slot components of a plugin by doing: + * + * 'talk-plugin-love': { + * disable_components: true, + * }, + */ + test: 'data', + debug: false + } + }) + +
diff --git a/views/dev/articles.ejs b/views/dev/articles.ejs index 0ce684029..3a67b53c5 100644 --- a/views/dev/articles.ejs +++ b/views/dev/articles.ejs @@ -1,14 +1,40 @@ - -

- Asset list -

-<% assets.forEach(function (asset) { %> - <%= asset.url %>
-<% }) %> -

- (For dev use only. FYI, you can: ?skip=100&limit=25) -

- - + + All Assets + <%- include ../partials/dev %> + + + <%- include ../partials/dev-nav %> +
+
+

All Assets

+ <%= skip + 1 %> - <%= skip + assets.length %> of <%= count %> +
+
+ <% if (skip === 0) { %> Create a random article<% } %> + <% assets.forEach(function (asset) { %> + +
+
<%= asset.title %>
+ Created <%= asset.created_at.toLocaleString('en-US') %> +
+ <%= asset.url %> +
+ <% }) %> +
+ <% if (count !== assets.length) { %> + + <% } %> +
+ diff --git a/views/embed/stream.ejs b/views/embed/stream.ejs index 2027f6069..a1c708b55 100644 --- a/views/embed/stream.ejs +++ b/views/embed/stream.ejs @@ -5,6 +5,7 @@ <%- include ../partials/head %> + <%- include ../partials/custom-css %>
diff --git a/views/login.ejs b/views/login.ejs index c5a86c2fb..671b25c83 100644 --- a/views/login.ejs +++ b/views/login.ejs @@ -6,6 +6,7 @@ <%- include partials/head %> + <%- include partials/custom-css %>
diff --git a/views/partials/account.ejs b/views/partials/account.ejs index 79857baf0..ff2166816 100644 --- a/views/partials/account.ejs +++ b/views/partials/account.ejs @@ -1,3 +1,4 @@ -<%- include ./head %> +<%- include head %> +<%- include custom-css %> diff --git a/views/partials/custom-css.ejs b/views/partials/custom-css.ejs new file mode 100644 index 000000000..d453c37e1 --- /dev/null +++ b/views/partials/custom-css.ejs @@ -0,0 +1 @@ +<% if (locals.customCssUrl) { %><% } %> diff --git a/views/partials/dev-nav.ejs b/views/partials/dev-nav.ejs new file mode 100644 index 000000000..0d6d067d6 --- /dev/null +++ b/views/partials/dev-nav.ejs @@ -0,0 +1,8 @@ + diff --git a/views/partials/dev.ejs b/views/partials/dev.ejs new file mode 100644 index 000000000..691b548e5 --- /dev/null +++ b/views/partials/dev.ejs @@ -0,0 +1,5 @@ + + + + + diff --git a/views/partials/head.ejs b/views/partials/head.ejs index e848b3909..dc3df7103 100644 --- a/views/partials/head.ejs +++ b/views/partials/head.ejs @@ -18,9 +18,6 @@ -<%_ if (locals.customCssUrl) { _%> - -<%_ } _%> <%- include data %> diff --git a/yarn.lock b/yarn.lock index 5cf363b6e..f0da6d006 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1828,6 +1828,13 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +casual@^1.5.19: + version "1.5.19" + resolved "https://registry.yarnpkg.com/casual/-/casual-1.5.19.tgz#66fac46f7ae463f468f5913eb139f9c41c58bbf2" + dependencies: + mersenne-twister "^1.0.1" + moment "^2.15.2" + center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -7154,6 +7161,10 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" +mersenne-twister@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" + metascraper-author@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-3.9.2.tgz#ff2020ac428f59a875d655df3b0d4bea171fde19" @@ -7436,6 +7447,10 @@ moment@^2.10.3: version "2.19.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" +moment@^2.15.2: + version "2.22.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad" + mongodb-core@2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.17.tgz#a418b337a14a14990fb510b923dee6a813173df8" From 55a9c1e6b4a64e5a85556b48c7da4072b636edf9 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:05:16 -0600 Subject: [PATCH 05/41] added link to graphiql --- public/css/dev.css | 4 ++++ views/partials/dev-nav.ejs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/css/dev.css b/public/css/dev.css index 73ccaed2c..2fd6c1982 100644 --- a/public/css/dev.css +++ b/public/css/dev.css @@ -3,3 +3,7 @@ max-width: 680px; padding: 0 15px; } + +.graphiql em { + font-family: georgia; +} diff --git a/views/partials/dev-nav.ejs b/views/partials/dev-nav.ejs index 0d6d067d6..088c52c20 100644 --- a/views/partials/dev-nav.ejs +++ b/views/partials/dev-nav.ejs @@ -2,7 +2,7 @@ <%= organizationName %> Organization
Admin - Development + GraphiQL All Assets
From 302cab8ca02150f1329cd217b65b0d503cdaa6dc Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:12:27 -0600 Subject: [PATCH 06/41] added header to graphiql --- views/api/graphiql.ejs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/views/api/graphiql.ejs b/views/api/graphiql.ejs index b6d609de6..8007569b3 100644 --- a/views/api/graphiql.ejs +++ b/views/api/graphiql.ejs @@ -5,13 +5,17 @@ GraphiQL + <%- include ../partials/dev %> @@ -20,6 +24,8 @@ + <%- include ../partials/dev-nav %> +
- \ No newline at end of file + From 969206f9057ffac2c9b68e110014128b018846c2 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:14:48 -0600 Subject: [PATCH 07/41] copy adjustment --- views/dev/articles.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/dev/articles.ejs b/views/dev/articles.ejs index 3a67b53c5..1b07ed116 100644 --- a/views/dev/articles.ejs +++ b/views/dev/articles.ejs @@ -8,7 +8,7 @@

All Assets

- <%= skip + 1 %> - <%= skip + assets.length %> of <%= count %> + <%= skip + 1 %> - <%= skip + assets.length %> of <%= count %> Assets
<% if (skip === 0) { %> Create a random article<% } %> From 51525de679c3957d583d6b85ad8bf02398556ff5 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:17:43 -0600 Subject: [PATCH 08/41] fixed set-role description --- bin/cli-users | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cli-users b/bin/cli-users index a01980a20..bf34cce97 100755 --- a/bin/cli-users +++ b/bin/cli-users @@ -328,7 +328,7 @@ program .action(searchUsers); program - .command('set-role ') + .command('set-role ') .description('sets the role on a user') .action(setUserRole); From 9af3772087198b9645d5fcfb2733c5f01966b419 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:28:04 -0600 Subject: [PATCH 09/41] Added json output for assets list - Fixes #1597 --- bin/cli-assets | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/bin/cli-assets b/bin/cli-assets index 210b9f22e..62c996412 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -23,23 +23,33 @@ util.onshutdown([() => mongoose.disconnect()]); /** * Lists all the assets registered in the database. */ -async function listAssets() { +async function listAssets(opts) { try { let assets = await AssetModel.find({}).sort({ created_at: 1 }); - let table = new Table({ - head: ['ID', 'Title', 'URL'], - }); + switch (opts.format) { + case 'json': { + console.log(JSON.stringify(assets, null, 2)); + break; + } + default: { + let table = new Table({ + head: ['ID', 'Title', 'URL'], + }); - assets.forEach(asset => { - table.push([ - asset.id, - asset.title ? asset.title : '', - asset.url ? asset.url : '', - ]); - }); + assets.forEach(asset => { + table.push([ + asset.id, + asset.title ? asset.title : '', + asset.url ? asset.url : '', + ]); + }); + + console.log(table.toString()); + break; + } + } - console.log(table.toString()); util.shutdown(); } catch (e) { console.error(e); @@ -202,6 +212,12 @@ async function rewrite(search, replace, options) { program .command('list') + .option( + '--format ', + 'Specify the output format [table]', + /^(table|json)$/i, + 'table' + ) .description('list all the assets in the database') .action(listAssets); From ca3d56def87d1e478fb44a3d29ce82d1dd47ad1d Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 May 2018 14:40:24 -0600 Subject: [PATCH 10/41] asset refresh patched --- bin/cli-assets | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/bin/cli-assets b/bin/cli-assets index 62c996412..5c1afb741 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -59,12 +59,13 @@ async function listAssets(opts) { async function refreshAssets(ageString) { try { - const now = new Date().getTime(); - const ageMs = parseDuration(ageString); - const age = new Date(now - ageMs); + const query = AssetModel.find({}, { id: 1 }); + if (ageString) { + // An age was specified, so filter only those assets. + const ageMs = parseDuration(ageString); + const age = new Date(Date.now() - ageMs); - let assets = await AssetModel.find( - { + query.merge({ $or: [ { scraped: { @@ -75,16 +76,28 @@ async function refreshAssets(ageString) { scraped: null, }, ], - }, - { id: 1 } - ); + }); + } // Create a graph context. const ctx = Context.forSystem(); + // Load the assets. + const cursor = query.cursor(); + // Queue all the assets for scraping. - await Promise.all(assets.map(({ id }) => scraper.create(ctx, id))); - console.log('Assets were queued to be scraped'); + const promises = []; + + let asset = await cursor.next(); + while (asset) { + promises.push(scraper.create(ctx, asset.id)); + asset = await cursor.next(); + } + + await Promise.all(promises); + + console.log(`${promises.length} Assets were queued to be scraped.`); + util.shutdown(); } catch (e) { console.error(e); @@ -222,7 +235,7 @@ program .action(listAssets); program - .command('refresh ') + .command('refresh [age]') .description('queues the assets that exceed the age requested') .action(refreshAssets); From a092a197cffeb3a8a1591a2af4332effaadb7e3f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 9 May 2018 23:45:53 +0200 Subject: [PATCH 11/41] Check correct email --- plugins/talk-plugin-local-auth/client/components/Profile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/talk-plugin-local-auth/client/components/Profile.js b/plugins/talk-plugin-local-auth/client/components/Profile.js index 2b2d4be11..c3b09aab2 100644 --- a/plugins/talk-plugin-local-auth/client/components/Profile.js +++ b/plugins/talk-plugin-local-auth/client/components/Profile.js @@ -114,11 +114,11 @@ class Profile extends React.Component { isSaveEnabled = () => { const { formData } = this.state; - const { emailAddress, username } = this.props; + const { root: { me: { username, email } } } = this.props; const formHasErrors = !!Object.keys(this.state.errors).length; const validUsername = formData.newUsername && formData.newUsername !== username; - const validEmail = formData.newEmail && formData.newEmail !== emailAddress; + const validEmail = formData.newEmail && formData.newEmail !== email; return !formHasErrors && (validUsername || validEmail); }; From 2554b261100ab80d0f38e8c83bdfb78cded75dd6 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Thu, 10 May 2018 10:21:44 -0400 Subject: [PATCH 12/41] Corrections and clarity Fixed the incorrect thresholds and made the description clearer --- docs/source/03-07-product-guide-trust.md | 44 ++++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/docs/source/03-07-product-guide-trust.md b/docs/source/03-07-product-guide-trust.md index c27c59dcc..961cf843d 100644 --- a/docs/source/03-07-product-guide-trust.md +++ b/docs/source/03-07-product-guide-trust.md @@ -3,47 +3,53 @@ title: Trust permalink: /trust/ --- -Trust is a set of components within Talk that incorporate automated moderation -features based on a user's previous behavior. +Trust is a set of components within Talk that incorporate basic automated moderation features based on a user's previous behavior. ## User Karma Score -Using Trust’s calculations, Talk will automatically pre-moderate comments of -users who have a negative karma score. All users start out with a `0` neutral -karma score. If they have a comment approved by a moderator, their score -increases by `1`; if they have a comment rejected by a moderator, it decreases -by `1`. When a commenter is labeled as Unreliable, their comments must be -moderated before they are posted. +Using Trust’s calculations, Talk will automatically hold back, move to the Reported queue, and tag with a 'History' marker, any comments by users who have an Unreliable karma score. (This is for sites who practice post-moderation. If you set pre-moderation of all comments sitewide, this feature has limited use.) -When a commenter has one comment rejected, their next comment must be moderated -once in order to post freely again. If they instead get rejected again, then -they must have two of their comments approved in order to get added back to the -queue. +All users start out with a Neutral karma score (`0`). If they have a comment approved by a moderator, their score increases by `1`; if they have a comment rejected by a moderator, it decreases by `1`. When a commenter's score is labeled as Unreliable, their comments must be approved from the Reported queue before they are posted. Commenters are shown a message stating that a moderator will review their comment shortly. Here are the default thresholds: ```text --2 and lower: Unreliable --1 to +2: Neutral -+3 and higher: Reliable +-1 and lower: Unreliable +0 to +1: Neutral ++1 and higher: Reliable (we don't do anything with this label right now) ``` -You can configure your own Trust thresholds by using [TRUST_THRESHOLD](/talk/advanced-configuration/#trust-thresholds) in your -configuration. +So in this case, when a new commenter has their first comment rejected, their user karma score becomes `-1`, which triggers the Unreliable threshhold, and they must then have a comment approved by a moderator in order to post freely again. Until that occurs, all of their comments will be held back temporarily in the Reported queue, marked with a `History` tag. + +If their next comment is also rejected, their user karma score is now `-2`, and they must have two comments approved in order to reach a Neutral score, and post without pre-approval. + +We strongly recommend not telling your community how this system works, or where the threshholds lie. Firstly, they might try to game the system to meet approval, and secondly, it makes it harder for you to change the threshhold in the future. We suggest using language such as "We hold back comments for approval for a variety of reasons, including content, account history, and more." + +If you see that a high proportion of first-time commenters on your site are abusive, you might want to change the threshhold to `0`, at least temporarily. You can configure your own Trust thresholds by using [TRUST_THRESHOLD](/talk/advanced-configuration/#trust-thresholds) in your site configuration. ## Reliable and Unreliable Flaggers Trust also calculates how reliable users are in terms of the comments they report. This information is displayed to moderators in the User History drawer, -which is accessed by clicking on a user’s name in the Admin. +which is accessed by clicking on a user’s name in the Admin. Currently, no other action is taken based on this score. If a user's reports mostly match what moderators reject, their Report status will display to moderators as Reliable in the user information drawer. If a user's reports mostly differ from what moderators reject, their Report status will show as Unreliable. -If we don't have enough reports to make a call, or the reports even out, their +If Talk doesn't have enough reports to make a call, or the reports even out, their status is Neutral. +Here are the default thresholds: + +```text +-1 and lower: Unreliable +0 to +1: Neutral ++2 and higher: Reliable +``` +You can configure your own Trust thresholds by using [TRUST_THRESHOLD](/talk/advanced-configuration/#trust-thresholds) in your +configuration. + Note: Report Karma doesn't include reports of "I don't agree with this comment". From 2e37f78705dbaafd27202ea054dde01773f0fd57 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 10 May 2018 13:31:00 -0600 Subject: [PATCH 13/41] Email Attach Fixes --- client/coral-framework/components/Popup.js | 16 ++++++++++----- .../talk-plugin-local-auth/client/actions.js | 9 +++++++++ .../components/AddEmailAddressDialog.js | 20 ++++++++++++++++--- .../client/components/AddEmailContent.js | 9 +++------ .../client/constants.js | 4 ++++ .../containers/AddEmailAddressDialog.js | 16 +++++++++++---- .../talk-plugin-local-auth/client/index.js | 4 +++- .../talk-plugin-local-auth/client/reducer.js | 20 +++++++++++++++++++ plugins/talk-plugin-local-auth/index.js | 2 +- .../server/translations.yml | 9 --------- .../{client => }/translations.yml | 10 +++++++++- 11 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 plugins/talk-plugin-local-auth/client/actions.js create mode 100644 plugins/talk-plugin-local-auth/client/constants.js create mode 100644 plugins/talk-plugin-local-auth/client/reducer.js delete mode 100644 plugins/talk-plugin-local-auth/server/translations.yml rename plugins/talk-plugin-local-auth/{client => }/translations.yml (91%) diff --git a/client/coral-framework/components/Popup.js b/client/coral-framework/components/Popup.js index 53d8f7cd8..339ddb806 100644 --- a/client/coral-framework/components/Popup.js +++ b/client/coral-framework/components/Popup.js @@ -37,7 +37,8 @@ export default class Popup extends Component { this.onBlur(); }; - // Use `onunload` instead of `onbeforeunload` which is not supported in IOS Safari. + // Use `onunload` instead of `onbeforeunload` which is not supported in iOS + // Safari. this.ref.onunload = () => { this.onUnload(); @@ -46,10 +47,15 @@ export default class Popup extends Component { } this.resetCallbackInterval = setInterval(() => { - if (this.ref && this.ref.onload === null) { - clearInterval(this.resetCallbackInterval); - this.resetCallbackInterval = null; - this.setCallbacks(); + try { + if (this.ref && this.ref.onload === null) { + clearInterval(this.resetCallbackInterval); + this.resetCallbackInterval = null; + this.setCallbacks(); + } + } catch (err) { + // We could be getting a security exception here if the login page + // gets redirected to another domain to authenticate. } }, 50); diff --git a/plugins/talk-plugin-local-auth/client/actions.js b/plugins/talk-plugin-local-auth/client/actions.js new file mode 100644 index 000000000..9f2c9cfed --- /dev/null +++ b/plugins/talk-plugin-local-auth/client/actions.js @@ -0,0 +1,9 @@ +import * as actions from './constants'; + +export const startAttach = () => ({ + type: actions.STARTED_ATTACH, +}); + +export const finishAttach = () => ({ + type: actions.FINISH_ATTACH, +}); diff --git a/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js index dc7e0d974..76131c895 100644 --- a/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js @@ -45,6 +45,10 @@ class AddEmailAddressDialog extends React.Component { ), }; + componentDidMount() { + this.props.startAttach(); + } + onChange = e => { const { name, value } = e.target; this.setState( @@ -99,7 +103,13 @@ class AddEmailAddressDialog extends React.Component { }); }; - confirmChanges = async () => { + finish = () => { + this.props.finishAttach(); + }; + + confirmChanges = async e => { + e.preventDefault(); + if (!this.validate()) { this.showErrors(); return; @@ -113,6 +123,8 @@ class AddEmailAddressDialog extends React.Component { email: emailAddress, password: confirmPassword, }); + + // TODO: translate this.props.notify('success', 'Email Added!'); this.goToNextStep(); } catch (err) { @@ -143,13 +155,13 @@ class AddEmailAddressDialog extends React.Component { )} {step === 1 && !settings.requireEmailConfirmation && ( - {}} /> + )} {step === 1 && settings.requireEmailConfirmation && ( {}} + done={this.finish} /> )} @@ -161,6 +173,8 @@ AddEmailAddressDialog.propTypes = { attachLocalAuth: PropTypes.func.isRequired, notify: PropTypes.func.isRequired, root: PropTypes.object.isRequired, + startAttach: PropTypes.func.isRequired, + finishAttach: PropTypes.func.isRequired, }; export default AddEmailAddressDialog; diff --git a/plugins/talk-plugin-local-auth/client/components/AddEmailContent.js b/plugins/talk-plugin-local-auth/client/components/AddEmailContent.js index 8d3fc308f..de296c812 100644 --- a/plugins/talk-plugin-local-auth/client/components/AddEmailContent.js +++ b/plugins/talk-plugin-local-auth/client/components/AddEmailContent.js @@ -41,7 +41,7 @@ const AddEmailContent = ({ -
+
diff --git a/plugins/talk-plugin-local-auth/client/constants.js b/plugins/talk-plugin-local-auth/client/constants.js new file mode 100644 index 000000000..86f5242f7 --- /dev/null +++ b/plugins/talk-plugin-local-auth/client/constants.js @@ -0,0 +1,4 @@ +const prefix = 'TALK_LOCAL_AUTH'; + +export const STARTED_ATTACH = `${prefix}_STARTED_ATTACH`; +export const FINISH_ATTACH = `${prefix}_FINISH_ATTACH`; diff --git a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js index 841dc8420..4cc80d022 100644 --- a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js @@ -3,10 +3,15 @@ import { bindActionCreators } from 'redux'; import { connect, withFragments, excludeIf } from 'plugin-api/beta/client/hocs'; import AddEmailAddressDialog from '../components/AddEmailAddressDialog'; import { notify } from 'coral-framework/actions/notification'; - import { withAttachLocalAuth } from '../hocs'; +import { startAttach, finishAttach } from '../actions'; -const mapDispatchToProps = dispatch => bindActionCreators({ notify }, dispatch); +const mapStateToProps = ({ talkPluginLocalAuth: state }) => ({ + inProgress: state.inProgress, +}); + +const mapDispatchToProps = dispatch => + bindActionCreators({ notify, startAttach, finishAttach }, dispatch); const withData = withFragments({ root: gql` @@ -23,8 +28,11 @@ const withData = withFragments({ }); export default compose( - connect(null, mapDispatchToProps), + connect(mapStateToProps, mapDispatchToProps), withAttachLocalAuth, withData, - excludeIf(({ root: { me } }) => !me || me.email) + excludeIf( + ({ root: { me }, inProgress }) => + !((me && !me.email) || (me && me.email && inProgress)) + ) )(AddEmailAddressDialog); diff --git a/plugins/talk-plugin-local-auth/client/index.js b/plugins/talk-plugin-local-auth/client/index.js index 9c89e0490..a4dc01ac2 100644 --- a/plugins/talk-plugin-local-auth/client/index.js +++ b/plugins/talk-plugin-local-auth/client/index.js @@ -1,10 +1,12 @@ import ChangePassword from './containers/ChangePassword'; import AddEmailAddressDialog from './containers/AddEmailAddressDialog'; import Profile from './containers/Profile'; -import translations from './translations.yml'; +import translations from '../translations.yml'; import graphql from './graphql'; +import reducer from './reducer'; export default { + reducer, translations, slots: { profileHeader: [Profile], diff --git a/plugins/talk-plugin-local-auth/client/reducer.js b/plugins/talk-plugin-local-auth/client/reducer.js new file mode 100644 index 000000000..403a84814 --- /dev/null +++ b/plugins/talk-plugin-local-auth/client/reducer.js @@ -0,0 +1,20 @@ +import * as actions from './constants'; + +const initialState = { + inProgress: false, +}; + +export default function reducer(state = initialState, action) { + switch (action.type) { + case actions.STARTED_ATTACH: + return { + inProgress: true, + }; + case actions.FINISH_ATTACH: + return { + inProgress: false, + }; + default: + return state; + } +} diff --git a/plugins/talk-plugin-local-auth/index.js b/plugins/talk-plugin-local-auth/index.js index 7e4ee6a3c..fabe1dd2b 100644 --- a/plugins/talk-plugin-local-auth/index.js +++ b/plugins/talk-plugin-local-auth/index.js @@ -4,7 +4,7 @@ const mutators = require('./server/mutators'); const path = require('path'); module.exports = { - translations: path.join(__dirname, 'server', 'translations.yml'), + translations: path.join(__dirname, 'translations.yml'), typeDefs, mutators, resolvers, diff --git a/plugins/talk-plugin-local-auth/server/translations.yml b/plugins/talk-plugin-local-auth/server/translations.yml deleted file mode 100644 index d81de6c6d..000000000 --- a/plugins/talk-plugin-local-auth/server/translations.yml +++ /dev/null @@ -1,9 +0,0 @@ -en: - email: - email_change_original: - subject: Email change - body: Your email address has been changed from {0} to {1}. If you did not initiate this change, please contact {2}. # TODO: update translation - error: - NO_LOCAL_PROFILE: No existing email address is associated with this account. - LOCAL_PROFILE: An email address is already associated with this account. - INCORRECT_PASSWORD: Provided password was incorrect. diff --git a/plugins/talk-plugin-local-auth/client/translations.yml b/plugins/talk-plugin-local-auth/translations.yml similarity index 91% rename from plugins/talk-plugin-local-auth/client/translations.yml rename to plugins/talk-plugin-local-auth/translations.yml index fcb80782a..7180268ea 100644 --- a/plugins/talk-plugin-local-auth/client/translations.yml +++ b/plugins/talk-plugin-local-auth/translations.yml @@ -1,4 +1,12 @@ en: + email: + email_change_original: + subject: Email change + body: Your email address has been changed from {0} to {1}. If you did not initiate this change, please contact {2}. # TODO: update translation + error: + NO_LOCAL_PROFILE: No existing email address is associated with this account. + LOCAL_PROFILE: An email address is already associated with this account. + INCORRECT_PASSWORD: Provided password was incorrect. talk-plugin-local-auth: change_password: change_password: "Change Password" @@ -43,7 +51,7 @@ en: email_does_not_match: "Email Address does not match" insert_password: "Insert Password:" required_field: "This field is required" - done: "done" + done: "Done" content: title: "Add an Email Address" description: "For your added security, we require users to add an email address to their accounts. Your email address will be used to:" From e0cada5bde50f7b61a3e8bfd13b4040042eb92a9 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Thu, 10 May 2018 13:44:03 -0700 Subject: [PATCH 14/41] Update 03-07-product-guide-trust.md --- docs/source/03-07-product-guide-trust.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/03-07-product-guide-trust.md b/docs/source/03-07-product-guide-trust.md index 961cf843d..72248e7d1 100644 --- a/docs/source/03-07-product-guide-trust.md +++ b/docs/source/03-07-product-guide-trust.md @@ -16,7 +16,7 @@ Here are the default thresholds: ```text -1 and lower: Unreliable 0 to +1: Neutral -+1 and higher: Reliable (we don't do anything with this label right now) ++2 and higher: Reliable (we don't do anything with this label right now) ``` So in this case, when a new commenter has their first comment rejected, their user karma score becomes `-1`, which triggers the Unreliable threshhold, and they must then have a comment approved by a moderator in order to post freely again. Until that occurs, all of their comments will be held back temporarily in the Reported queue, marked with a `History` tag. From 631206f3603431d3456f445aff22f418738f48df Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 May 2018 00:30:12 +0200 Subject: [PATCH 15/41] Disable autofocus on IOS Safari, prevent zoom on narrow screens --- plugins/talk-plugin-rich-text/client/components/Editor.css | 7 +++++++ plugins/talk-plugin-rich-text/client/components/Editor.js | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/talk-plugin-rich-text/client/components/Editor.css b/plugins/talk-plugin-rich-text/client/components/Editor.css index 2259d01a3..be2646121 100644 --- a/plugins/talk-plugin-rich-text/client/components/Editor.css +++ b/plugins/talk-plugin-rich-text/client/components/Editor.css @@ -1,5 +1,12 @@ +@custom-media --narrow-viewport (max-width: 420px); + .commentContent { composes: content from "./CommentContent.css"; + + /* Prevent zoom on narrow viewports */ + @media (--narrow-viewport) { + font-size: 16px; + } } .placeholder { diff --git a/plugins/talk-plugin-rich-text/client/components/Editor.js b/plugins/talk-plugin-rich-text/client/components/Editor.js index e09931d38..089900e1a 100644 --- a/plugins/talk-plugin-rich-text/client/components/Editor.js +++ b/plugins/talk-plugin-rich-text/client/components/Editor.js @@ -8,6 +8,7 @@ import RTE from './rte/RTE'; import { Icon } from 'plugin-api/beta/client/components/ui'; import { Bold, Italic, Blockquote } from './rte/features'; import { t } from 'plugin-api/beta/client/services'; +import bowser from 'bowser'; class Editor extends React.Component { ref = null; @@ -40,7 +41,7 @@ class Editor extends React.Component { } }); } - if (this.props.isReply) { + if (this.props.isReply && !bowser.ios) { this.ref.focus(); } } From 2d1b675234578b5d7b11d9df2ddc406d37d480fa Mon Sep 17 00:00:00 2001 From: Mendel Konikov Date: Thu, 10 May 2018 21:01:26 -0400 Subject: [PATCH 16/41] Update download page copy --- plugins/talk-plugin-profile-data/translations.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/talk-plugin-profile-data/translations.yml b/plugins/talk-plugin-profile-data/translations.yml index 39b810cca..51555d305 100644 --- a/plugins/talk-plugin-profile-data/translations.yml +++ b/plugins/talk-plugin-profile-data/translations.yml @@ -7,7 +7,7 @@ en: date: "When you wrote the comment" url: "The permalink URL for the comment" body: "The comment text" - asset_url: "The URL on the article or story where the comment appears" + asset_url: "The URL of the article or story where the comment appears" confirm: "Download My Comment History" email: download: @@ -17,7 +17,7 @@ en: delete: subject: "Your account for {0} is scheduled to be deleted" body: | - A request to delete your account was received. Your account is scheduled for deletion on {1}. + A request to delete your account was received. Your account is scheduled for deletion on {1}. After that time all of your comments will be removed from the site, all of your comments will be removed from our database, and your username and email address will be removed from our system. From f57f8397feddc79c2ceb3da240fd2b18b6f2c29b Mon Sep 17 00:00:00 2001 From: Mendel Konikov Date: Fri, 11 May 2018 11:01:15 -0400 Subject: [PATCH 17/41] Capitalize "Information" --- locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.yml b/locales/en.yml index 9a26fbdb6..36cf956b4 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -162,7 +162,7 @@ en: suspect_word_title: "Suspect words list" suspect_word_text: "Comments which contain these words or phrases (not case-sensitive) will be highlighted in the comment stream. Type a word and press Enter or Tab to add. Optionally paste a comma-separated list." tech_settings: "Tech Settings" - organization_information: "Organization information" + organization_information: "Organization Information" organization_info_copy: "We use this information in email notifications generated by Talk. This connects the messages to your organization, and provides a way for users to contact you if they have an issue with their account." organization_info_copy_2: "We recommend creating a generic email account (eg. community@yournewsroom.com) for this purpose. This means it can remain consistent over time, and doesn't expose a name that users could target if their account were blocked." organization_details: "Organization Details" From fc5621a06042de9b0ca42d3da720e4cfcbde1477 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 May 2018 17:12:28 +0200 Subject: [PATCH 18/41] Add reference to story --- plugins/talk-plugin-rich-text/client/components/Editor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/talk-plugin-rich-text/client/components/Editor.js b/plugins/talk-plugin-rich-text/client/components/Editor.js index 089900e1a..99d2be617 100644 --- a/plugins/talk-plugin-rich-text/client/components/Editor.js +++ b/plugins/talk-plugin-rich-text/client/components/Editor.js @@ -41,6 +41,8 @@ class Editor extends React.Component { } }); } + + // Skip IOS due to a bug, see https://www.pivotaltracker.com/story/show/157434928 if (this.props.isReply && !bowser.ios) { this.ref.focus(); } From 29ffe287cdb7afdd6f9c954bccc6b2cefcc92153 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 11 May 2018 09:21:55 -0600 Subject: [PATCH 19/41] Delete package-lock.json --- package-lock.json | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d6105c594..000000000 --- a/package-lock.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "talk", - "version": "4.3.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" - }, - "react-side-effect": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", - "integrity": "sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw==", - "requires": { - "exenv": "1.2.2", - "shallowequal": "1.0.2" - } - }, - "shallowequal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz", - "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw==" - } - } -} From 42d4abf8426b499bc9722c7ac1a2ea5f29b7fc2c Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 11 May 2018 09:33:18 -0600 Subject: [PATCH 20/41] review updates --- plugins/talk-plugin-local-auth/client/actions.js | 2 +- .../client/components/AddEmailAddressDialog.js | 6 ++++-- plugins/talk-plugin-local-auth/client/constants.js | 2 +- plugins/talk-plugin-local-auth/client/reducer.js | 2 +- plugins/talk-plugin-local-auth/translations.yml | 1 + 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/talk-plugin-local-auth/client/actions.js b/plugins/talk-plugin-local-auth/client/actions.js index 9f2c9cfed..972e1f6fd 100644 --- a/plugins/talk-plugin-local-auth/client/actions.js +++ b/plugins/talk-plugin-local-auth/client/actions.js @@ -1,7 +1,7 @@ import * as actions from './constants'; export const startAttach = () => ({ - type: actions.STARTED_ATTACH, + type: actions.START_ATTACH, }); export const finishAttach = () => ({ diff --git a/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js index 76131c895..a5bfd7096 100644 --- a/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/components/AddEmailAddressDialog.js @@ -124,8 +124,10 @@ class AddEmailAddressDialog extends React.Component { password: confirmPassword, }); - // TODO: translate - this.props.notify('success', 'Email Added!'); + this.props.notify( + 'success', + t('talk-plugin-local-auth.add_email.added.alert') + ); this.goToNextStep(); } catch (err) { this.props.notify('error', getErrorMessages(err)); diff --git a/plugins/talk-plugin-local-auth/client/constants.js b/plugins/talk-plugin-local-auth/client/constants.js index 86f5242f7..d410a10fa 100644 --- a/plugins/talk-plugin-local-auth/client/constants.js +++ b/plugins/talk-plugin-local-auth/client/constants.js @@ -1,4 +1,4 @@ const prefix = 'TALK_LOCAL_AUTH'; -export const STARTED_ATTACH = `${prefix}_STARTED_ATTACH`; +export const START_ATTACH = `${prefix}_START_ATTACH`; export const FINISH_ATTACH = `${prefix}_FINISH_ATTACH`; diff --git a/plugins/talk-plugin-local-auth/client/reducer.js b/plugins/talk-plugin-local-auth/client/reducer.js index 403a84814..11666e505 100644 --- a/plugins/talk-plugin-local-auth/client/reducer.js +++ b/plugins/talk-plugin-local-auth/client/reducer.js @@ -6,7 +6,7 @@ const initialState = { export default function reducer(state = initialState, action) { switch (action.type) { - case actions.STARTED_ATTACH: + case actions.START_ATTACH: return { inProgress: true, }; diff --git a/plugins/talk-plugin-local-auth/translations.yml b/plugins/talk-plugin-local-auth/translations.yml index 7180268ea..5fd2264e6 100644 --- a/plugins/talk-plugin-local-auth/translations.yml +++ b/plugins/talk-plugin-local-auth/translations.yml @@ -67,6 +67,7 @@ en: subtitle: "Need to change your email address?" description_2: "You can change your account settings by visiting" path: "My Profile > Settings" + alert: "Email Added!" es: talk-plugin-local-auth: change_password: From faa0ee8ceb2f6ef1504906e4e49060bffe1ce465 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 11 May 2018 10:22:45 -0600 Subject: [PATCH 21/41] added global wrap --- client/coral-admin/src/components/App.css | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/client/coral-admin/src/components/App.css b/client/coral-admin/src/components/App.css index 1404a9293..39c7cf942 100644 --- a/client/coral-admin/src/components/App.css +++ b/client/coral-admin/src/components/App.css @@ -1,9 +1,11 @@ -html, body, #root, #root > div { - min-height: 100%; -} +:global { + html, body, #root, #root > div { + min-height: 100%; + } -body { - margin: 0; - background-color: #FAFAFA; - font-family: 'Roboto', sans-serif; + body { + margin: 0; + background-color: #FAFAFA; + font-family: 'Roboto', sans-serif; + } } From 622b79b4910a74bf2d3303d7050d73018bdec9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Curcio?= Date: Fri, 11 May 2018 13:22:48 -0300 Subject: [PATCH 22/41] Removing unnecessary functions --- .../src/tabs/profile/components/Comment.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/profile/components/Comment.js b/client/coral-embed-stream/src/tabs/profile/components/Comment.js index 84293096b..c9bdcb0ac 100644 --- a/client/coral-embed-stream/src/tabs/profile/components/Comment.js +++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.js @@ -11,16 +11,6 @@ import { getTotalReactionsCount } from 'coral-framework/utils'; import t from 'coral-framework/services/i18n'; class Comment extends React.Component { - goToStory = () => { - this.props.navigate(this.props.comment.asset.url); - }; - - goToConversation = () => { - this.props.navigate( - `${this.props.comment.asset.url}?commentId=${this.props.comment.id}` - ); - }; - render() { const { comment, root } = this.props; const reactionCount = getTotalReactionsCount(comment.action_summaries); @@ -76,7 +66,7 @@ class Comment extends React.Component {
{t('common.story')}:{' '} {comment.asset.title ? comment.asset.title : comment.asset.url} @@ -86,7 +76,7 @@ class Comment extends React.Component {
  • - + {t('view_conversation')} From a571cf830d554a686c761d85d3384f021ee2b0a0 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 11 May 2018 13:32:55 -0300 Subject: [PATCH 23/41] Adding target --- .../src/tabs/profile/components/Comment.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/tabs/profile/components/Comment.js b/client/coral-embed-stream/src/tabs/profile/components/Comment.js index c9bdcb0ac..badeb4262 100644 --- a/client/coral-embed-stream/src/tabs/profile/components/Comment.js +++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.js @@ -67,6 +67,7 @@ class Comment extends React.Component { {t('common.story')}:{' '} {comment.asset.title ? comment.asset.title : comment.asset.url} @@ -76,7 +77,13 @@ class Comment extends React.Component {
    • - + {t('view_conversation')} From f018ccf33ee894ed75c097070b623cfb71de038c Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 May 2018 18:38:41 +0200 Subject: [PATCH 24/41] Prevent showing add email address dialog when usernam is not set --- .../client/containers/AddEmailAddressDialog.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js index 4cc80d022..d8ccd18b0 100644 --- a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js @@ -19,6 +19,13 @@ const withData = withFragments({ me { id email + state { + status { + username { + status + } + } + } } settings { requireEmailConfirmation @@ -33,6 +40,7 @@ export default compose( withData, excludeIf( ({ root: { me }, inProgress }) => + me.state.status.username.status === 'UNSET' || !((me && !me.email) || (me && me.email && inProgress)) ) )(AddEmailAddressDialog); From 6cf4203c9b7ca9db28ec253ef469ddeb45398cea Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 11 May 2018 11:38:54 -0600 Subject: [PATCH 25/41] Update translations.yml --- plugins/talk-plugin-local-auth/translations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/talk-plugin-local-auth/translations.yml b/plugins/talk-plugin-local-auth/translations.yml index 5fd2264e6..7f67e33bd 100644 --- a/plugins/talk-plugin-local-auth/translations.yml +++ b/plugins/talk-plugin-local-auth/translations.yml @@ -2,7 +2,7 @@ en: email: email_change_original: subject: Email change - body: Your email address has been changed from {0} to {1}. If you did not initiate this change, please contact {2}. # TODO: update translation + body: Your email address has been changed from {0} to {1}. If you did not request this change, please contact {2}. error: NO_LOCAL_PROFILE: No existing email address is associated with this account. LOCAL_PROFILE: An email address is already associated with this account. From f0dcd844a787d5309b90509806389ddfccba4455 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 May 2018 20:44:45 +0200 Subject: [PATCH 26/41] Fix excludeIf condition --- .../client/containers/AddEmailAddressDialog.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js index d8ccd18b0..44ee17df7 100644 --- a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js @@ -40,7 +40,8 @@ export default compose( withData, excludeIf( ({ root: { me }, inProgress }) => + !me || me.state.status.username.status === 'UNSET' || - !((me && !me.email) || (me && me.email && inProgress)) + (me.email && !inProgress) ) )(AddEmailAddressDialog); From 7d8d5a6115ef0b77103c39143368f6655f7f46ee Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 11 May 2018 20:46:32 +0200 Subject: [PATCH 27/41] Use lodash get --- .../client/containers/AddEmailAddressDialog.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js index 44ee17df7..f6de3b39c 100644 --- a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js +++ b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js @@ -5,6 +5,7 @@ import AddEmailAddressDialog from '../components/AddEmailAddressDialog'; import { notify } from 'coral-framework/actions/notification'; import { withAttachLocalAuth } from '../hocs'; import { startAttach, finishAttach } from '../actions'; +import get from 'lodash/get'; const mapStateToProps = ({ talkPluginLocalAuth: state }) => ({ inProgress: state.inProgress, @@ -41,7 +42,7 @@ export default compose( excludeIf( ({ root: { me }, inProgress }) => !me || - me.state.status.username.status === 'UNSET' || + get(me, 'state.status.username.status') === 'UNSET' || (me.email && !inProgress) ) )(AddEmailAddressDialog); From 1f7c4e58a1ebd6c3abd9579498a7c0c3746ac087 Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 14 May 2018 13:31:24 -0300 Subject: [PATCH 28/41] sepatate state --- .../components/ChangeEmailContentDialog.js | 92 +++++++++++++++++-- .../client/components/Profile.js | 7 +- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js index f29dd8d86..b0fdeb14d 100644 --- a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js +++ b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js @@ -4,10 +4,77 @@ import styles from './ChangeEmailContentDialog.css'; import InputField from './InputField'; import { Button } from 'plugin-api/beta/client/components/ui'; import { t } from 'plugin-api/beta/client/services'; +import validate from 'coral-framework/helpers/validate'; +import errorMsj from 'coral-framework/helpers/error'; + +const initialState = { + showError: false, + formData: {}, + errors: {}, +}; class ChangeEmailContentDialog extends React.Component { - state = { - showError: false, + state = initialState; + + clearForm = () => { + this.setState(initialState); + }; + + addError = err => { + this.setState(({ errors }) => ({ + errors: { ...errors, ...err }, + })); + }; + + removeError = errKey => { + this.setState(state => { + const { [errKey]: _, ...errors } = state.errors; + return { + errors, + }; + }); + }; + + fieldValidation = (value, type, name) => { + if (!value.length) { + this.addError({ + [name]: t('talk-plugin-local-auth.change_password.required_field'), + }); + } else if (!validate[type](value)) { + this.addError({ [name]: errorMsj[type] }); + } else { + this.removeError(name); + } + }; + + onChange = e => { + const { name, value, type, dataset } = e.target; + const validationType = dataset.validationType || type; + + this.setState( + state => ({ + formData: { + ...state.formData, + [name]: value, + }, + }), + () => { + this.fieldValidation(value, validationType, name); + } + ); + }; + + hasError = err => { + return Object.keys(this.state.errors).indexOf(err) !== -1; + }; + + isSaveEnabled = () => { + const formHasErrors = !!Object.keys(this.state.errors).length; + return !formHasErrors; + }; + + getError = errorKey => { + return this.state.errors[errorKey]; }; showError = () => { @@ -16,24 +83,31 @@ class ChangeEmailContentDialog extends React.Component { }); }; + cancel = () => { + this.clearForm(); + this.disableEditing(); + }; + confirmChanges = async e => { e.preventDefault(); + const { confirmPassword = '' } = this.state.formData; + if (this.formHasError()) { this.showError(); return; } - await this.props.save(); + await this.props.save(confirmPassword); this.props.next(); }; - formHasError = () => this.props.hasError('confirmPassword'); + formHasError = () => this.hasError('confirmPassword'); render() { return (
      - + ×

      @@ -59,17 +133,17 @@ class ChangeEmailContentDialog extends React.Component { label={t('talk-plugin-local-auth.change_email.enter_password')} name="confirmPassword" type="password" - onChange={this.props.onChange} + onChange={this.onChange} defaultValue="" - hasError={this.props.hasError('confirmPassword')} - errorMsg={this.props.getError('confirmPassword')} + hasError={this.hasError('confirmPassword')} + errorMsg={this.getError('confirmPassword')} showError={this.state.showError} columnDisplay />
      diff --git a/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js b/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js index e47a162ef..c22843c8f 100644 --- a/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js +++ b/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js @@ -204,7 +204,7 @@ class ModerationQueue extends React.Component { } componentDidUpdate(prev) { - const { commentCount, selectedCommentId } = this.props; + const { selectedCommentId, hasNextPage } = this.props; const switchedToMultiMode = prev.singleView && !this.props.singleView; const switchedMode = prev.singleView !== this.props.singleView; @@ -212,7 +212,6 @@ class ModerationQueue extends React.Component { prev.selectedCommentId !== selectedCommentId && selectedCommentId; const moderatedLastComment = prev.comments.length > 0 && this.getCommentCountWithoutDagling() === 0; - const hasMoreComment = commentCount > 0; if (switchedToMultiMode) { // Reflow virtual list. @@ -223,7 +222,7 @@ class ModerationQueue extends React.Component { this.scrollToSelectedComment(); } - if (moderatedLastComment && hasMoreComment) { + if (moderatedLastComment && hasNextPage) { this.props.loadMore(); } } @@ -240,10 +239,7 @@ class ModerationQueue extends React.Component { const index = view.findIndex( ({ id }) => id === this.props.selectedCommentId ); - if ( - index === view.length - 1 && - this.getCommentCountWithoutDagling() !== this.props.commentCount - ) { + if (index === view.length - 1 && this.props.hasNextPage) { await this.props.loadMore(); this.selectDown(); return; @@ -467,7 +463,6 @@ ModerationQueue.propTypes = { acceptComment: PropTypes.func.isRequired, commentBelongToQueue: PropTypes.func.isRequired, cleanUpQueue: PropTypes.func.isRequired, - commentCount: PropTypes.number.isRequired, loadMore: PropTypes.func.isRequired, singleView: PropTypes.bool, isLoadingMore: PropTypes.bool, diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index c908404cb..eae2f875f 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -314,11 +314,11 @@ class ModerationContainer extends Component { const currentQueueConfig = Object.assign({}, this.props.queueConfig); - if (premodEnabled && root.newCount === 0) { + if (premodEnabled && root.new.nodes.length === 0) { delete currentQueueConfig.new; } - if (!premodEnabled && root.premodCount === 0) { + if (!premodEnabled && root.premod.nodes.length === 0) { delete currentQueueConfig.premod; } @@ -456,7 +456,11 @@ const withModQueueQuery = withQuery( } ` )} - ${Object.keys(queueConfig).map( + ${ + '' + /* + TODO: eventually we'll reintroduce counting.. + Object.keys(queueConfig).map( queue => ` ${queue}Count: commentCount(query: { excludeDeleted: true, @@ -478,7 +482,8 @@ const withModQueueQuery = withQuery( asset_id: $asset_id, }) ` - )} + )*/ + } asset(id: $asset_id) @skip(if: $allAssets) { id title From 12e848aace2fd03e79006c67f86ca6e07bc4919d Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 19:27:18 +0200 Subject: [PATCH 30/41] Exclude deleted when loading more --- .../coral-admin/src/routes/Moderation/containers/Moderation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index eae2f875f..e3f35081c 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -402,7 +402,7 @@ const COMMENT_RESET_SUBSCRIPTION = gql` const LOAD_MORE_QUERY = gql` query CoralAdmin_Moderation_LoadMore($limit: Int = 10, $cursor: Cursor, $sortOrder: SORT_ORDER, $asset_id: ID, $tags:[String!], $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) { - comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sortOrder: $sortOrder, action_type: $action_type, tags: $tags}) { + comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sortOrder: $sortOrder, action_type: $action_type, tags: $tags, excludeDeleted: true}) { nodes { ...${getDefinitionName(Comment.fragments.comment)} } From ab7e5bc858b0bec3865bff1688aec972a3cb8016 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 20:05:18 +0200 Subject: [PATCH 31/41] No indicator on new queue --- .../coral-admin/src/routes/Moderation/components/Moderation.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/components/Moderation.js b/client/coral-admin/src/routes/Moderation/components/Moderation.js index 0c5ae4c7c..7ea7856ca 100644 --- a/client/coral-admin/src/routes/Moderation/components/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/components/Moderation.js @@ -140,8 +140,7 @@ class Moderation extends Component { name: queueConfig[queue].name, icon: queueConfig[queue].icon, indicator: - ['new', 'premod', 'reported'].includes(queue) && - root[queue].nodes.length > 0, + ['premod', 'reported'].includes(queue) && root[queue].nodes.length > 0, // TODO: Eventually we'll reintroduce counting // count: root[`${props.queue}Count`] })); From 5a190839d1490123afa5e059c68f5cf748482909 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 20:44:30 +0200 Subject: [PATCH 32/41] Show username already exists error --- client/coral-framework/hocs/withSetUsername.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/coral-framework/hocs/withSetUsername.js b/client/coral-framework/hocs/withSetUsername.js index 05323efa1..3a8f65a22 100644 --- a/client/coral-framework/hocs/withSetUsername.js +++ b/client/coral-framework/hocs/withSetUsername.js @@ -59,6 +59,7 @@ const withSetUsername = hoistStatics(WrappedComponent => { } const changeSet = { success: false, loading: false, error }; this.setState(changeSet); + throw error; } }; From beef4e8b3b91a495715d3363b9832e4606d61cce Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 21:28:48 +0200 Subject: [PATCH 33/41] Remove ModerationIndicator from container --- client/coral-admin/src/containers/Header.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/coral-admin/src/containers/Header.js b/client/coral-admin/src/containers/Header.js index d3be66764..c1abac35b 100644 --- a/client/coral-admin/src/containers/Header.js +++ b/client/coral-admin/src/containers/Header.js @@ -2,16 +2,13 @@ import { gql } from 'react-apollo'; import withQuery from 'coral-framework/hocs/withQuery'; import Header from '../components/Header'; import CommunityIndicator from '../routes/Community/containers/Indicator'; -import ModerationIndicator from '../routes/Moderation/containers/Indicator'; import { getDefinitionName } from 'coral-framework/utils'; export default withQuery( gql` query TalkAdmin_Header($nullID: ID) { - ...${getDefinitionName(ModerationIndicator.fragments.root)} ...${getDefinitionName(CommunityIndicator.fragments.root)} } - ${ModerationIndicator.fragments.root} ${CommunityIndicator.fragments.root} `, { From b5adea4b2a0ebc0e16cf08888b6222f5108713ef Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 21:30:09 +0200 Subject: [PATCH 34/41] Remove unused variable --- client/coral-admin/src/containers/Header.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/containers/Header.js b/client/coral-admin/src/containers/Header.js index c1abac35b..49aff5aa8 100644 --- a/client/coral-admin/src/containers/Header.js +++ b/client/coral-admin/src/containers/Header.js @@ -2,6 +2,8 @@ import { gql } from 'react-apollo'; import withQuery from 'coral-framework/hocs/withQuery'; import Header from '../components/Header'; import CommunityIndicator from '../routes/Community/containers/Indicator'; +// TODO: eventually we will readd modqueue counts +// import ModerationIndicator from '../routes/Moderation/containers/Indicator'; import { getDefinitionName } from 'coral-framework/utils'; export default withQuery( @@ -13,7 +15,7 @@ export default withQuery( `, { options: { - variables: { nullID: null }, + // variables: { nullID: null }, }, } )(Header); From 08d523a4d61cdc3335f83b6754d958475c9ac741 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 14 May 2018 21:38:30 +0200 Subject: [PATCH 35/41] Remove unused variable 2 --- client/coral-admin/src/containers/Header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-admin/src/containers/Header.js b/client/coral-admin/src/containers/Header.js index 49aff5aa8..4b8760cc7 100644 --- a/client/coral-admin/src/containers/Header.js +++ b/client/coral-admin/src/containers/Header.js @@ -8,7 +8,7 @@ import { getDefinitionName } from 'coral-framework/utils'; export default withQuery( gql` - query TalkAdmin_Header($nullID: ID) { + query TalkAdmin_Header { ...${getDefinitionName(CommunityIndicator.fragments.root)} } ${CommunityIndicator.fragments.root} From 8d44525c16adcf173da456c173c84698daf0031d Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 14 May 2018 14:37:27 -0600 Subject: [PATCH 36/41] Added support for new indexes --- graph/loaders/settings.js | 12 ++ graph/resolvers/comment.js | 4 +- models/schema/action.js | 19 +-- models/schema/comment.js | 159 +++++++------------- models/schema/setting.js | 2 + models/schema/user.js | 47 +++--- plugin-api/beta/server/getReactionConfig.js | 18 ++- services/settings.js | 10 +- 8 files changed, 118 insertions(+), 153 deletions(-) diff --git a/graph/loaders/settings.js b/graph/loaders/settings.js index 8b07d712b..7010d7533 100644 --- a/graph/loaders/settings.js +++ b/graph/loaders/settings.js @@ -55,6 +55,18 @@ class SettingsLoader { // assembled Settings object. return zipObject(fields, values); } + + /** + * get, like select, will retrieve the settings, but get will only return a + * single setting. + * + * @param {String} field the field to get + */ + async get(field) { + const value = await this._loader.load(field); + + return value; + } } module.exports = () => ({ Settings: new SettingsLoader() }); diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index c40aa49e0..acd55cb1a 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -57,8 +57,8 @@ const Comment = { asset({ asset_id }, _, { loaders: { Assets } }) { return Assets.getByID.load(asset_id); }, - async editing(comment, _, { loaders: { Settings } }) { - const { editCommentWindowLength } = await Settings.select( + editing: async (comment, _, { loaders: { Settings } }) => { + const editCommentWindowLength = await Settings.get( 'editCommentWindowLength' ); diff --git a/models/schema/action.js b/models/schema/action.js index 1df7bd793..d2047faac 100644 --- a/models/schema/action.js +++ b/models/schema/action.js @@ -9,7 +9,8 @@ const Action = new Schema( id: { type: String, default: uuid.v4, - unique: true, + unique: 1, + index: 1, }, action_type: { type: String, @@ -19,7 +20,10 @@ const Action = new Schema( type: String, enum: ITEM_TYPES, }, - item_id: String, + item_id: { + type: String, + index: 1, + }, user_id: String, // The element that summaries will additionally group on in addtion to their action_type, item_type, and @@ -37,15 +41,4 @@ const Action = new Schema( } ); -// Create an index on the `item_id` field so that queries looking for -// actions based on the item id can resolve faster. -Action.index( - { - item_id: 1, - }, - { - background: true, - } -); - module.exports = Action; diff --git a/models/schema/comment.js b/models/schema/comment.js index a211884ff..7ae51ff41 100644 --- a/models/schema/comment.js +++ b/models/schema/comment.js @@ -55,12 +55,16 @@ const Comment = new Schema( type: String, default: uuid.v4, unique: true, + index: true, }, body: { type: String, }, body_history: [BodyHistoryItemSchema], - asset_id: String, + asset_id: { + type: String, + index: true, + }, author_id: String, status_history: [Status], status: { @@ -90,7 +94,6 @@ const Comment = new Schema( // deleted_at stores the date that the given comment was deleted. deleted_at: { type: Date, - default: null, }, // Additional metadata stored on the field. @@ -110,95 +113,67 @@ const Comment = new Schema( } ); -// Add the indexes for the id of the comment. Comment.index( { - id: 1, - }, - { - unique: true, - background: false, - } -); - -Comment.index( - { - status: 1, - created_at: 1, - }, - { - background: true, - } -); - -Comment.index( - { - status: 1, - created_at: 1, - asset_id: 1, - }, - { - background: true, - } -); - -// Create a sparse index to search across. -Comment.index( - { - created_at: 1, - 'action_counts.flag': 1, - status: 1, - }, - { - background: true, - sparse: true, - } -); - -// Create a sparse index to search across. -Comment.index( - { - 'action_counts.flag': 1, - status: 1, - }, - { - background: true, - sparse: true, - } -); - -// Add an index that is optimized for finding flagged comments. -Comment.index( - { - asset_id: 1, - created_at: 1, - 'action_counts.flag': 1, - }, - { - background: true, - } -); - -// Add an index for the reply sort. -Comment.index( - { - asset_id: 1, + deleted_at: 1, created_at: -1, - reply_count: -1, }, - { - background: true, - } + { partialFilterExpression: { deleted_at: null } } ); -// Add an index that is optimized for finding a user's comments. Comment.index( { - author_id: 1, + deleted_at: 1, + status: 1, + created_at: -1, + }, + { partialFilterExpression: { deleted_at: null } } +); + +Comment.index({ + asset_id: 1, + created_at: -1, +}); + +Comment.index({ + asset_id: 1, + created_at: 1, +}); + +Comment.index({ + author_id: 1, + created_at: -1, +}); + +Comment.index({ + asset_id: 1, + status: 1, +}); + +Comment.index({ + asset_id: 1, + parent_id: 1, + reply_count: -1, + created_at: -1, +}); + +Comment.index({ + asset_id: 1, + reply_count: -1, + created_at: -1, +}); + +Comment.index( + { + 'action_counts.flag': 1, + status: 1, created_at: -1, }, { - background: true, + partialFilterExpression: { + 'action_counts.flag': { $exists: true, $gt: 0 }, + deleted_at: null, + }, } ); @@ -210,34 +185,10 @@ Comment.index( status: 1, }, { - background: true, - } -); - -// Optimize for tag searches/counts. -Comment.index( - { - 'tags.tag.name': 1, - status: 1, - }, - { - background: true, sparse: true, } ); -// Add an index that is optimized for sorting based on the created_at timestamp -// but also good at locating comments that have a specific asset id. -Comment.index( - { - asset_id: 1, - created_at: 1, - }, - { - background: true, - } -); - Comment.virtual('edited').get(function() { return this.body_history.length > 1; }); diff --git a/models/schema/setting.js b/models/schema/setting.js index eb9dc76d1..4e6a2c6ab 100644 --- a/models/schema/setting.js +++ b/models/schema/setting.js @@ -12,6 +12,8 @@ const Setting = new Schema( id: { type: String, default: '1', + unique: 1, + index: true, }, moderation: { type: String, diff --git a/models/schema/user.js b/models/schema/user.js index ec9c018cc..a4ccfd185 100644 --- a/models/schema/user.js +++ b/models/schema/user.js @@ -58,6 +58,7 @@ const User = new Schema( default: uuid.v4, unique: true, required: true, + index: true, }, // This is sourced from the social provider or set manually during user setup @@ -107,6 +108,7 @@ const User = new Schema( status: { type: String, enum: USER_STATUS_USERNAME, + index: true, }, // History stores the history of username status changes. @@ -135,6 +137,7 @@ const User = new Schema( type: Boolean, required: true, default: false, + index: true, }, history: [ { @@ -226,41 +229,26 @@ User.index( } ); -User.index( - { - lowercaseUsername: 1, - 'profiles.id': 1, - created_at: -1, - }, - { - background: true, - } -); +User.index({ + lowercaseUsername: 1, + 'profiles.id': 1, + created_at: -1, +}); // This query is executed often, to count the number of flagged accounts with // usernames. -User.index( - { - 'action_counts.flag': 1, - 'status.username.status': 1, - }, - { - background: true, - } -); +User.index({ + 'action_counts.flag': 1, + 'status.username.status': 1, +}); // Sorting users by created at is the default people search. -User.index( - { - created_at: -1, - }, - { - background: true, - } -); +User.index({ + created_at: -1, +}); /** - * returns true if a commenter is staff + * returns true if a commenter is staff. */ User.method('isStaff', function() { return this.role !== 'COMMENTER'; @@ -330,6 +318,9 @@ User.virtual('hasVerifiedEmail').get(function() { }); }); +/** + * system returns true when the user is a system user. + */ User.virtual('system') .get(function() { return this._system; diff --git a/plugin-api/beta/server/getReactionConfig.js b/plugin-api/beta/server/getReactionConfig.js index 5aa3063b3..f1f25b261 100644 --- a/plugin-api/beta/server/getReactionConfig.js +++ b/plugin-api/beta/server/getReactionConfig.js @@ -11,10 +11,22 @@ function getReactionConfig(reaction) { if (CREATE_MONGO_INDEXES) { // Create the index on the comment model based on the reaction config. - Comment.collection.createIndex( + Comment.collection.ensureIndex( { - created_at: 1, - [`action_counts.${sc(reaction)}`]: 1, + asset_id: 1, + [`action_counts.${sc(reaction)}`]: -1, + created_at: -1, + }, + { + background: true, + } + ); + + Comment.collection.ensureIndex( + { + asset_id: 1, + [`action_counts.${sc(reaction)}`]: -1, + created_at: -1, }, { background: true, diff --git a/services/settings.js b/services/settings.js index def42187a..e009c707b 100644 --- a/services/settings.js +++ b/services/settings.js @@ -1,13 +1,17 @@ const Setting = require('../models/setting'); const { ErrSettingsNotInit } = require('../errors'); const { dotize } = require('./utils'); -const { isEmpty, zipObject, uniq } = require('lodash'); +const { isEmpty, zipObject } = require('lodash'); const DataLoader = require('dataloader'); const selector = { id: '1' }; -async function loadFn(fields = []) { - const model = await Setting.findOne(selector).select(uniq(fields)); +async function loadFn(/* fields = [] */) { + // Originally, we used the projection operation, turns out this isn't that + // fast. We should utilize the redis cache instead here. + // const model = await Setting.findOne(selector).select(uniq(fields)); + + const model = await Setting.findOne(selector); if (!model) { throw new ErrSettingsNotInit(); } From 50acf9ca5043c4e08739de2f32d3ff3aa930c869 Mon Sep 17 00:00:00 2001 From: okbel Date: Tue, 15 May 2018 11:47:07 -0300 Subject: [PATCH 37/41] Removing --- .../client/components/ChangeEmailContentDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js index b0fdeb14d..cd6738d02 100644 --- a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js +++ b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js @@ -85,7 +85,6 @@ class ChangeEmailContentDialog extends React.Component { cancel = () => { this.clearForm(); - this.disableEditing(); }; confirmChanges = async e => { From ef5e5ddbcf41a0a9d17739c95e4758bf2d8261d9 Mon Sep 17 00:00:00 2001 From: Nat Welch Date: Tue, 15 May 2018 11:55:02 -0400 Subject: [PATCH 38/41] Set is_test to false Akismet is no longer in test mode. --- plugins/talk-plugin-akismet/server/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/talk-plugin-akismet/server/hooks.js b/plugins/talk-plugin-akismet/server/hooks.js index 80a233584..b06557783 100644 --- a/plugins/talk-plugin-akismet/server/hooks.js +++ b/plugins/talk-plugin-akismet/server/hooks.js @@ -71,7 +71,7 @@ module.exports = { permalink: asset.url, comment_type: 'comment', comment_content: input.body, - is_test: true, + is_test: false, }); debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`); From 40a431b374e883d44777a6c248077c117f914bf9 Mon Sep 17 00:00:00 2001 From: okbel Date: Tue, 15 May 2018 13:31:29 -0300 Subject: [PATCH 39/41] Ready --- .../components/ChangeEmailContentDialog.js | 19 +++++++------------ .../client/components/Profile.js | 1 + 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js index cd6738d02..f671c065b 100644 --- a/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js +++ b/plugins/talk-plugin-local-auth/client/components/ChangeEmailContentDialog.js @@ -9,7 +9,9 @@ import errorMsj from 'coral-framework/helpers/error'; const initialState = { showError: false, - formData: {}, + formData: { + confirmPassword: '', + }, errors: {}, }; @@ -68,11 +70,6 @@ class ChangeEmailContentDialog extends React.Component { return Object.keys(this.state.errors).indexOf(err) !== -1; }; - isSaveEnabled = () => { - const formHasErrors = !!Object.keys(this.state.errors).length; - return !formHasErrors; - }; - getError = errorKey => { return this.state.errors[errorKey]; }; @@ -85,6 +82,7 @@ class ChangeEmailContentDialog extends React.Component { cancel = () => { this.clearForm(); + this.props.closeDialog(); }; confirmChanges = async e => { @@ -133,7 +131,7 @@ class ChangeEmailContentDialog extends React.Component { name="confirmPassword" type="password" onChange={this.onChange} - defaultValue="" + value={this.state.formData.confirmPassword} hasError={this.hasError('confirmPassword')} errorMsg={this.getError('confirmPassword')} showError={this.state.showError} @@ -159,14 +157,11 @@ class ChangeEmailContentDialog extends React.Component { } ChangeEmailContentDialog.propTypes = { - save: PropTypes.func, next: PropTypes.func, - cancel: PropTypes.func, - onChange: PropTypes.func, + save: PropTypes.func, formData: PropTypes.object, email: PropTypes.string, - hasError: PropTypes.func, - getError: PropTypes.func, + closeDialog: PropTypes.func, }; export default ChangeEmailContentDialog; diff --git a/plugins/talk-plugin-local-auth/client/components/Profile.js b/plugins/talk-plugin-local-auth/client/components/Profile.js index 7c4c8e493..8484fa2b8 100644 --- a/plugins/talk-plugin-local-auth/client/components/Profile.js +++ b/plugins/talk-plugin-local-auth/client/components/Profile.js @@ -205,6 +205,7 @@ class Profile extends React.Component { formData={this.state.formData} email={email} enable={formData.newEmail && email !== formData.newEmail} + closeDialog={this.closeDialog} /> From ee0790dbe8bd19a7ed1c6410717776cb207baafa Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 15 May 2018 11:25:58 -0600 Subject: [PATCH 40/41] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01b671dde..97f4e373d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "4.4.0", + "version": "4.4.1", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "private": true, From 0b407d516b30efbb2937814aca879645c167394b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 15 May 2018 15:51:14 -0600 Subject: [PATCH 41/41] copy updates --- .../client/components/Profile.js | 18 ++++++++++++++---- .../talk-plugin-local-auth/translations.yml | 6 ++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/plugins/talk-plugin-local-auth/client/components/Profile.js b/plugins/talk-plugin-local-auth/client/components/Profile.js index 8484fa2b8..caa09543b 100644 --- a/plugins/talk-plugin-local-auth/client/components/Profile.js +++ b/plugins/talk-plugin-local-auth/client/components/Profile.js @@ -223,11 +223,21 @@ class Profile extends React.Component { disabled={!usernameCanBeUpdated} columnDisplay > - - {t( - 'talk-plugin-local-auth.change_username.change_username_note' +
      + + {t( + 'talk-plugin-local-auth.change_username.change_username_note' + )} + + {!usernameCanBeUpdated && ( + + {' '} + {t( + 'talk-plugin-local-auth.change_username.is_not_eligible' + )} + )} - +