From 3fcad354923a8ddf20530234231e52d1da9e2f1e Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 9 Apr 2018 16:27:02 -0300 Subject: [PATCH 1/8] autocomplete off --- client/coral-ui/components/TextField.js | 3 +++ plugins/talk-plugin-auth/client/login/components/SignUp.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/client/coral-ui/components/TextField.js b/client/coral-ui/components/TextField.js index 6cbb7a06b..2ba23e6e8 100644 --- a/client/coral-ui/components/TextField.js +++ b/client/coral-ui/components/TextField.js @@ -27,11 +27,14 @@ const TextField = ({ ); TextField.propTypes = { + id: PropTypes.string, label: PropTypes.string, value: PropTypes.string, onChange: PropTypes.func, errorMsg: PropTypes.string, type: PropTypes.string, + className: PropTypes.string, + showErrors: PropTypes.bool, }; export default TextField; diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index cc49e7391..2a6de0c24 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -75,6 +75,7 @@ class SignUp extends React.Component { showErrors={!!emailError} errorMsg={emailError} onChange={this.handleEmailChange} + autocomplete="off" /> {passwordError && ( @@ -113,6 +116,7 @@ class SignUp extends React.Component { errorMsg={passwordRepeatError} onChange={this.handlePasswordRepeatChange} minLength="8" + autocomplete="off" /> Date: Tue, 10 Apr 2018 17:33:56 -0600 Subject: [PATCH 2/8] Ignored User Reply Notifications --- graph/resolvers/user.js | 4 ++-- .../index.js | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/graph/resolvers/user.js b/graph/resolvers/user.js index 46fe0ac3c..67478785a 100644 --- a/graph/resolvers/user.js +++ b/graph/resolvers/user.js @@ -29,9 +29,9 @@ const User = { return Comments.getByQuery(query); }, - ignoredUsers({ ignoresUsers }, args, { user, loaders: { Users } }) { + ignoredUsers({ ignoresUsers }, args, { loaders: { Users } }) { // Return nothing if there is nothing to query for. - if (!user.ignoresUsers || user.ignoresUsers.length <= 0) { + if (!ignoresUsers || ignoresUsers.length <= 0) { return []; } diff --git a/plugins/talk-plugin-notifications-category-reply/index.js b/plugins/talk-plugin-notifications-category-reply/index.js index e214975ac..cce258daa 100644 --- a/plugins/talk-plugin-notifications-category-reply/index.js +++ b/plugins/talk-plugin-notifications-category-reply/index.js @@ -1,4 +1,4 @@ -const { get } = require('lodash'); +const { get, map } = require('lodash'); const path = require('path'); const handle = async (ctx, comment) => { @@ -23,6 +23,9 @@ const handle = async (ctx, comment) => { id user { id + ignoredUsers { + id + } notificationSettings { onReply } @@ -53,13 +56,23 @@ const handle = async (ctx, comment) => { return; } + // Pull out the author of the new comment. + const authorID = get(comment, 'author_id'); + // Check to see if this is yourself replying to yourself, if that's the case // don't send a notification. - if (userID === get(comment, 'author_id')) { + if (userID === authorID) { ctx.log.info('user id of parent comment is the same as the new comment'); return; } + // Check to see if this user is ignoring the user who replied to their + // comment. + if (map(get(comment, 'user.ignoredUsers', []), 'id').indexOf(authorID)) { + ctx.log.info('parent user has ignored the author of the new comment'); + return; + } + // The user does have notifications for replied comments enabled, queue the // notification to be sent. return { userID, date: comment.created_at, context: comment.id }; From 0206804bf459007ba17a1c46a5241285d0d3dbdb Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 12 Apr 2018 12:18:22 -0300 Subject: [PATCH 3/8] autocapitalize off for the username field --- plugins/talk-plugin-auth/client/login/components/SignUp.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index 2a6de0c24..83f81f81d 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -87,6 +87,7 @@ class SignUp extends React.Component { errorMsg={usernameError} onChange={this.handleUsernameChange} autocomplete="off" + autocapitalize="none" /> Date: Thu, 12 Apr 2018 12:47:09 -0600 Subject: [PATCH 4/8] Storage Fixes - Implements storage fixes (fixes #1520) - Implements updates to locale --- client/coral-framework/services/i18n.js | 43 +++++-- client/coral-framework/services/storage.js | 128 ++++++++++++++++----- 2 files changed, 131 insertions(+), 40 deletions(-) diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 9c65c9c4e..2ba0e4e07 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -53,25 +53,44 @@ let timeagoInstance; function setLocale(storage, locale) { try { - if (storage) { - storage.setItem('locale', locale); - } + storage.setItem('locale', locale); } catch (err) { - console.error(err); + console.warn('Error while trying to persist the language detection', err); } } -function getLocale(storage) { +// detectLanguage will try to get the locale from storage if available, +// otherwise will try to get it from the navigator, otherwise, it will fallback +// to the default language. +function detectLanguage(storage) { try { - return ( - (storage && storage.getItem('locale')) || - navigator.language || - defaultLanguage - ).split('-')[0]; + const lang = storage.getItem('locale') || navigator.language; + if (lang) { + return lang; + } } catch (err) { - console.error(err); - return null; + console.warn( + 'Error while trying to detect language, will fallback to', + err + ); } + + console.warn('Could not detect language, will fallback to', defaultLanguage); + return defaultLanguage; +} + +// getLocale will get the users locale from the local detector and parse it to a +// format we can work with. +function getLocale(storage) { + // Get the language from the local detector. + const lang = detectLanguage(storage); + + // Some language strings come with additional subtags as defined in: + // + // https://www.ietf.org/rfc/bcp/bcp47.txt + // + // So we should strip that off if we find it. + return lang.split('-')[0]; } export function setupTranslations() { diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index 66e2c96cd..2e54718c7 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -1,40 +1,112 @@ import uuid from 'uuid/v4'; -function getStorage(type) { - let storage; - try { - storage = window[type]; - const x = '__storage_test__'; - storage.setItem(x, x); - storage.removeItem(x); - } catch (e) { - const ignore = - e instanceof DOMException && - // everything except Firefox - (e.code === 22 || - // SecurityError related to having 3rd party cookies disabled. - e.code === 18 || - // Firefox +function testStorage(storage) { + const key = '__storage_test__'; - e.code === 1014 || - // test name field too, because code might not be present + // Create a unique test value. + const expectedValue = String(Date.now()); - // everything except Firefox - e.name === 'QuotaExceededError' || - // Firefox - e.name === 'NS_ERROR_DOM_QUOTA_REACHED'); - if (!ignore) { - console.warn(e); + // Try to set, get, and remove that item. + storage.setItem(key, expectedValue); + const canSetGet = expectedValue === storage.getItem(key); + storage.removeItem(key); + + return canSetGet; +} + +// InMemoryStorage is a dumb implementation of the Storage interface that will +// not persist the data at all. It implements the Storage interface found: +// +// https://developer.mozilla.org/en-US/docs/Web/API/Storage +// +class InMemoryStorage { + constructor() { + this.storage = {}; + } + + get length() { + return Object.keys(this.storage).length; + } + + key(n) { + if (this.length <= n) { + return undefined; } - // When third party cookies are disabled, session storage is readable/ - // writable, but localStorage is not. Try to get the sessionStorage to use. - if (type !== 'sessionStorage') { - return getStorage('sessionStorage'); + return this.storage[Object.keys(this.storage)[n]]; + } + + getItem(key) { + return this.storage[key]; + } + + setItem(key, value) { + this.storage[key] = value; + + try { + // Test sessionStorage. We could have been given access recently. + const canSetGet = testStorage(sessionStorage); + + if (canSetGet) { + sessionStorage.setItem(key, value); + console.log( + 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' + ); + } + } catch (err) { + console.warn( + 'Attempt to persist InMemoryStorage value to sessionStorage failed', + err + ); } } - return storage; + removeItem(key) { + delete this.storage[key]; + } +} + +// getStorage will test to see if the requested storage type is available, if it +// is not, it will try sessionStorage, and if that is also not available, it +// will fallback to InMemoryStorage. +function getStorage(type) { + let storage; + try { + // Get the desired storage from the window. + storage = window[type]; + + // Test the storage. + const canSetGet = testStorage(storage); + + // If we can set/get then use that storage. + if (canSetGet) { + console.log('Access to', type, 'is available'); + return storage; + } else { + console.warn( + 'Failed to set/get on', + type, + 'falling back to InMemoryStorage' + ); + } + } catch (err) { + // When third party cookies are disabled, session storage is readable/ + // writable, but localStorage is not. Try to get the sessionStorage to use. + if (type !== 'sessionStorage') { + console.warn('Could not access', type, 'trying sessionStorage', err); + return getStorage('sessionStorage'); + } + + console.warn( + 'Could not access', + type, + 'falling back to InMemoryStorage', + err + ); + } + + // No acceptable storage could be found, returning the InMemoryStorage. + return new InMemoryStorage(); } /** From 1c87181b1709528aacb8bad5b5d44ffaaee0169f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 12:58:30 -0600 Subject: [PATCH 5/8] cleanups around storageAccess --- client/coral-admin/src/actions/moderation.js | 9 +------- client/coral-admin/src/index.js | 3 ++- client/coral-framework/actions/auth.js | 22 +++++++------------- client/coral-framework/services/storage.js | 17 +++++++++++++++ 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js index 16c2bd2f7..a46d2dccd 100644 --- a/client/coral-admin/src/actions/moderation.js +++ b/client/coral-admin/src/actions/moderation.js @@ -5,14 +5,7 @@ export const singleView = () => ({ type: actions.SINGLE_VIEW }); // hide shortcuts note export const hideShortcutsNote = () => (dispatch, _, { localStorage }) => { - try { - if (localStorage) { - localStorage.setItem('coral:shortcutsNote', 'hide'); - } - } catch (e) { - // above will fail in Safari private mode - } - + localStorage.setItem('coral:shortcutsNote', 'hide'); dispatch({ type: actions.HIDE_SHORTCUTS_NOTE }); }; diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js index 0cf0a8cf9..158870dec 100644 --- a/client/coral-admin/src/index.js +++ b/client/coral-admin/src/index.js @@ -15,7 +15,8 @@ import { hideShortcutsNote } from './actions/moderation'; smoothscroll.polyfill(); function init({ store, localStorage }) { - if (localStorage && localStorage.getItem('coral:shortcutsNote') === 'hide') { + const shouldHide = localStorage.getItem('coral:shortcutsNote') === 'hide'; + if (shouldHide) { store.dispatch(hideShortcutsNote()); } } diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 5390153e6..5e3b0846f 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -15,9 +15,7 @@ export const checkLogin = () => ( rest('/auth') .then(result => { if (!result.user) { - if (localStorage) { - cleanAuthData(localStorage); - } + cleanAuthData(localStorage); dispatch(checkLoginSuccess(null)); return; } @@ -52,10 +50,8 @@ const checkLoginSuccess = user => ({ }); export const setAuthToken = token => (dispatch, _, { localStorage }) => { - if (localStorage) { - localStorage.setItem('exp', jwtDecode(token).exp); - localStorage.setItem('token', token); - } + localStorage.setItem('exp', jwtDecode(token).exp); + localStorage.setItem('token', token); // Dispatch the set auth token action. For some browsers and situations, we // may not be able to persist the auth token any other way. Keep it in redux! @@ -70,11 +66,8 @@ export const handleSuccessfulLogin = (user, token) => ( { client, localStorage, postMessage } ) => { const { exp } = jwtDecode(token); - - if (localStorage) { - localStorage.setItem('exp', exp); - localStorage.setItem('token', token); - } + localStorage.setItem('exp', exp); + localStorage.setItem('token', token); // Send the message via the messages service to the window.opener if it // exists. @@ -105,9 +98,8 @@ export const logout = () => async ( ) => { await rest('/auth', { method: 'DELETE' }); - if (localStorage) { - cleanAuthData(localStorage); - } + // Clear the auth data persisted to localStorage. + cleanAuthData(localStorage); // Reset the websocket. client.resetWebsocket(); diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index 2e54718c7..e206163e6 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -63,6 +63,23 @@ class InMemoryStorage { removeItem(key) { delete this.storage[key]; + + try { + // Test sessionStorage. We could have been given access recently. + const canSetGet = testStorage(sessionStorage); + + if (canSetGet) { + sessionStorage.removeItem(key); + console.log( + 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' + ); + } + } catch (err) { + console.warn( + 'Attempt to persist InMemoryStorage delete to sessionStorage failed', + err + ); + } } } From 5125bb5d2d77c89dc8558a648c79d3def9c62840 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:22:20 -0600 Subject: [PATCH 6/8] logic cleanup --- client/coral-framework/services/storage.js | 59 +++++++++------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index e206163e6..7b7acf655 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -1,6 +1,6 @@ import uuid from 'uuid/v4'; -function testStorage(storage) { +function testStorageAccess(storage) { const key = '__storage_test__'; // Create a unique test value. @@ -11,7 +11,10 @@ function testStorage(storage) { const canSetGet = expectedValue === storage.getItem(key); storage.removeItem(key); - return canSetGet; + if (!canSetGet) { + // We can't access the desired storage! + throw new Error('Storage access test failed'); + } } // InMemoryStorage is a dumb implementation of the Storage interface that will @@ -45,14 +48,13 @@ class InMemoryStorage { try { // Test sessionStorage. We could have been given access recently. - const canSetGet = testStorage(sessionStorage); + testStorageAccess(sessionStorage); - if (canSetGet) { - sessionStorage.setItem(key, value); - console.log( - 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' - ); - } + // Test passed! Set the item in sessionStorage. + sessionStorage.setItem(key, value); + console.log( + 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' + ); } catch (err) { console.warn( 'Attempt to persist InMemoryStorage value to sessionStorage failed', @@ -66,14 +68,13 @@ class InMemoryStorage { try { // Test sessionStorage. We could have been given access recently. - const canSetGet = testStorage(sessionStorage); + testStorageAccess(sessionStorage); - if (canSetGet) { - sessionStorage.removeItem(key); - console.log( - 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' - ); - } + // Test passed! Remove the item from sessionStorage. + sessionStorage.removeItem(key); + console.log( + 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' + ); } catch (err) { console.warn( 'Attempt to persist InMemoryStorage delete to sessionStorage failed', @@ -87,25 +88,13 @@ class InMemoryStorage { // is not, it will try sessionStorage, and if that is also not available, it // will fallback to InMemoryStorage. function getStorage(type) { - let storage; try { - // Get the desired storage from the window. - storage = window[type]; + // Get the desired storage from the window and test it out. + const storage = window[type]; + testStorageAccess(storage); - // Test the storage. - const canSetGet = testStorage(storage); - - // If we can set/get then use that storage. - if (canSetGet) { - console.log('Access to', type, 'is available'); - return storage; - } else { - console.warn( - 'Failed to set/get on', - type, - 'falling back to InMemoryStorage' - ); - } + // Storage test was successful! Return it. + return storage; } catch (err) { // When third party cookies are disabled, session storage is readable/ // writable, but localStorage is not. Try to get the sessionStorage to use. @@ -115,9 +104,7 @@ function getStorage(type) { } console.warn( - 'Could not access', - type, - 'falling back to InMemoryStorage', + 'Could not access sessionStorage falling back to InMemoryStorage', err ); } From bb11de562f9a9014cae9a57826383f84cce97f4f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:24:56 -0600 Subject: [PATCH 7/8] Removed storage check --- client/coral-framework/services/i18n.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 2ba0e4e07..e1c3ab99e 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -52,11 +52,7 @@ let lang; let timeagoInstance; function setLocale(storage, locale) { - try { - storage.setItem('locale', locale); - } catch (err) { - console.warn('Error while trying to persist the language detection', err); - } + storage.setItem('locale', locale); } // detectLanguage will try to get the locale from storage if available, From 4a4357bc4c45130d80e0e26a2dddace9870219ed Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:37:42 -0600 Subject: [PATCH 8/8] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c9243a560..02cc45e70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "4.3.1", + "version": "4.3.2", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "private": true,