diff --git a/package.json b/package.json index 8e1a1fa668..ffd701a233 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "karma-summary-reporter": "^1.5.1", "karma-webpack": "^4.0.0-beta.0", "matrix-mock-request": "^1.2.3", - "matrix-react-test-utils": "^0.1.1", + "matrix-react-test-utils": "^0.2.2", "mocha": "^5.0.5", "react-addons-test-utils": "^15.4.0", "require-json": "0.0.1", diff --git a/src/Modal.js b/src/Modal.js index 96be445ab1..26c9da8bbb 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -274,7 +274,7 @@ class ModalManager { this._reRender(); return { close: closeDialog, - then: (resolve, reject) => onFinishedProm.then(resolve, reject), + finished: onFinishedProm, }; } @@ -285,7 +285,7 @@ class ModalManager { this._reRender(); return { close: closeDialog, - then: (resolve, reject) => onFinishedProm.then(resolve, reject), + finished: onFinishedProm, }; } diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index deef8488f4..b8903076c7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -935,7 +935,7 @@ export default React.createClass({ const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog'); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog); - const [shouldCreate, name, noFederate] = await modal; + const [shouldCreate, name, noFederate] = await modal.finished; if (shouldCreate) { const createOpts = {}; if (name) createOpts.name = name; diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index b5af58adf1..ff8d88f738 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -15,13 +15,13 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import SdkConfig from "../../../SdkConfig"; import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; import * as ServerType from '../../views/auth/ServerTypeSelector'; +import ServerConfig from "./ServerConfig"; const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication'; @@ -33,49 +33,8 @@ const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_ * This is a variant of ServerConfig with only the HS field and different body * text that is specific to the Modular case. */ -export default class ModularServerConfig extends React.PureComponent { - static propTypes = { - onServerConfigChange: PropTypes.func, - - // The current configuration that the user is expecting to change. - serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, - - delayTimeMs: PropTypes.number, // time to wait before invoking onChanged - - // Called after the component calls onServerConfigChange - onAfterSubmit: PropTypes.func, - - // Optional text for the submit button. If falsey, no button will be shown. - submitText: PropTypes.string, - - // Optional class for the submit button. Only applies if the submit button - // is to be rendered. - submitClass: PropTypes.string, - }; - - static defaultProps = { - onServerConfigChange: function() {}, - customHsUrl: "", - delayTimeMs: 0, - }; - - constructor(props) { - super(props); - - this.state = { - busy: false, - errorText: "", - hsUrl: props.serverConfig.hsUrl, - isUrl: props.serverConfig.isUrl, - }; - } - - componentWillReceiveProps(newProps) { - if (newProps.serverConfig.hsUrl === this.state.hsUrl && - newProps.serverConfig.isUrl === this.state.isUrl) return; - - this.validateAndApplyServer(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); - } +export default class ModularServerConfig extends ServerConfig { + static propTypes = ServerConfig.propTypes; async validateAndApplyServer(hsUrl, isUrl) { // Always try and use the defaults first @@ -120,35 +79,6 @@ export default class ModularServerConfig extends React.PureComponent { return this.validateAndApplyServer(this.state.hsUrl, ServerType.TYPES.PREMIUM.identityServerUrl); } - onHomeserverBlur = (ev) => { - this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => { - this.validateServer(); - }); - }; - - onHomeserverChange = (ev) => { - const hsUrl = ev.target.value; - this.setState({ hsUrl }); - }; - - onSubmit = async (ev) => { - ev.preventDefault(); - ev.stopPropagation(); - const result = await this.validateServer(); - if (!result) return; // Do not continue. - - if (this.props.onAfterSubmit) { - this.props.onAfterSubmit(); - } - }; - - _waitThenInvoke(existingTimeoutId, fn) { - if (existingTimeoutId) { - clearTimeout(existingTimeoutId); - } - return setTimeout(fn.bind(this), this.props.delayTimeMs); - } - render() { const Field = sdk.getComponent('elements.Field'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index cd3dab12ac..f3b9640e16 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -2,6 +2,7 @@ Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd Copyright 2018, 2019 New Vector Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -69,10 +70,10 @@ module.exports = React.createClass({ fieldValid: {}, // The ISO2 country code selected in the phone number entry phoneCountry: this.props.defaultPhoneCountry, - username: "", - email: "", - phoneNumber: "", - password: "", + username: this.props.defaultUsername || "", + email: this.props.defaultEmail || "", + phoneNumber: this.props.defaultPhoneNumber || "", + password: this.props.defaultPassword || "", passwordConfirm: "", passwordComplexity: null, passwordSafe: false, @@ -90,7 +91,7 @@ module.exports = React.createClass({ } const self = this; - if (this.state.email == '') { + if (this.state.email === '') { const haveIs = Boolean(this.props.serverConfig.isUrl); let desc; @@ -455,7 +456,6 @@ module.exports = React.createClass({ ref={field => this[FIELD_EMAIL] = field} type="text" label={emailPlaceholder} - defaultValue={this.props.defaultEmail} value={this.state.email} onChange={this.onEmailChange} onValidate={this.onEmailValidate} @@ -469,7 +469,6 @@ module.exports = React.createClass({ ref={field => this[FIELD_PASSWORD] = field} type="password" label={_t("Password")} - defaultValue={this.props.defaultPassword} value={this.state.password} onChange={this.onPasswordChange} onValidate={this.onPasswordValidate} @@ -483,7 +482,6 @@ module.exports = React.createClass({ ref={field => this[FIELD_PASSWORD_CONFIRM] = field} type="password" label={_t("Confirm")} - defaultValue={this.props.defaultPassword} value={this.state.passwordConfirm} onChange={this.onPasswordConfirmChange} onValidate={this.onPasswordConfirmValidate} @@ -512,7 +510,6 @@ module.exports = React.createClass({ ref={field => this[FIELD_PHONE_NUMBER] = field} type="text" label={phoneLabel} - defaultValue={this.props.defaultPhoneNumber} value={this.state.phoneNumber} prefix={phoneCountry} onChange={this.onPhoneNumberChange} @@ -528,7 +525,6 @@ module.exports = React.createClass({ type="text" autoFocus={true} label={_t("Username")} - defaultValue={this.props.defaultUsername} value={this.state.username} onChange={this.onUsernameChange} onValidate={this.onUsernameValidate} diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 80f5c43d0c..afc6faa18d 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2018 New Vector Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +20,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { MatrixClient } from 'matrix-js-sdk'; import AvatarLogic from '../../../Avatar'; -import sdk from '../../../index'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; @@ -121,6 +121,10 @@ module.exports = React.createClass({ ); urls.push(defaultImageUrl); // lowest priority } + + // deduplicate URLs + urls = Array.from(new Set(urls)); + return { imageUrls: urls, defaultImageUrl: defaultImageUrl, diff --git a/src/components/views/messages/RoomAvatarEvent.js b/src/components/views/messages/RoomAvatarEvent.js index d035fc9237..207a385b92 100644 --- a/src/components/views/messages/RoomAvatarEvent.js +++ b/src/components/views/messages/RoomAvatarEvent.js @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +18,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import { ContentRepo } from 'matrix-js-sdk'; import { _t } from '../../../languageHandler'; import sdk from '../../../index'; import Modal from '../../../Modal'; @@ -31,12 +31,21 @@ module.exports = React.createClass({ mxEvent: PropTypes.object.isRequired, }, - onAvatarClick: function(name) { - const httpUrl = MatrixClientPeg.get().mxcUrlToHttp(this.props.mxEvent.getContent().url); + onAvatarClick: function() { + const cli = MatrixClientPeg.get(); + const ev = this.props.mxEvent; + const httpUrl = cli.mxcUrlToHttp(ev.getContent().url); + + const room = cli.getRoom(this.props.mxEvent.getRoomId()); + const text = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', { + senderDisplayName: ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(), + roomName: room ? room.name : '', + }); + const ImageView = sdk.getComponent("elements.ImageView"); const params = { src: httpUrl, - name: name, + name: text, }; Modal.createDialog(ImageView, params, "mx_Dialog_lightbox"); }, @@ -44,29 +53,22 @@ module.exports = React.createClass({ render: function() { const ev = this.props.mxEvent; const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); - - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); - const name = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', { - senderDisplayName: senderDisplayName, - roomName: room ? room.name : '', - }); + const RoomAvatar = sdk.getComponent("avatars.RoomAvatar"); if (!ev.getContent().url || ev.getContent().url.trim().length === 0) { return (
- { _t('%(senderDisplayName)s removed the room avatar.', {senderDisplayName: senderDisplayName}) } + { _t('%(senderDisplayName)s removed the room avatar.', {senderDisplayName}) }
); } - const url = ContentRepo.getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - ev.getContent().url, - Math.ceil(14 * window.devicePixelRatio), - Math.ceil(14 * window.devicePixelRatio), - 'crop', - ); + const room = MatrixClientPeg.get().getRoom(ev.getRoomId()); + // Provide all arguments to RoomAvatar via oobData because the avatar is historic + const oobData = { + avatarUrl: ev.getContent().url, + name: room ? room.name : "", + }; return (
@@ -75,8 +77,8 @@ module.exports = React.createClass({ { 'img': () => - + onClick={this.onAvatarClick}> + , }) } diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 4c0ebef3f3..7e0d9f686f 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -1,6 +1,7 @@ /* Copyright 2019 New Vector Ltd Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,6 +27,7 @@ import LanguageDropdown from "../../../elements/LanguageDropdown"; import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PropTypes from "prop-types"; +import {THEMES} from "../../../../../themes"; import PlatformPeg from "../../../../../PlatformPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; import sdk from "../../../../.."; @@ -160,8 +162,9 @@ export default class GeneralUserSettingsTab extends React.Component { {_t("Theme")} - - + {Object.entries(THEMES).map(([theme, text]) => { + return ; + })}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 61d9fbc49e..154871a977 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -251,6 +251,8 @@ "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s", "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s", "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s", + "Light theme": "Light theme", + "Dark theme": "Dark theme", "%(displayName)s is typing …": "%(displayName)s is typing …", "%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …", "%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …", @@ -557,8 +559,6 @@ "Set a new account password...": "Set a new account password...", "Language and region": "Language and region", "Theme": "Theme", - "Light theme": "Light theme", - "Dark theme": "Dark theme", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", "Deactivate Account": "Deactivate Account", diff --git a/src/settings/controllers/ThemeController.js b/src/settings/controllers/ThemeController.js index 615fc4c192..da20521873 100644 --- a/src/settings/controllers/ThemeController.js +++ b/src/settings/controllers/ThemeController.js @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,17 +16,13 @@ limitations under the License. */ import SettingController from "./SettingController"; - -const SUPPORTED_THEMES = [ - "light", - "dark", -]; +import {DEFAULT_THEME, THEMES} from "../../themes"; export default class ThemeController extends SettingController { getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { // Override in case some no longer supported theme is stored here - if (!SUPPORTED_THEMES.includes(calculatedValue)) { - return "light"; + if (!THEMES[calculatedValue]) { + return DEFAULT_THEME; } return null; // no override diff --git a/src/themes.js b/src/themes.js new file mode 100644 index 0000000000..1896333844 --- /dev/null +++ b/src/themes.js @@ -0,0 +1,24 @@ +/* +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> + +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 {_td} from "./languageHandler"; + +export const DEFAULT_THEME = "light"; + +export const THEMES = { + "light": _td("Light theme"), + "dark": _td("Dark theme"), +}; diff --git a/yarn.lock b/yarn.lock index f6ae81d6e9..b9341b2a0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5061,13 +5061,10 @@ matrix-mock-request@^1.2.3: bluebird "^3.5.0" expect "^1.20.2" -matrix-react-test-utils@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.1.1.tgz#b548844d0ebe338ea1b9c8f16474c30d17c3bdf4" - integrity sha1-tUiETQ6+M46hucjxZHTDDRfDvfQ= - dependencies: - react "^15.6.1" - react-dom "^15.6.1" +matrix-react-test-utils@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853" + integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ== md5.js@^1.3.4: version "1.3.5" @@ -6373,7 +6370,7 @@ react-beautiful-dnd@^4.0.1: redux-thunk "^2.2.0" reselect "^3.0.1" -react-dom@^15.6.0, react-dom@^15.6.1: +react-dom@^15.6.0: version "15.6.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730" integrity sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA= @@ -6436,7 +6433,7 @@ react-redux@^5.0.6: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react@^15.6.0, react@^15.6.1: +react@^15.6.0: version "15.6.2" resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=