diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 38b7634edb9..740ffd1629d 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -77,12 +77,15 @@ const LoggedInView = React.createClass({ return { // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), + interfaceScale: SettingsStore.getValue('interfaceScale'), + chatFontScale: SettingsStore.getValue('chatFontScale'), }; }, componentWillMount: function() { // stash the MatrixClient in case we log out before we are unmounted this._matrixClient = this.props.matrixClient; + this.dispatcherRef = dis.register(this.onAction); CallMediaHandler.loadDevices(); @@ -98,6 +101,7 @@ const LoggedInView = React.createClass({ }, componentWillUnmount: function() { + dis.unregister(this.dispatcherRef); document.removeEventListener('keydown', this._onKeyDown); this._matrixClient.removeListener("accountData", this.onAccountData); if (this._sessionStoreToken) { @@ -128,6 +132,21 @@ const LoggedInView = React.createClass({ }); }, + onAction(payload) { + switch(payload.action) { + case 'set_interface_scale': + this.setState({ + interfaceScale: payload.value, + }); + break; + case 'set_chat_font_scale': + this.setState({ + chatFontScale: payload.value, + }); + break + } + }, + onAccountData: function(event) { if (event.getType() === "im.vector.web.settings") { this.setState({ @@ -239,6 +258,7 @@ const LoggedInView = React.createClass({ key={this.props.currentRoomId || 'roomview'} disabled={this.props.middleDisabled} collapsedRhs={this.props.collapseRhs} + chatFontScale={this.state.chatFontScale} ConferenceHandler={this.props.ConferenceHandler} />; if (!this.props.collapseRhs) { @@ -326,9 +346,9 @@ const LoggedInView = React.createClass({ if (this.state.useCompactLayout) { bodyClasses += ' mx_MatrixChat_useCompactLayout'; } - + return ( -
+
{ topBar }
{ SettingsStore.isFeatureEnabled("feature_tag_panel") ? :
} diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index e240ab38d5e..045c01036ac 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -84,6 +84,8 @@ module.exports = React.createClass({ // is the RightPanel collapsed? collapsedRhs: React.PropTypes.bool, + + chatFontScale: React.PropTypes.number, }, getInitialState: function() { @@ -1737,7 +1739,7 @@ module.exports = React.createClass({ onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null} /> { auxPanel } -
+
{ topUnreadMessagesBar } { messagePanel } { searchResultsPanel } diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 087b96c5097..60864c55524 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -228,6 +228,8 @@ module.exports = React.createClass({ this.setState({ language: languageHandler.getCurrentLanguage(), + interfaceScale: SettingsStore.getValue("interfaceScale"), + chatFontScale: SettingsStore.getValue("chatFontScale"), }); this._sessionStore = sessionStore; @@ -631,6 +633,32 @@ module.exports = React.createClass({ } }, + onInterfaceScaleChange: function(newInterfaceScale) { + if (this.state.interfaceScale !== newInterfaceScale) { + this.setState({ + interfaceScale: newInterfaceScale, + }); + } + }, + + onInterfaceScaleCommitted: function(newInterfaceScale) { + SettingsStore.setValue("interfaceScale", null, SettingLevel.DEVICE, newInterfaceScale); + dis.dispatch({action: 'set_interface_scale', value: newInterfaceScale}); + }, + + onChatFontScaleChange: function(newChatFontScale) { + if (this.state.chatFontScale !== newChatFontScale) { + this.setState({ + chatFontScale: newChatFontScale, + }); + } + }, + + onChatFontScaleCommitted: function(newChatFontScale) { + SettingsStore.setValue("chatFontScale", null, SettingLevel.DEVICE, newChatFontScale); + dis.dispatch({action: 'set_chat_font_scale', value: newChatFontScale}); + }, + _renderLanguageSetting: function() { const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown'); return
@@ -642,6 +670,22 @@ module.exports = React.createClass({
; }, + _renderScaleSettings: function() { + const SettingsSlider = sdk.getComponent('views.elements.SettingsSlider'); + return
+ + + + +
; + }, + _renderUserInterfaceSettings: function() { // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render @@ -668,6 +712,7 @@ module.exports = React.createClass({ { this._renderLanguageSetting() } + { this._renderScaleSettings() }
); diff --git a/src/components/views/elements/SettingsSlider.js b/src/components/views/elements/SettingsSlider.js new file mode 100644 index 00000000000..1db69911118 --- /dev/null +++ b/src/components/views/elements/SettingsSlider.js @@ -0,0 +1,54 @@ +/* +Copyright 2017 Mohammad Nokhbatolfoghahai (monokh) + +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 PropTypes from 'prop-types'; + + +export default class SettingsSlider extends React.Component { + static propTypes = { + className: PropTypes.string, + onValueChange: PropTypes.func.isRequired, + onValueCommitted: PropTypes.func.isRequired, + value: PropTypes.number, + }; + + static defaultProps = { + className: 'mx_SettingsSlider', + }; + + constructor(props) { + super(props); + this._onValueChange = this._onValueChange.bind(this); + this._onMouseUp = this._onMouseUp.bind(this); + } + + _onValueChange(e) { + this.props.onValueChange(parseInt(e.target.value)); + } + + _onMouseUp(e) { + this.props.onValueCommitted(parseInt(e.target.value)); + } + + render() { + return
+ +
{ this.props.value }%
+
; + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f28322398c2..99ed80e7b9b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -820,6 +820,8 @@ "Unable to remove contact information": "Unable to remove contact information", "Refer a friend to Riot:": "Refer a friend to Riot:", "Interface Language": "Interface Language", + "Interface Scale": "Interface Scale", + "Chat Font Scale": "Chat Font Scale", "User Interface": "User Interface", "Autocomplete Delay (ms):": "Autocomplete Delay (ms):", "": "", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 3783a42ddcc..4d1f5186a39 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -186,6 +186,8 @@ "Incorrect username and/or password.": "Incorrect username and/or password.", "Incorrect verification code": "Incorrect verification code", "Interface Language": "Interface Language", + "Interface Scale": "Interface Scale", + "Chat Font Scale": "Chat Font Scale", "Invalid alias format": "Invalid alias format", "Invalid address format": "Invalid address format", "Invalid Email Address": "Invalid Email Address", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 07de17ccfdc..48b34740613 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -203,6 +203,14 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en", }, + "interfaceScale": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, + default: 100, + }, + "chatFontScale": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, + default: 100, + }, "analyticsOptOut": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, displayName: _td('Opt out of analytics'),