Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into rxl881/invertOutlineColour

This commit is contained in:
Richard Lewis 2017-11-15 13:26:29 +00:00
commit 7bab7813bb
57 changed files with 2199 additions and 756 deletions

View file

@ -19,7 +19,6 @@ limitations under the License.
import * as Matrix from 'matrix-js-sdk';
import React from 'react';
import UserSettingsStore from '../../UserSettingsStore';
import KeyCode from '../../KeyCode';
import Notifier from '../../Notifier';
import PageTypes from '../../PageTypes';
@ -28,6 +27,7 @@ import sdk from '../../index';
import dis from '../../dispatcher';
import sessionStore from '../../stores/SessionStore';
import MatrixClientPeg from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore";
/**
* This is what our MatrixChat shows when we are logged in. The precise view is
@ -74,7 +74,7 @@ export default React.createClass({
getInitialState: function() {
return {
// use compact timeline view
useCompactLayout: UserSettingsStore.getSyncedSetting('useCompactLayout'),
useCompactLayout: SettingsStore.getValue('useCompactLayout'),
};
},

View file

@ -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, {SettingLevel} from "../../settings/SettingsStore";
/** constants for MatrixChat.state.view */
const VIEWS = {
@ -224,7 +224,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;
@ -591,6 +591,9 @@ module.exports = React.createClass({
this._onWillStartClient();
});
break;
case 'client_started':
this._onClientStarted();
break;
case 'new_version':
this.onVersion(
payload.currentVersion, payload.newVersion,
@ -922,6 +925,11 @@ module.exports = React.createClass({
styleElements[theme].disabled = false;
const switchTheme = function() {
// we re-enable our theme here just in case we raced with another
// theme set request as per https://github.com/vector-im/riot-web/issues/5601.
// We could alternatively lock or similar to stop the race, but
// this is probably good enough for now.
styleElements[theme].disabled = false;
Object.values(styleElements).forEach((a) => {
if (a == styleElements[theme]) return;
a.disabled = true;
@ -1119,6 +1127,34 @@ module.exports = React.createClass({
cli.on("crypto.roomKeyRequestCancellation", (req) => {
krh.handleKeyRequestCancellation(req);
});
cli.on("Room", (room) => {
if (MatrixClientPeg.get().isCryptoEnabled()) {
const blacklistEnabled = SettingsStore.getValueAt(
SettingLevel.ROOM_DEVICE,
"blacklistUnverifiedDevices",
room.roomId,
/*explicit=*/true,
);
room.setBlacklistUnverifiedDevices(blacklistEnabled);
}
});
},
/**
* Called shortly after the matrix client has started. Useful for
* setting up anything that requires the client to be started.
* @private
*/
_onClientStarted: function() {
const cli = MatrixClientPeg.get();
if (cli.isCryptoEnabled()) {
const blacklistEnabled = SettingsStore.getValueAt(
SettingLevel.DEVICE,
"blacklistUnverifiedDevices",
);
cli.setGlobalBlacklistUnverifiedDevices(blacklistEnabled);
}
},
showScreen: function(screen, params) {

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import UserSettingsStore from '../../UserSettingsStore';
import shouldHideEvent from '../../shouldHideEvent';
import dis from "../../dispatcher";
import sdk from '../../index';
@ -110,8 +109,6 @@ module.exports = React.createClass({
// Velocity requires
this._readMarkerGhostNode = null;
this._syncedSettings = UserSettingsStore.getSyncedSettings();
this._isMounted = true;
},
@ -251,7 +248,7 @@ module.exports = React.createClass({
// Always show highlighted event
if (this.props.highlightedEventId === mxEv.getId()) return true;
return !shouldHideEvent(mxEv, this._syncedSettings);
return !shouldHideEvent(mxEv);
},
_getEventTiles: function() {

View file

@ -29,7 +29,6 @@ const classNames = require("classnames");
const Matrix = require("matrix-js-sdk");
import { _t } from '../../languageHandler';
const UserSettingsStore = require('../../UserSettingsStore');
const MatrixClientPeg = require("../../MatrixClientPeg");
const ContentMessages = require("../../ContentMessages");
const Modal = require("../../Modal");
@ -46,6 +45,7 @@ import KeyCode from '../../KeyCode';
import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
import SettingsStore from "../../settings/SettingsStore";
const DEBUG = false;
let debuglog = function() {};
@ -149,8 +149,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("RoomMember.membership", this.onRoomMemberMembership);
MatrixClientPeg.get().on("accountData", this.onAccountData);
this._syncedSettings = UserSettingsStore.getSyncedSettings();
// Start listening for RoomViewStore updates
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
this._onRoomViewStoreUpdate(true);
@ -542,7 +540,7 @@ module.exports = React.createClass({
// update unread count when scrolled up
if (!this.state.searchResults && this.state.atEndOfLiveTimeline) {
// no change
} else if (!shouldHideEvent(ev, this._syncedSettings)) {
} else if (!shouldHideEvent(ev)) {
this.setState((state, props) => {
return {numUnreadMessages: state.numUnreadMessages + 1};
});
@ -616,38 +614,8 @@ module.exports = React.createClass({
},
_updatePreviewUrlVisibility: function(room) {
// console.log("_updatePreviewUrlVisibility");
// check our per-room overrides
const roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
if (roomPreviewUrls && roomPreviewUrls.getContent().disable !== undefined) {
this.setState({
showUrlPreview: !roomPreviewUrls.getContent().disable,
});
return;
}
// check our global disable override
const userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
if (userRoomPreviewUrls && userRoomPreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false,
});
return;
}
// check the room state event
const roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
if (roomStatePreviewUrls && roomStatePreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false,
});
return;
}
// otherwise, we assume they're on.
this.setState({
showUrlPreview: true,
showUrlPreview: SettingsStore.getValue("urlPreviewsEnabled", room.roomId),
});
},
@ -666,12 +634,7 @@ module.exports = React.createClass({
const room = this.state.room;
if (!room) return;
const color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
let color_scheme = {};
if (color_scheme_event) {
color_scheme = color_scheme_event.getContent();
// XXX: we should validate the event
}
const color_scheme = SettingsStore.getValue("roomColor", room.room_id);
console.log("Tinter.tint from updateTint");
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
},
@ -1775,7 +1738,7 @@ module.exports = React.createClass({
const messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef}
timelineSet={this.state.room.getUnfilteredTimelineSet()}
showReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
showReadReceipts={!SettingsStore.getValue('hideReadReceipts')}
manageReadReceipts={!this.state.isPeeking}
manageReadMarkers={!this.state.isPeeking}
hidden={hideMessagePanel}

View file

@ -15,6 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import SettingsStore from "../../settings/SettingsStore";
const React = require('react');
const ReactDOM = require("react-dom");
import Promise from 'bluebird';
@ -30,7 +32,6 @@ const ObjectUtils = require('../../ObjectUtils');
const Modal = require("../../Modal");
const UserActivity = require("../../UserActivity");
const KeyCode = require('../../KeyCode');
import UserSettingsStore from '../../UserSettingsStore';
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
@ -129,8 +130,6 @@ var TimelinePanel = React.createClass({
}
}
const syncedSettings = UserSettingsStore.getSyncedSettings();
return {
events: [],
timelineLoading: true, // track whether our room timeline is loading
@ -175,10 +174,10 @@ var TimelinePanel = React.createClass({
clientSyncState: MatrixClientPeg.get().getSyncState(),
// should the event tiles have twelve hour times
isTwelveHour: syncedSettings.showTwelveHourTimestamps,
isTwelveHour: SettingsStore.getValue("showTwelveHourTimestamps"),
// always show timestamps on event tiles?
alwaysShowTimestamps: syncedSettings.alwaysShowTimestamps,
alwaysShowTimestamps: SettingsStore.getValue("alwaysShowTimestamps"),
};
},

View file

@ -15,6 +15,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
const React = require('react');
const ReactDOM = require('react-dom');
const sdk = require('../../index');
@ -56,138 +58,64 @@ const gHVersionLabel = function(repo, token='') {
return <a target="_blank" rel="noopener" href={url}>{ token }</a>;
};
// Enumerate some simple 'flip a bit' UI settings (if any).
// 'id' gives the key name in the im.vector.web.settings account data event
// 'label' is how we describe it in the UI.
// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
// since they will be translated when rendered.
const SETTINGS_LABELS = [
{
id: 'autoplayGifsAndVideos',
label: _td('Autoplay GIFs and videos'),
},
{
id: 'hideReadReceipts',
label: _td('Hide read receipts'),
},
{
id: 'dontSendTypingNotifications',
label: _td("Don't send typing notifications"),
},
{
id: 'alwaysShowTimestamps',
label: _td('Always show message timestamps'),
},
{
id: 'showTwelveHourTimestamps',
label: _td('Show timestamps in 12 hour format (e.g. 2:30pm)'),
},
{
id: 'hideJoinLeaves',
label: _td('Hide join/leave messages (invites/kicks/bans unaffected)'),
},
{
id: 'hideAvatarDisplaynameChanges',
label: _td('Hide avatar and display name changes'),
},
{
id: 'useCompactLayout',
label: _td('Use compact timeline layout'),
},
{
id: 'hideRedactions',
label: _td('Hide removed messages'),
},
{
id: 'enableSyntaxHighlightLanguageDetection',
label: _td('Enable automatic language detection for syntax highlighting'),
},
{
id: 'MessageComposerInput.autoReplaceEmoji',
label: _td('Automatically replace plain text Emoji'),
},
{
id: 'MessageComposerInput.dontSuggestEmoji',
label: _td('Disable Emoji suggestions while typing'),
},
{
id: 'Pill.shouldHidePillAvatar',
label: _td('Hide avatars in user and room mentions'),
},
{
id: 'TextualBody.disableBigEmoji',
label: _td('Disable big emoji in chat'),
},
{
id: 'VideoView.flipVideoHorizontally',
label: _td('Mirror local video feed'),
},
/*
{
id: 'useFixedWidthFont',
label: 'Use fixed width font',
},
*/
// Enumerate some simple 'flip a bit' UI settings (if any). The strings provided here
// must be settings defined in SettingsStore.
const SIMPLE_SETTINGS = [
{ id: "urlPreviewsEnabled" },
{ id: "autoplayGifsAndVideos" },
{ id: "hideReadReceipts" },
{ id: "dontSendTypingNotifications" },
{ id: "alwaysShowTimestamps" },
{ id: "showTwelveHourTimestamps" },
{ id: "hideJoinLeaves" },
{ id: "hideAvatarChanges" },
{ id: "hideDisplaynameChanges" },
{ id: "useCompactLayout" },
{ id: "hideRedactions" },
{ id: "enableSyntaxHighlightLanguageDetection" },
{ id: "MessageComposerInput.autoReplaceEmoji" },
{ id: "MessageComposerInput.dontSuggestEmoji" },
{ id: "Pill.shouldHidePillAvatar" },
{ id: "TextualBody.disableBigEmoji" },
{ id: "VideoView.flipVideoHorizontally" },
];
const ANALYTICS_SETTINGS_LABELS = [
// These settings must be defined in SettingsStore
const ANALYTICS_SETTINGS = [
{
id: 'analyticsOptOut',
label: _td('Opt out of analytics'),
fn: function(checked) {
Analytics[checked ? 'disable' : 'enable']();
},
},
];
const WEBRTC_SETTINGS_LABELS = [
{
id: 'webRtcForceTURN',
label: _td('Disable Peer-to-Peer for 1:1 calls'),
},
// These settings must be defined in SettingsStore
const WEBRTC_SETTINGS = [
{ id: 'webRtcForceTURN' },
];
// 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 = [
// These settings must be defined in SettingsStore
const CRYPTO_SETTINGS = [
{
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.
// 'id' gives the key name in the im.vector.web.settings account data event
// 'value' is the value for that key in the event
// 'label' is how we describe it in the UI.
// 'value' is the value for the theme setting
//
// XXX: Ideally we would have a theme manifest or something and they'd be nicely
// packaged up in a single directory, and/or located at the application layer.
// But for now for expedience we just hardcode them here.
const THEMES = [
{
id: 'theme',
label: _td('Light theme'),
value: 'light',
},
{
id: 'theme',
label: _td('Dark theme'),
value: 'dark',
},
{
id: 'theme',
label: _td('Status.im theme'),
value: 'status',
},
{ label: _td('Light theme'), value: 'light' },
{ label: _td('Dark theme'), value: 'dark' },
{ label: _td('Status.im theme'), value: 'status' },
];
const IgnoredUser = React.createClass({
@ -286,14 +214,6 @@ module.exports = React.createClass({
});
this._refreshFromServer();
const syncedSettings = UserSettingsStore.getSyncedSettings();
if (!syncedSettings.theme) {
syncedSettings.theme = SdkConfig.get().default_theme || 'light';
}
this._syncedSettings = syncedSettings;
this._localSettings = UserSettingsStore.getLocalSettings();
if (PlatformPeg.get().isElectron()) {
const {ipcRenderer} = require('electron');
@ -364,8 +284,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(SettingLevel.DEVICE, 'webrtc_audioinput'),
activeVideoInput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_videoinput'),
});
});
},
@ -497,10 +417,6 @@ module.exports = React.createClass({
dis.dispatch({action: 'password_changed'});
},
onEnableNotificationsChange: function(event) {
UserSettingsStore.setEnableNotifications(event.target.checked);
},
_onAddEmailEditFinished: function(value, shouldSubmit) {
if (!shouldSubmit) return;
this._addEmail();
@ -697,7 +613,8 @@ module.exports = React.createClass({
onLanguageChange: function(newLang) {
if(this.state.language !== newLang) {
UserSettingsStore.setLocalSetting('language', newLang);
// We intentionally promote this to the account level at this point
SettingsStore.setValue("language", null, SettingLevel.ACCOUNT, newLang);
this.setState({
language: newLang,
});
@ -720,14 +637,13 @@ 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, SettingLevel.DEVICE, e.target.value);
return (
<div>
<h3>{ _t("User Interface") }</h3>
<div className="mx_UserSettings_section">
{ this._renderUrlPreviewSelector() }
{ SETTINGS_LABELS.map( this._renderSyncedSetting ) }
{ THEMES.map( this._renderThemeSelector ) }
{ SIMPLE_SETTINGS.map( this._renderAccountSetting ) }
{ THEMES.map( this._renderThemeOption ) }
<table>
<tbody>
<tr>
@ -735,7 +651,7 @@ module.exports = React.createClass({
<td>
<input
type="number"
defaultValue={UserSettingsStore.getLocalSetting('autocompleteDelay', 200)}
defaultValue={SettingsStore.getValueAt(SettingLevel.DEVICE, "autocompleteDelay")}
onChange={onChange}
/>
</td>
@ -748,69 +664,31 @@ module.exports = React.createClass({
);
},
_renderUrlPreviewSelector: function() {
return <div className="mx_UserSettings_toggle">
<input id="urlPreviewsDisabled"
type="checkbox"
defaultChecked={UserSettingsStore.getUrlPreviewsDisabled()}
onChange={this._onPreviewsDisabledChanged}
/>
<label htmlFor="urlPreviewsDisabled">
{ _t("Disable inline URL previews by default") }
</label>
</div>;
_renderAccountSetting: function(setting) {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
return (
<div className="mx_UserSettings_toggle" key={setting.id}>
<SettingsFlag name={setting.id}
label={setting.label}
level={SettingLevel.ACCOUNT}
onChange={setting.fn} />
</div>
);
},
_onPreviewsDisabledChanged: function(e) {
UserSettingsStore.setUrlPreviewsDisabled(e.target.checked);
},
_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
const onChange = (e) => {
UserSettingsStore.setSyncedSetting(setting.id, e.target.checked);
if (setting.fn) setting.fn(e.target.checked);
};
return <div className="mx_UserSettings_toggle" key={setting.id}>
<input id={setting.id}
type="checkbox"
defaultChecked={this._syncedSettings[setting.id]}
onChange={onChange}
/>
<label htmlFor={setting.id}>
{ _t(setting.label) }
</label>
</div>;
},
_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
const onChange = (e) => {
if (e.target.checked) {
this._syncedSettings[setting.id] = setting.value;
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
}
dis.dispatch({
action: 'set_theme',
value: setting.value,
});
};
return <div className="mx_UserSettings_toggle" key={setting.id + "_" + setting.value}>
<input id={setting.id + "_" + setting.value}
type="radio"
name={setting.id}
value={setting.value}
checked={this._syncedSettings[setting.id] === setting.value}
onChange={onChange}
/>
<label htmlFor={setting.id + "_" + setting.value}>
{ _t(setting.label) }
</label>
</div>;
_renderThemeOption: function(setting) {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
const onChange = (v) => dis.dispatch({action: 'set_theme', value: setting.value});
return (
<div className="mx_UserSettings_toggle" key={setting.id + '_' + setting.value}>
<SettingsFlag name="theme"
label={setting.label}
level={SettingLevel.ACCOUNT}
onChange={onChange}
group="theme"
value={setting.value} />
</div>
);
},
_renderCryptoInfo: function() {
@ -852,7 +730,7 @@ module.exports = React.createClass({
{ importExportButtons }
</div>
<div className="mx_UserSettings_section">
{ CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) }
{ CRYPTO_SETTINGS.map( this._renderDeviceSetting ) }
</div>
</div>
);
@ -878,24 +756,16 @@ module.exports = React.createClass({
} else return (<div />);
},
_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);
if (setting.fn) setting.fn(e.target.checked);
};
return <div className="mx_UserSettings_toggle" key={setting.id}>
<input id={setting.id}
type="checkbox"
defaultChecked={this._localSettings[setting.id]}
onChange={onChange}
/>
<label htmlFor={setting.id}>
{ _t(setting.label) }
</label>
</div>;
_renderDeviceSetting: function(setting) {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
return (
<div className="mx_UserSettings_toggle" key={setting.id}>
<SettingsFlag name={setting.id}
label={setting.label}
level={SettingLevel.DEVICE}
onChange={setting.fn} />
</div>
);
},
_renderDevicesPanel: function() {
@ -932,18 +802,18 @@ module.exports = React.createClass({
<h3>{ _t('Analytics') }</h3>
<div className="mx_UserSettings_section">
{ _t('Riot collects anonymous analytics to allow us to improve the application.') }
{ ANALYTICS_SETTINGS_LABELS.map( this._renderLocalSetting ) }
{ ANALYTICS_SETTINGS.map( this._renderDeviceSetting ) }
</div>
</div>;
},
_renderLabs: function() {
const features = [];
UserSettingsStore.getLabsFeatures().forEach((featureId) => {
SettingsStore.getLabsFeatures().forEach((featureId) => {
// 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.setFeatureEnabled(featureId, e.target.checked);
SettingsStore.setFeatureEnabled(featureId, e.target.checked);
this.forceUpdate();
};
@ -953,10 +823,10 @@ module.exports = React.createClass({
type="checkbox"
id={featureId}
name={featureId}
defaultChecked={UserSettingsStore.isFeatureEnabled(featureId)}
defaultChecked={SettingsStore.isFeatureEnabled(featureId)}
onChange={onChange}
/>
<label htmlFor={featureId}>{ UserSettingsStore.translatedNameForFeature(featureId) }</label>
<label htmlFor={featureId}>{ SettingsStore.getDisplayName(featureId) }</label>
</div>);
});
@ -1049,6 +919,8 @@ module.exports = React.createClass({
const settings = this.state.electron_settings;
if (!settings) return;
// TODO: This should probably be a granular setting, but it only applies to electron
// and ends up being get/set outside of matrix anyways (local system setting).
return <div>
<h3>{ _t('Desktop specific') }</h3>
<div className="mx_UserSettings_section">
@ -1171,7 +1043,7 @@ module.exports = React.createClass({
return <div>
<h3>{ _t('VoIP') }</h3>
<div className="mx_UserSettings_section">
{ WEBRTC_SETTINGS_LABELS.map(this._renderLocalSetting) }
{ WEBRTC_SETTINGS.map(this._renderDeviceSetting) }
{ this._renderWebRtcDeviceSettings() }
</div>
</div>;

View file

@ -22,9 +22,9 @@ 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 SdkConfig from '../../../SdkConfig';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/;
@ -328,7 +328,7 @@ module.exports = React.createClass({
_onLanguageChange: function(newLang) {
if(languageHandler.getCurrentLanguage() !== newLang) {
UserSettingsStore.setLocalSetting('language', newLang);
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang);
PlatformPeg.get().reload();
}
},
@ -385,7 +385,7 @@ module.exports = React.createClass({
}
// FIXME: remove status.im theme tweaks
const theme = UserSettingsStore.getTheme();
const theme = SettingsStore.getValue("theme");
if (theme !== "status") {
header = <h2>{ _t('Sign in') }</h2>;
}

View file

@ -26,8 +26,8 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import RegistrationForm from '../../views/login/RegistrationForm';
import RtsClient from '../../../RtsClient';
import { _t } from '../../../languageHandler';
import UserSettingsStore from '../../../UserSettingsStore';
import SdkConfig from '../../../SdkConfig';
import SettingsStore from "../../../settings/SettingsStore";
const MIN_PASSWORD_LENGTH = 6;
@ -329,7 +329,7 @@ module.exports = React.createClass({
const Spinner = sdk.getComponent("elements.Spinner");
const ServerConfig = sdk.getComponent('views.login.ServerConfig');
const theme = UserSettingsStore.getTheme();
const theme = SettingsStore.getValue("theme");
let registerBody;
if (this.state.doingUIAuth) {

View file

@ -22,8 +22,8 @@ import MatrixClientPeg from "../../../MatrixClientPeg";
import AccessibleButton from '../elements/AccessibleButton';
import Presence from "../../../Presence";
import dispatcher from "../../../dispatcher";
import UserSettingsStore from "../../../UserSettingsStore";
import * as ContextualMenu from "../../structures/ContextualMenu";
import SettingsStore from "../../../settings/SettingsStore";
module.exports = React.createClass({
displayName: 'MemberPresenceAvatar',
@ -133,7 +133,7 @@ module.exports = React.createClass({
);
// LABS: Disable presence management functions for now
if (!UserSettingsStore.isFeatureEnabled("feature_presence_management")) {
if (!SettingsStore.isFeatureEnabled("feature_presence_management")) {
statusNode = null;
onClickFn = null;
}

View file

@ -20,6 +20,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import GeminiScrollbar from 'react-gemini-scrollbar';
import Resend from '../../../Resend';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
function DeviceListEntry(props) {
const {userId, device} = props;
@ -112,12 +113,13 @@ export default React.createClass({
},
render: function() {
const client = MatrixClientPeg.get();
const blacklistUnverified = client.getGlobalBlacklistUnverifiedDevices() ||
this.props.room.getBlacklistUnverifiedDevices();
if (this.state.devices === null) {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
let warning;
if (blacklistUnverified) {
if (SettingsStore.getValue("blacklistUnverifiedDevices", this.props.room.roomId)) {
warning = (
<h4>
{ _t("You are currently blacklisting unverified devices; to send " +

View file

@ -18,8 +18,8 @@ limitations under the License.
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 +54,9 @@ 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);
const language = SettingsStore.getValue("language", null, /*excludeDefault:*/true);
if (language) {
this.props.onOptionChange(language);
}else {
const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser());
this.props.onOptionChange(language);
@ -95,12 +95,12 @@ 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
let language = SettingsStore.getValue("language", null, /*excludeDefault:*/true);
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;
}

View file

@ -0,0 +1,110 @@
/*
Copyright 2017 Travis Ralston
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import SettingsStore from "../../../settings/SettingsStore";
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'SettingsFlag',
propTypes: {
name: React.PropTypes.string.isRequired,
level: React.PropTypes.string.isRequired,
roomId: React.PropTypes.string, // for per-room settings
label: React.PropTypes.string, // untranslated
onChange: React.PropTypes.func,
isExplicit: React.PropTypes.bool,
manualSave: React.PropTypes.bool,
// If group is supplied, then this will create a radio button instead.
group: React.PropTypes.string,
value: React.PropTypes.any, // the value for the radio button
},
getInitialState: function() {
return {
value: SettingsStore.getValueAt(
this.props.level,
this.props.name,
this.props.roomId,
this.props.isExplicit,
),
};
},
onChange: function(e) {
if (this.props.group && !e.target.checked) return;
const newState = this.props.group ? this.props.value : e.target.checked;
if (!this.props.manualSave) this.save(newState);
else this.setState({ value: newState });
if (this.props.onChange) this.props.onChange(newState);
},
save: function(val = undefined) {
return SettingsStore.setValue(
this.props.name,
this.props.roomId,
this.props.level,
val !== undefined ? val : this.state.value,
);
},
render: function() {
const value = this.props.manualSave ? this.state.value : SettingsStore.getValueAt(
this.props.level,
this.props.name,
this.props.roomId,
this.props.isExplicit,
);
const canChange = SettingsStore.canSetValue(this.props.name, this.props.roomId, this.props.level);
let label = this.props.label;
if (!label) label = SettingsStore.getDisplayName(this.props.name, this.props.level);
else label = _t(label);
// We generate a relatively complex ID to avoid conflicts
const id = this.props.name + "_" + this.props.group + "_" + this.props.value + "_" + this.props.level;
let checkbox = (
<input id={id}
type="checkbox"
defaultChecked={value}
onChange={this.onChange}
disabled={!canChange}
/>
);
if (this.props.group) {
checkbox = (
<input id={id}
type="radio"
name={this.props.group}
value={this.props.value}
checked={value === this.props.value}
onChange={this.onChange}
disabled={!canChange}
/>
);
}
return (
<label>
{ checkbox }
{ label }
</label>
);
},
});

View file

@ -16,7 +16,7 @@ limitations under the License.
'use strict';
import UserSettingsStore from '../../../UserSettingsStore';
import SettingsStore from "../../../settings/SettingsStore";
const React = require('react');
@ -25,7 +25,7 @@ module.exports = React.createClass({
render: function() {
// FIXME: this should be turned into a proper skin with a StatusLoginPage component
if (UserSettingsStore.getTheme() === 'status') {
if (SettingsStore.getValue("theme") === 'status') {
return (
<div className="mx_StatusLogin">
<div className="mx_StatusLogin_brand">

View file

@ -22,8 +22,8 @@ import Email from '../../../email';
import { looksValid as phoneNumberLooksValid } from '../../../phonenumber';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
import UserSettingsStore from '../../../UserSettingsStore';
import SdkConfig from '../../../SdkConfig';
import SettingsStore from "../../../settings/SettingsStore";
const FIELD_EMAIL = 'field_email';
const FIELD_PHONE_COUNTRY = 'field_phone_country';
@ -275,7 +275,7 @@ module.exports = React.createClass({
render: function() {
const self = this;
const theme = UserSettingsStore.getTheme();
const theme = SettingsStore.getValue("theme");
// FIXME: remove hardcoded Status team tweaks at some point
const emailPlaceholder = theme === 'status' ? _t("Email address") : _t("Email address (optional)");

View file

@ -25,8 +25,8 @@ import sdk from '../../../index';
import dis from '../../../dispatcher';
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
module.exports = React.createClass({
displayName: 'MImageBody',
@ -81,7 +81,7 @@ module.exports = React.createClass({
},
onImageEnter: function(e) {
if (!this._isGif() || UserSettingsStore.getSyncedSetting("autoplayGifsAndVideos", false)) {
if (!this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
return;
}
const imgElement = e.target;
@ -89,7 +89,7 @@ module.exports = React.createClass({
},
onImageLeave: function(e) {
if (!this._isGif() || UserSettingsStore.getSyncedSetting("autoplayGifsAndVideos", false)) {
if (!this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
return;
}
const imgElement = e.target;
@ -218,7 +218,7 @@ module.exports = React.createClass({
const contentUrl = this._getContentUrl();
let thumbUrl;
if (this._isGif() && UserSettingsStore.getSyncedSetting("autoplayGifsAndVideos", false)) {
if (this._isGif() && SettingsStore.getValue("autoplayGifsAndVideos")) {
thumbUrl = contentUrl;
} else {
thumbUrl = this._getThumbUrl();

View file

@ -21,8 +21,8 @@ import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
module.exports = React.createClass({
displayName: 'MVideoBody',
@ -151,7 +151,7 @@ module.exports = React.createClass({
const contentUrl = this._getContentUrl();
const thumbUrl = this._getThumbUrl();
const autoplay = UserSettingsStore.getSyncedSetting("autoplayGifsAndVideos", false);
const autoplay = SettingsStore.getValue("autoplayGifsAndVideos");
let height = null;
let width = null;
let poster = null;

View file

@ -29,11 +29,11 @@ import Modal from '../../../Modal';
import SdkConfig from '../../../SdkConfig';
import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler';
import UserSettingsStore from "../../../UserSettingsStore";
import MatrixClientPeg from '../../../MatrixClientPeg';
import ContextualMenu from '../../structures/ContextualMenu';
import {RoomMember} from 'matrix-js-sdk';
import classNames from 'classnames';
import SettingsStore from "../../../settings/SettingsStore";
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
linkifyMatrix(linkify);
@ -104,7 +104,7 @@ module.exports = React.createClass({
setTimeout(() => {
if (this._unmounted) return;
for (let i = 0; i < blocks.length; i++) {
if (UserSettingsStore.getSyncedSetting("enableSyntaxHighlightLanguageDetection", false)) {
if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
highlight.highlightBlock(blocks[i]);
} else {
// Only syntax highlight if there's a class starting with language-
@ -169,7 +169,7 @@ module.exports = React.createClass({
},
pillifyLinks: function(nodes) {
const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false);
const shouldShowPillAvatar = !SettingsStore.getValue("Pill.shouldHidePillAvatar");
let node = nodes[0];
while (node) {
let pillified = false;
@ -419,7 +419,7 @@ module.exports = React.createClass({
const content = mxEvent.getContent();
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
disableBigEmoji: UserSettingsStore.getSyncedSetting('TextualBody.disableBigEmoji', false),
disableBigEmoji: SettingsStore.getValue('TextualBody.disableBigEmoji'),
});
if (this.props.highlightLink) {

View file

@ -22,6 +22,7 @@ const MatrixClientPeg = require("../../../MatrixClientPeg");
const Modal = require("../../../Modal");
import dis from '../../../dispatcher';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
const ROOM_COLORS = [
// magic room default values courtesy of Ribot
@ -47,17 +48,17 @@ module.exports = React.createClass({
getInitialState: function() {
const data = {
index: 0,
primary_color: ROOM_COLORS[0].primary_color,
secondary_color: ROOM_COLORS[0].secondary_color,
primary_color: ROOM_COLORS[0][0],
secondary_color: ROOM_COLORS[0][1],
hasChanged: false,
};
const event = this.props.room.getAccountData("org.matrix.room.color_scheme");
if (!event) {
return data;
const scheme = SettingsStore.getValueAt(SettingLevel.ROOM_ACCOUNT, "roomColor", this.props.room.roomId);
if (scheme.primary_color && scheme.secondary_color) {
// We only use the user's scheme if the scheme is valid.
data.primary_color = scheme.primary_color;
data.secondary_color = scheme.secondary_color;
}
const scheme = event.getContent();
data.primary_color = scheme.primary_color;
data.secondary_color = scheme.secondary_color;
data.index = this._getColorIndex(data);
if (data.index === -1) {
@ -81,13 +82,13 @@ module.exports = React.createClass({
// We would like guests to be able to set room colour but currently
// they can't, so we still send the request but display a sensible
// error if it fails.
return MatrixClientPeg.get().setRoomAccountData(
this.props.room.roomId, "org.matrix.room.color_scheme", {
primary_color: this.state.primary_color,
secondary_color: this.state.secondary_color,
},
).catch(function(err) {
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
// TODO: Support guests for room color. Technically this is possible via granular settings
// Granular settings would mean the guest is forced to use the DEVICE level though.
SettingsStore.setValue("roomColor", this.props.room.roomId, SettingLevel.ROOM_ACCOUNT, {
primary_color: this.state.primary_color,
secondary_color: this.state.secondary_color,
}).catch(function(err) {
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') {
dis.dispatch({action: 'view_set_mxid'});
}
});

View file

@ -1,5 +1,6 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Travis Ralston
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,13 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import Promise from 'bluebird';
const React = require('react');
const MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require("../../../index");
const Modal = require("../../../Modal");
const UserSettingsStore = require('../../../UserSettingsStore');
import { _t, _tJsx } from '../../../languageHandler';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
module.exports = React.createClass({
@ -30,137 +28,64 @@ module.exports = React.createClass({
room: React.PropTypes.object,
},
getInitialState: function() {
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
const roomPreviewUrls = this.props.room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
const userPreviewUrls = this.props.room.getAccountData("org.matrix.room.preview_urls");
return {
globalDisableUrlPreview: (roomPreviewUrls && roomPreviewUrls.getContent().disable) || false,
userDisableUrlPreview: (userPreviewUrls && (userPreviewUrls.getContent().disable === true)) || false,
userEnableUrlPreview: (userPreviewUrls && (userPreviewUrls.getContent().disable === false)) || false,
};
},
componentDidMount: function() {
this.originalState = Object.assign({}, this.state);
},
saveSettings: function() {
const promises = [];
if (this.state.globalDisableUrlPreview !== this.originalState.globalDisableUrlPreview) {
console.log("UrlPreviewSettings: Updating room's preview_urls state event");
promises.push(
MatrixClientPeg.get().sendStateEvent(
this.props.room.roomId, "org.matrix.room.preview_urls", {
disable: this.state.globalDisableUrlPreview,
}, "",
),
);
}
let content = undefined;
if (this.state.userDisableUrlPreview !== this.originalState.userDisableUrlPreview) {
console.log("UrlPreviewSettings: Disabling user's per-room preview_urls");
content = this.state.userDisableUrlPreview ? { disable: true } : {};
}
if (this.state.userEnableUrlPreview !== this.originalState.userEnableUrlPreview) {
console.log("UrlPreviewSettings: Enabling user's per-room preview_urls");
if (!content || content.disable === undefined) {
content = this.state.userEnableUrlPreview ? { disable: false } : {};
}
}
if (content) {
promises.push(
MatrixClientPeg.get().setRoomAccountData(
this.props.room.roomId, "org.matrix.room.preview_urls", content,
),
);
}
console.log("UrlPreviewSettings: saveSettings: " + JSON.stringify(promises));
if (this.refs.urlPreviewsRoom) promises.push(this.refs.urlPreviewsRoom.save());
if (this.refs.urlPrviewsSelf) promises.push(this.refs.urlPreviewsSelf.save());
return promises;
},
onGlobalDisableUrlPreviewChange: function() {
this.setState({
globalDisableUrlPreview: this.refs.globalDisableUrlPreview.checked ? true : false,
});
},
onUserEnableUrlPreviewChange: function() {
this.setState({
userDisableUrlPreview: false,
userEnableUrlPreview: this.refs.userEnableUrlPreview.checked ? true : false,
});
},
onUserDisableUrlPreviewChange: function() {
this.setState({
userDisableUrlPreview: this.refs.userDisableUrlPreview.checked ? true : false,
userEnableUrlPreview: false,
});
},
render: function() {
const self = this;
const roomState = this.props.room.currentState;
const cli = MatrixClientPeg.get();
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
const roomId = this.props.room.roomId;
const maySetRoomPreviewUrls = roomState.mayClientSendStateEvent('org.matrix.room.preview_urls', cli);
let disableRoomPreviewUrls;
if (maySetRoomPreviewUrls) {
disableRoomPreviewUrls =
<label>
<input type="checkbox" ref="globalDisableUrlPreview"
onChange={this.onGlobalDisableUrlPreviewChange}
checked={this.state.globalDisableUrlPreview} />
{ _t("Disable URL previews by default for participants in this room") }
</label>;
} else {
disableRoomPreviewUrls =
<label>
{ _t("URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", {globalDisableUrlPreview: this.state.globalDisableUrlPreview ? _t("disabled") : _t("enabled")}) }
</label>;
}
let urlPreviewText = null;
if (UserSettingsStore.getUrlPreviewsDisabled()) {
urlPreviewText = (
_tJsx("You have <a>disabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
);
} else {
urlPreviewText = (
let previewsForAccount = null;
if (SettingsStore.getValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled")) {
previewsForAccount = (
_tJsx("You have <a>enabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
);
} else {
previewsForAccount = (
_tJsx("You have <a>disabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
);
}
let previewsForRoom = null;
if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, "room")) {
previewsForRoom = (
<label>
<SettingsFlag name="urlPreviewsEnabled"
level={SettingLevel.ROOM}
roomId={this.props.room.roomId}
isExplicit={true}
manualSave={true}
ref="urlPreviewsRoom" />
</label>
);
} else {
let str = "URL previews are enabled by default for participants in this room.";
if (!SettingsStore.getValueAt(SettingLevel.ROOM, "urlPreviewsEnabled")) {
str = "URL previews are disabled by default for participants in this room.";
}
previewsForRoom = (<label>{ _t(str) }</label>);
}
let previewsForRoomAccount = (
<SettingsFlag name="urlPreviewsEnabled"
level={SettingLevel.ROOM_ACCOUNT}
roomId={this.props.room.roomId}
manualSave={true}
ref="urlPreviewsSelf"
/>
);
return (
<div className="mx_RoomSettings_toggles">
<h3>{ _t("URL Previews") }</h3>
<label>
{ urlPreviewText }
</label>
{ disableRoomPreviewUrls }
<label>
<input type="checkbox" ref="userEnableUrlPreview"
onChange={this.onUserEnableUrlPreviewChange}
checked={this.state.userEnableUrlPreview} />
{ _t("Enable URL previews for this room (affects only you)") }
</label>
<label>
<input type="checkbox" ref="userDisableUrlPreview"
onChange={this.onUserDisableUrlPreviewChange}
checked={this.state.userDisableUrlPreview} />
{ _t("Disable URL previews for this room (affects only you)") }
</label>
<label>{ previewsForAccount }</label>
{ previewsForRoom }
<label>{ previewsForRoomAccount }</label>
</div>
);
},

View file

@ -24,9 +24,10 @@ import isEqual from 'lodash/isEqual';
import sdk from '../../../index';
import type {Completion} from '../../../autocomplete/Autocompleter';
import Promise from 'bluebird';
import UserSettingsStore from '../../../UserSettingsStore';
import { Room } from 'matrix-js-sdk';
import {getCompletions} from '../../../autocomplete/Autocompleter';
import SettingsStore from "../../../settings/SettingsStore";
import Autocompleter from '../../../autocomplete/Autocompleter';
const COMPOSER_SELECTED = 0;
@ -95,7 +96,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) {

View file

@ -22,7 +22,6 @@ import dis from "../../../dispatcher";
import ObjectUtils from '../../../ObjectUtils';
import AppsDrawer from './AppsDrawer';
import { _t, _tJsx} from '../../../languageHandler';
import UserSettingsStore from '../../../UserSettingsStore';
module.exports = React.createClass({

View file

@ -22,7 +22,7 @@ import Modal from '../../../Modal';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import Autocomplete from './Autocomplete';
import UserSettingsStore from '../../../UserSettingsStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
export default class MessageComposer extends React.Component {
@ -49,10 +49,10 @@ export default class MessageComposer extends React.Component {
inputState: {
style: [],
blockType: null,
isRichtextEnabled: UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false),
isRichtextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
wordCount: 0,
},
showFormatting: UserSettingsStore.getSyncedSetting('MessageComposer.showFormatting', false),
showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
};
}
@ -226,7 +226,7 @@ export default class MessageComposer extends React.Component {
}
onToggleFormattingClicked() {
UserSettingsStore.setSyncedSetting('MessageComposer.showFormatting', !this.state.showFormatting);
SettingsStore.setValue("MessageComposer.showFormatting", null, SettingLevel.DEVICE, !this.state.showFormatting);
this.setState({showFormatting: !this.state.showFormatting});
}

View file

@ -35,7 +35,6 @@ import { _t, _td } from '../../../languageHandler';
import Analytics from '../../../Analytics';
import dis from '../../../dispatcher';
import UserSettingsStore from '../../../UserSettingsStore';
import * as RichText from '../../../RichText';
import * as HtmlUtils from '../../../HtmlUtils';
@ -50,6 +49,7 @@ const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
const REGEX_MATRIXTO_MARKDOWN_GLOBAL = new RegExp(MATRIXTO_MD_LINK_PATTERN, 'g');
import {asciiRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort} from 'emojione';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
const EMOJI_SHORTNAMES = Object.keys(emojioneList);
const EMOJI_UNICODE_TO_SHORTNAME = mapUnicodeToShort();
const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + asciiRegexp + ')\\s$');
@ -165,7 +165,7 @@ export default class MessageComposerInput extends React.Component {
this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this);
this.onTextPasted = this.onTextPasted.bind(this);
const isRichtextEnabled = UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false);
const isRichtextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled');
Analytics.setRichtextMode(isRichtextEnabled);
@ -216,7 +216,7 @@ export default class MessageComposerInput extends React.Component {
createEditorState(richText: boolean, contentState: ?ContentState): EditorState {
const decorators = richText ? RichText.getScopedRTDecorators(this.props) :
RichText.getScopedMDDecorators(this.props);
const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false);
const shouldShowPillAvatar = !SettingsStore.getValue("Pill.shouldHidePillAvatar");
decorators.push({
strategy: this.findPillEntities.bind(this),
component: (entityProps) => {
@ -384,7 +384,7 @@ export default class MessageComposerInput extends React.Component {
}
sendTyping(isTyping) {
if (UserSettingsStore.getSyncedSetting('dontSendTypingNotifications', false)) return;
if (SettingsStore.getValue('dontSendTypingNotifications')) return;
MatrixClientPeg.get().sendTyping(
this.props.room.roomId,
this.isTyping, TYPING_SERVER_TIMEOUT,
@ -431,7 +431,7 @@ export default class MessageComposerInput extends React.Component {
}
// Automatic replacement of plaintext emoji to Unicode emoji
if (UserSettingsStore.getSyncedSetting('MessageComposerInput.autoReplaceEmoji', false)) {
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
// The first matched group includes just the matched plaintext emoji
const emojiMatch = REGEX_EMOJI_WHITESPACE.exec(text.slice(0, currentStartOffset));
if(emojiMatch) {
@ -551,7 +551,7 @@ export default class MessageComposerInput extends React.Component {
editorState: this.createEditorState(enabled, contentState),
isRichtextEnabled: enabled,
});
UserSettingsStore.setSyncedSetting('MessageComposerInput.isRichTextEnabled', enabled);
SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled);
}
handleKeyCommand = (command: string): boolean => {

View file

@ -31,7 +31,7 @@ import linkifyMatrix from '../../../linkify-matrix';
import AccessibleButton from '../elements/AccessibleButton';
import ManageIntegsButton from '../elements/ManageIntegsButton';
import {CancelButton} from './SimpleRoomHeader';
import UserSettingsStore from "../../../UserSettingsStore";
import SettingsStore from "../../../settings/SettingsStore";
linkifyMatrix(linkify);
@ -339,7 +339,7 @@ module.exports = React.createClass({
</AccessibleButton>;
}
if (this.props.onPinnedClick && UserSettingsStore.isFeatureEnabled('feature_pinning')) {
if (this.props.onPinnedClick && SettingsStore.isFeatureEnabled('feature_pinning')) {
let pinsIndicator = null;
if (this._hasUnreadPins()) {
pinsIndicator = (<div className="mx_RoomHeader_pinsIndicator mx_RoomHeader_pinsIndicatorUnread" />);

View file

@ -23,8 +23,8 @@ 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, {SettingLevel} from "../../../settings/SettingsStore";
// parse a string as an integer; if the input is undefined, or cannot be parsed
@ -309,9 +309,9 @@ module.exports = React.createClass({
}
// url preview settings
const ps = this.saveUrlPreviewSettings();
let ps = this.saveUrlPreviewSettings();
if (ps.length > 0) {
promises.push(ps);
ps.map(p => promises.push(p));
}
// related groups
@ -363,26 +363,16 @@ module.exports = React.createClass({
},
saveBlacklistUnverifiedDevicesPerRoom: function() {
if (!this.refs.blacklistUnverified) return;
if (this._isRoomBlacklistUnverified() !== this.refs.blacklistUnverified.checked) {
this._setRoomBlacklistUnverified(this.refs.blacklistUnverified.checked);
}
},
_isRoomBlacklistUnverified: function() {
const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom;
if (blacklistUnverifiedDevicesPerRoom) {
return blacklistUnverifiedDevicesPerRoom[this.props.room.roomId];
}
return false;
},
_setRoomBlacklistUnverified: function(value) {
const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom || {};
blacklistUnverifiedDevicesPerRoom[this.props.room.roomId] = value;
UserSettingsStore.setLocalSetting('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom);
this.props.room.setBlacklistUnverifiedDevices(value);
if (!this.refs.blacklistUnverifiedDevices) return;
this.refs.blacklistUnverifiedDevices.save().then(() => {
const value = SettingsStore.getValueAt(
SettingLevel.ROOM_DEVICE,
"blacklistUnverifiedDevices",
this.props.room.roomId,
/*explicit=*/true,
);
this.props.room.setBlacklistUnverifiedDevices(value);
});
},
_hasDiff: function(strA, strB) {
@ -588,19 +578,20 @@ module.exports = React.createClass({
},
_renderEncryptionSection: function() {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
const isEncrypted = cli.isRoomEncrypted(this.props.room.roomId);
const isGlobalBlacklistUnverified = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevices;
const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
const settings =
<label>
<input type="checkbox" ref="blacklistUnverified"
defaultChecked={isGlobalBlacklistUnverified || isRoomBlacklistUnverified}
disabled={isGlobalBlacklistUnverified || (this.refs.encrypt && !this.refs.encrypt.checked)} />
{ _t('Never send encrypted messages to unverified devices in this room from this device') }.
</label>;
let settings = (
<SettingsFlag name="blacklistUnverifiedDevices"
level={SettingLevel.ROOM_DEVICE}
roomId={this.props.room.roomId}
manualSave={true}
ref="blacklistUnverifiedDevices"
/>
);
if (!isEncrypted && roomState.mayClientSendStateEvent("m.room.encryption", cli)) {
return (

View file

@ -27,7 +27,6 @@ const ContextualMenu = require('../../structures/ContextualMenu');
const RoomNotifs = require('../../../RoomNotifs');
const FormattingUtils = require('../../../utils/FormattingUtils');
import AccessibleButton from '../elements/AccessibleButton';
const UserSettingsStore = require('../../../UserSettingsStore');
import ActiveRoomObserver from '../../../ActiveRoomObserver';
import RoomViewStore from '../../../stores/RoomViewStore';

View file

@ -23,7 +23,7 @@ import classNames from 'classnames';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import UserSettingsStore from '../../../UserSettingsStore';
import SettingsStore from "../../../settings/SettingsStore";
module.exports = React.createClass({
displayName: 'VideoView',
@ -113,7 +113,7 @@ module.exports = React.createClass({
const maxVideoHeight = fullscreenElement ? null : this.props.maxHeight;
const localVideoFeedClasses = classNames("mx_VideoView_localVideoFeed",
{ "mx_VideoView_localVideoFeed_flipped":
UserSettingsStore.getSyncedSetting('VideoView.flipVideoHorizontally', false),
SettingsStore.getValue('VideoView.flipVideoHorizontally'),
},
);
return (