From 0d3f0eaf9828540b88e58ddc738e18a6d107440c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 29 Oct 2017 16:53:00 -0600 Subject: [PATCH] Convert local settings to granular settings This breaks language selection. Signed-off-by: Travis Ralston --- src/CallHandler.js | 4 +- src/CallMediaHandler.js | 18 ++++---- src/UserSettingsStore.js | 17 -------- src/components/structures/MatrixChat.js | 4 +- src/components/structures/UserSettings.js | 41 +++++++------------ src/components/structures/login/Login.js | 4 +- .../views/elements/LanguageDropdown.js | 17 ++++---- src/components/views/rooms/Autocomplete.js | 4 +- src/components/views/rooms/RoomSettings.js | 11 ++--- src/languageHandler.js | 5 +-- src/settings/SettingsStore.js | 35 ++++++++++++++++ 11 files changed, 83 insertions(+), 77 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index a9539d40e1..7dbd0c899b 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -52,13 +52,13 @@ limitations under the License. */ import MatrixClientPeg from './MatrixClientPeg'; -import UserSettingsStore from './UserSettingsStore'; import PlatformPeg from './PlatformPeg'; import Modal from './Modal'; import sdk from './index'; import { _t } from './languageHandler'; import Matrix from 'matrix-js-sdk'; import dis from './dispatcher'; +import SettingsStore from "./settings/SettingsStore"; global.mxCalls = { //room_id: MatrixCall @@ -246,7 +246,7 @@ function _onAction(payload) { } else if (members.length === 2) { console.log("Place %s call in %s", payload.type, payload.room_id); const call = Matrix.createNewMatrixCall(MatrixClientPeg.get(), payload.room_id, { - forceTURN: UserSettingsStore.getLocalSetting('webRtcForceTURN', false), + forceTURN: SettingsStore.getValue('webRtcForceTURN'), }); placeCall(call); } else { // > 2 diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 839b496845..2c3c707102 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -14,8 +14,8 @@ limitations under the License. */ -import UserSettingsStore from './UserSettingsStore'; import * as Matrix from 'matrix-js-sdk'; +import SettingsStore from "./settings/SettingsStore"; export default { getDevices: function() { @@ -43,22 +43,20 @@ export default { }, loadDevices: function() { - // this.getDevices().then((devices) => { - const localSettings = UserSettingsStore.getLocalSettings(); - // // if deviceId is not found, automatic fallback is in spec - // // recall previously stored inputs if any - Matrix.setMatrixCallAudioInput(localSettings['webrtc_audioinput']); - Matrix.setMatrixCallVideoInput(localSettings['webrtc_videoinput']); - // }); + const audioDeviceId = SettingsStore.getValue("webrtc_audioinput"); + const videoDeviceId = SettingsStore.getValue("webrtc_videoinput"); + + Matrix.setMatrixCallAudioInput(audioDeviceId); + Matrix.setMatrixCallVideoInput(videoDeviceId); }, setAudioInput: function(deviceId) { - UserSettingsStore.setLocalSetting('webrtc_audioinput', deviceId); + SettingsStore.setValue("webrtc_audioinput", null, "device", deviceId); Matrix.setMatrixCallAudioInput(deviceId); }, setVideoInput: function(deviceId) { - UserSettingsStore.setLocalSetting('webrtc_videoinput', deviceId); + SettingsStore.setValue("webrtc_videoinput", null, "device", deviceId); Matrix.setMatrixCallVideoInput(deviceId); }, }; diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 591eaa1f0f..20c1c3d17f 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -136,21 +136,4 @@ export default { disable: disabled, }); }, - - getLocalSettings: function() { - const localSettingsString = localStorage.getItem('mx_local_settings') || '{}'; - return JSON.parse(localSettingsString); - }, - - getLocalSetting: function(type, defaultValue = null) { - const settings = this.getLocalSettings(); - return settings.hasOwnProperty(type) ? settings[type] : defaultValue; - }, - - setLocalSetting: function(type, value) { - const settings = this.getLocalSettings(); - settings[type] = value; - // FIXME: handle errors - localStorage.setItem('mx_local_settings', JSON.stringify(settings)); - }, }; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e679276a08..f76201fa55 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -22,7 +22,6 @@ import React from 'react'; import Matrix from "matrix-js-sdk"; import Analytics from "../../Analytics"; -import UserSettingsStore from '../../UserSettingsStore'; import MatrixClientPeg from "../../MatrixClientPeg"; import PlatformPeg from "../../PlatformPeg"; import SdkConfig from "../../SdkConfig"; @@ -44,6 +43,7 @@ import createRoom from "../../createRoom"; import * as UDEHandler from '../../UnknownDeviceErrorHandler'; import KeyRequestHandler from '../../KeyRequestHandler'; import { _t, getCurrentLanguage } from '../../languageHandler'; +import SettingsStore from "../../settings/SettingsStore"; /** constants for MatrixChat.state.view */ const VIEWS = { @@ -213,7 +213,7 @@ module.exports = React.createClass({ componentWillMount: function() { SdkConfig.put(this.props.config); - if (!UserSettingsStore.getLocalSetting('analyticsOptOut', false)) Analytics.enable(); + if (!SettingsStore.getValue("analyticsOptOut")) Analytics.enable(); // Used by _viewRoom before getting state from sync this.firstSyncComplete = false; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 84b56e8a0f..2320d7edb9 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -80,41 +80,26 @@ const SIMPLE_SETTINGS = [ // TODO: {Travis} Consider making generic setting handler to support `label` and `fn` optionally (backed by SettingsStore) -// TODO: {Travis} Convert const ANALYTICS_SETTINGS_LABELS = [ { id: 'analyticsOptOut', - label: _td('Opt out of analytics'), fn: function(checked) { Analytics[checked ? 'disable' : 'enable'](); }, }, ]; -// TODO: {Travis} Convert const WEBRTC_SETTINGS_LABELS = [ - { - id: 'webRtcForceTURN', - label: _td('Disable Peer-to-Peer for 1:1 calls'), - }, + { id: 'webRtcForceTURN' }, ]; -// TODO: {Travis} Convert -// Warning: Each "label" string below must be added to i18n/strings/en_EN.json, -// since they will be translated when rendered. const CRYPTO_SETTINGS_LABELS = [ { id: 'blacklistUnverifiedDevices', - label: _td('Never send encrypted messages to unverified devices from this device'), fn: function(checked) { MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked); }, }, - // XXX: this is here for documentation; the actual setting is managed via RoomSettings - // { - // id: 'blacklistUnverifiedDevicesPerRoom' - // label: 'Never send encrypted messages to unverified devices in this room', - // } ]; // Enumerate the available themes, with a nice human text label. @@ -226,8 +211,6 @@ module.exports = React.createClass({ }); this._refreshFromServer(); - this._localSettings = UserSettingsStore.getLocalSettings(); - if (PlatformPeg.get().isElectron()) { const {ipcRenderer} = require('electron'); @@ -298,8 +281,8 @@ module.exports = React.createClass({ if (this._unmounted) return; this.setState({ mediaDevices, - activeAudioInput: this._localSettings['webrtc_audioinput'], - activeVideoInput: this._localSettings['webrtc_videoinput'], + activeAudioInput: SettingsStore.getValueAt("device", 'webrtc_audioinput'), + activeVideoInput: SettingsStore.getValueAt("device", 'webrtc_videoinput'), }); }); }, @@ -631,7 +614,7 @@ module.exports = React.createClass({ onLanguageChange: function(newLang) { if(this.state.language !== newLang) { - UserSettingsStore.setLocalSetting('language', newLang); + SettingsStore.setValue("language", null, "device", newLang); this.setState({ language: newLang, }); @@ -654,7 +637,7 @@ module.exports = React.createClass({ // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render const onChange = (e) => - UserSettingsStore.setLocalSetting('autocompleteDelay', + e.target.value); + SettingsStore.setValue("autocompleteDelay", null, "device", e.target.value); return (

{ _t("User Interface") }

@@ -669,7 +652,7 @@ module.exports = React.createClass({ @@ -699,6 +682,7 @@ module.exports = React.createClass({ UserSettingsStore.setUrlPreviewsDisabled(e.target.checked); }, + // TODO: {Travis} Make this a component () _renderSyncedSetting: function(setting) { // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render @@ -715,11 +699,13 @@ module.exports = React.createClass({ onChange={onChange} />
; }, + // TODO: {Travis} Make this a component () + // {Travis} Maybe make that part of CheckboxSetting somehow? _renderThemeSelector: function(setting) { // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render @@ -811,22 +797,23 @@ module.exports = React.createClass({ } else return (
); }, + // TODO: {Travis} Make this a component () _renderLocalSetting: function(setting) { // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render const onChange = (e) => { - UserSettingsStore.setLocalSetting(setting.id, e.target.checked); + SettingsStore.setValue(setting.id, null, "device", e.target.checked); if (setting.fn) setting.fn(e.target.checked); }; return
; }, diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 8ee6eafad4..de79c57ec4 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -22,8 +22,8 @@ import { _t, _tJsx } from '../../../languageHandler'; import * as languageHandler from '../../../languageHandler'; import sdk from '../../../index'; import Login from '../../../Login'; -import UserSettingsStore from '../../../UserSettingsStore'; import PlatformPeg from '../../../PlatformPeg'; +import SettingsStore from "../../../settings/SettingsStore"; // For validating phone numbers without country codes const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/; @@ -312,7 +312,7 @@ module.exports = React.createClass({ _onLanguageChange: function(newLang) { if(languageHandler.getCurrentLanguage() !== newLang) { - UserSettingsStore.setLocalSetting('language', newLang); + SettingsStore.setValue("language", null, "device", newLang); PlatformPeg.get().reload(); } }, diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js index 6e3f73f7d5..f6e63ca6f5 100644 --- a/src/components/views/elements/LanguageDropdown.js +++ b/src/components/views/elements/LanguageDropdown.js @@ -20,6 +20,7 @@ import React from 'react'; import sdk from '../../../index'; import UserSettingsStore from '../../../UserSettingsStore'; import * as languageHandler from '../../../languageHandler'; +import SettingsStore from "../../../settings/SettingsStore"; function languageMatchesSearchQuery(query, language) { if (language.label.toUpperCase().indexOf(query.toUpperCase()) == 0) return true; @@ -54,9 +55,10 @@ export default class LanguageDropdown extends React.Component { // If no value is given, we start with the first // country selected, but our parent component // doesn't know this, therefore we do this. - const _localSettings = UserSettingsStore.getLocalSettings(); - if (_localSettings.hasOwnProperty('language')) { - this.props.onOptionChange(_localSettings.language); + // TODO: {Travis} Ensure the default is *not* used for this check. It should try and use the browser. + const language = SettingsStore.getValue("language"); + if (language) { + this.props.onOptionChange(language); }else { const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser()); this.props.onOptionChange(language); @@ -95,12 +97,13 @@ export default class LanguageDropdown extends React.Component { // default value here too, otherwise we need to handle null / undefined // values between mounting and the initial value propgating + // TODO: {Travis} Ensure the default is *not* used for this check. It should try and use the browser. + let language = SettingsStore.getValue("language"); let value = null; - const _localSettings = UserSettingsStore.getLocalSettings(); - if (_localSettings.hasOwnProperty('language')) { - value = this.props.value || _localSettings.language; + if (language) { + value = this.props.value || language; } else { - const language = navigator.language || navigator.userLanguage; + language = navigator.language || navigator.userLanguage; value = this.props.value || language; } diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index ecc908a02c..b2bfb45ad4 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -6,9 +6,9 @@ import isEqual from 'lodash/isEqual'; import sdk from '../../../index'; import type {Completion} from '../../../autocomplete/Autocompleter'; import Promise from 'bluebird'; -import UserSettingsStore from '../../../UserSettingsStore'; import {getCompletions} from '../../../autocomplete/Autocompleter'; +import SettingsStore from "../../../settings/SettingsStore"; const COMPOSER_SELECTED = 0; @@ -66,7 +66,7 @@ export default class Autocomplete extends React.Component { }); return Promise.resolve(null); } - let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200); + let autocompleteDelay = SettingsStore.getValue("autocompleteDelay"); // Don't debounce if we are already showing completions if (this.state.completions.length > 0 || this.state.forceComplete) { diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index f582cc29ef..e109371f8b 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -23,7 +23,6 @@ import sdk from '../../../index'; import Modal from '../../../Modal'; import ObjectUtils from '../../../ObjectUtils'; import dis from '../../../dispatcher'; -import UserSettingsStore from '../../../UserSettingsStore'; import AccessibleButton from '../elements/AccessibleButton'; import SettingsStore from "../../../settings/SettingsStore"; @@ -370,7 +369,8 @@ module.exports = React.createClass({ }, _isRoomBlacklistUnverified: function() { - const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom; + // TODO: {Travis} Use generic blacklistUnverifiedDevices + const blacklistUnverifiedDevicesPerRoom = SettingsStore.getValue("blacklistUnverifiedDevicesPerRoom"); if (blacklistUnverifiedDevicesPerRoom) { return blacklistUnverifiedDevicesPerRoom[this.props.room.roomId]; } @@ -378,9 +378,10 @@ module.exports = React.createClass({ }, _setRoomBlacklistUnverified: function(value) { - const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom || {}; + // TODO: {Travis} Use generic blacklistUnverifiedDevices + const blacklistUnverifiedDevicesPerRoom = SettingsStore.getValue("blacklistUnverifiedDevicesPerRoom"); blacklistUnverifiedDevicesPerRoom[this.props.room.roomId] = value; - UserSettingsStore.setLocalSetting('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom); + SettingsStore.setValue("blacklistUnverifiedDevicesPerRoom", null, "device", blacklistUnverifiedDevicesPerRoom); this.props.room.setBlacklistUnverifiedDevices(value); }, @@ -591,7 +592,7 @@ module.exports = React.createClass({ const cli = MatrixClientPeg.get(); const roomState = this.props.room.currentState; const isEncrypted = cli.isRoomEncrypted(this.props.room.roomId); - const isGlobalBlacklistUnverified = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevices; + const isGlobalBlacklistUnverified = SettingsStore.getValue("blacklistUnverifiedDevices"); const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified(); const settings = diff --git a/src/languageHandler.js b/src/languageHandler.js index a90b78c40e..599c1edb40 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -19,8 +19,7 @@ import request from 'browser-request'; import counterpart from 'counterpart'; import Promise from 'bluebird'; import React from 'react'; - -import UserSettingsStore from './UserSettingsStore'; +import SettingsStore from "./settings/SettingsStore"; const i18nFolder = 'i18n/'; @@ -168,7 +167,7 @@ export function setLanguage(preferredLangs) { }).then((langData) => { counterpart.registerTranslations(langToUse, langData); counterpart.setLocale(langToUse); - UserSettingsStore.setLocalSetting('language', langToUse); + SettingsStore.setValue("language", null, "device", langToUse); console.log("set language to " + langToUse); // Set 'en' as fallback language: diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 804b5df65c..357c5eb484 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -157,6 +157,41 @@ const SETTINGS = { supportedLevels: LEVELS_PRESET_ACCOUNT, default: "light", }, + "webRtcForceTURN": { + supportedLevels: ['device'], + default: false, + displayName: _td('Disable Peer-to-Peer for 1:1 calls'), + }, + "webrtc_audioinput": { + supportedLevels: ['device'], + }, + "webrtc_videoinput": { + supportedLevels: ['device'], + }, + "language": { + supportedLevels: ['device'], + default: "en" + }, + "analyticsOptOut": { + supportedLevels: ['device'], + default: false, + displayName: _td('Opt out of analytics'), + }, + "autocompleteDelay": { + supportedLevels: ['device'], + default: 200, + }, + "blacklistUnverifiedDevicesPerRoom": { + // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices + supportedLevels: ['device'], + default: {}, + }, + "blacklistUnverifiedDevices": { + // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices + supportedLevels: ['device'], + default: false, + label: _td('Never send encrypted messages to unverified devices from this device'), + } }; // Convert the above into simpler formats for the handlers