diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index ffc3b21181..db12611ade 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -14,9 +14,7 @@ src/components/views/elements/AddressSelector.js src/components/views/elements/DirectorySearchBox.js src/components/views/elements/MemberEventListSummary.js src/components/views/elements/UserSelector.js -src/components/views/globals/MatrixToolbar.js src/components/views/globals/NewVersionBar.js -src/components/views/globals/UpdateCheckBar.js src/components/views/messages/MFileBody.js src/components/views/messages/TextualBody.js src/components/views/room_settings/ColorSettings.js diff --git a/code_style.md b/code_style.md index 01c1f37146..fe04d2cc3d 100644 --- a/code_style.md +++ b/code_style.md @@ -4,7 +4,7 @@ Matrix JavaScript/ECMAScript Style Guide The intention of this guide is to make Matrix's JavaScript codebase clean, consistent with other popular JavaScript styles and consistent with the rest of the Matrix codebase. For reference, the Matrix Python style guide can be found -at https://github.com/matrix-org/synapse/blob/master/docs/code_style.rst +at https://github.com/matrix-org/synapse/blob/master/docs/code_style.md This document reflects how we would like Matrix JavaScript code to look, with acknowledgement that a significant amount of code is written to older @@ -17,7 +17,7 @@ writing in modern ECMAScript and using a transpile step to generate the file that applications can then include. There are significant benefits in being able to use modern ECMAScript, although the tooling for doing so can be awkward for library code, especially with regard to translating source maps and line -number throgh from the original code to the final application. +number through from the original code to the final application. General Style ------------- diff --git a/res/css/_common.scss b/res/css/_common.scss index 03442ca510..ebeeb381e6 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -335,6 +335,9 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { .mx_Dialog_header.mx_Dialog_headerWithButton > .mx_Dialog_title { text-align: center; } +.mx_Dialog_header.mx_Dialog_headerWithCancel > .mx_Dialog_title { + margin-right: 20px; // leave space for the 'X' cancel button +} .mx_Dialog_title.danger { color: $warning-color; diff --git a/res/css/_components.scss b/res/css/_components.scss index 44c63b9df7..eefe0f009b 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -116,6 +116,7 @@ @import "./views/elements/_RoomAliasField.scss"; @import "./views/elements/_Slider.scss"; @import "./views/elements/_Spinner.scss"; +@import "./views/elements/_StyledCheckbox.scss"; @import "./views/elements/_SyntaxHighlight.scss"; @import "./views/elements/_TextWithTooltip.scss"; @import "./views/elements/_ToggleSwitch.scss"; @@ -123,7 +124,6 @@ @import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; @import "./views/emojipicker/_EmojiPicker.scss"; -@import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @import "./views/groups/_GroupUserSettings.scss"; @@ -202,6 +202,7 @@ @import "./views/settings/_ProfileSettings.scss"; @import "./views/settings/_SetIdServer.scss"; @import "./views/settings/_SetIntegrationManager.scss"; +@import "./views/settings/_UpdateCheckButton.scss"; @import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index 76a9b16425..2d7ab67e40 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -15,6 +15,7 @@ limitations under the License. */ $font-1px: 0.067rem; +$font-1-5px: 0.100rem; $font-2px: 0.133rem; $font-3px: 0.200rem; $font-4px: 0.267rem; diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index c5a5d50068..05c703ab6d 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -41,10 +41,6 @@ limitations under the License. height: 40px; } -.mx_MatrixChat_toolbarShowing { - height: auto; -} - .mx_MatrixChat { width: 100%; height: 100%; diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index 44205b1f01..561ab1446f 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -63,6 +63,10 @@ limitations under the License. padding-left: 32px; padding-top: 8px; position: relative; + + a { + display: flex; + } } .mx_NotificationPanel .mx_EventTile_roomName a, diff --git a/res/css/views/dialogs/_GroupAddressPicker.scss b/res/css/views/dialogs/_GroupAddressPicker.scss index 20a7cc1047..5fa18931f0 100644 --- a/res/css/views/dialogs/_GroupAddressPicker.scss +++ b/res/css/views/dialogs/_GroupAddressPicker.scss @@ -18,8 +18,3 @@ limitations under the License. margin-top: 10px; display: flex; } - -.mx_GroupAddressPicker_checkboxContainer input[type="checkbox"] { - /* Stop flex from shrinking the checkbox */ - width: 20px; -} diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index e08469ec6d..e3d2ae8306 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -55,6 +55,7 @@ limitations under the License. margin-left: 5px; width: 20px; height: 20px; + background-repeat: none; } .mx_ShareDialog_split { diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss new file mode 100644 index 0000000000..14081f1e99 --- /dev/null +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -0,0 +1,66 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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. +*/ + + +.mx_Checkbox { + $size: $font-16px; + $border-size: $font-1-5px; + $border-radius: $font-4px; + + display: flex; + align-items: flex-start; + + input[type=checkbox] { + display: none; + + & + label { + display: flex; + align-items: center; + + flex-grow: 1; + } + + & + label > .mx_Checkbox_background { + display: inline-flex; + position: relative; + + flex-shrink: 0; + + height: $size; + width: $size; + size: 0.5rem; + + border: $border-size solid rgba($muted-fg-color, 0.5); + box-sizing: border-box; + border-radius: $border-radius; + + img { + height: 100%; + width: 100%; + filter: invert(100%); + } + } + + &:checked + label > .mx_Checkbox_background { + background: $accent-color; + border-color: $accent-color; + } + + & + label > *:not(.mx_Checkbox_background) { + margin-left: 10px; + } + } +} diff --git a/res/css/views/globals/_MatrixToolbar.scss b/res/css/views/globals/_MatrixToolbar.scss deleted file mode 100644 index 5fdf572f99..0000000000 --- a/res/css/views/globals/_MatrixToolbar.scss +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -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. -*/ - -.mx_MatrixToolbar { - background-color: $accent-color; - color: $accent-fg-color; - - display: flex; - align-items: center; -} - -.mx_MatrixToolbar_warning { - margin-left: 16px; - margin-right: 8px; - margin-top: -2px; -} - -.mx_MatrixToolbar_info { - padding-left: 16px; - padding-right: 8px; - background-color: $info-bg-color; -} - -.mx_MatrixToolbar_error { - padding-left: 16px; - padding-right: 8px; - background-color: $warning-bg-color; -} - -.mx_MatrixToolbar_content { - flex: 1; -} - -.mx_MatrixToolbar_link { - color: $accent-fg-color !important; - text-decoration: underline !important; - cursor: pointer; -} - -.mx_MatrixToolbar_clickable { - cursor: pointer; -} - -.mx_MatrixToolbar_close { - cursor: pointer; -} - -.mx_MatrixToolbar_close img { - display: block; - float: right; - margin-right: 10px; -} - -.mx_MatrixToolbar_action { - margin-right: 16px; -} - -.mx_MatrixToolbar_changelog { - white-space: pre; -} diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index e4743f189e..1b1bab67bc 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -96,10 +96,6 @@ $AppsDrawerBodyHeight: 273px; height: $AppsDrawerBodyHeight; } -.mx_AppTile_persistedWrapper > div { - height: 100%; -} - .mx_AppTile_mini .mx_AppTile_persistedWrapper { height: 114px; } diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 928ea75a79..af12cce5d6 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -53,79 +53,79 @@ $left-gutter: 65px; /* Compact layout overrides */ .mx_MatrixChat_useCompactLayout { - .mx_EventTile { - padding-top: 4px; - } - - .mx_EventTile.mx_EventTile_info { - // same as the padding for non-compact .mx_EventTile.mx_EventTile_info - padding-top: 0px; - font-size: $font-13px; - .mx_EventTile_line, .mx_EventTile_reply { - line-height: $font-20px; - } - .mx_EventTile_avatar { - top: 4px; - } - } - - .mx_EventTile .mx_SenderProfile { - font-size: $font-13px; - } - - .mx_EventTile.mx_EventTile_emote { - // add a bit more space for emotes so that avatars don't collide - padding-top: 8px; - .mx_EventTile_avatar { - top: 2px; - } - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 1px; - } - } - - .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { - padding-top: 0; - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - } - .mx_EventTile_line, .mx_EventTile_reply { padding-top: 0px; padding-bottom: 0px; } - .mx_EventTile_avatar { - top: 2px; - } + .mx_EventTile { + padding-top: 4px; - .mx_EventTile_e2eIcon { - top: 3px; - } + &.mx_EventTile_info { + // same as the padding for non-compact .mx_EventTile.mx_EventTile_info + padding-top: 0px; + font-size: $font-13px; + .mx_EventTile_line, .mx_EventTile_reply { + line-height: $font-20px; + } + .mx_EventTile_avatar { + top: 4px; + } + } - .mx_EventTile_readAvatars { - top: 27px; - } + .mx_SenderProfile { + font-size: $font-13px; + } - .mx_EventTile_continuation .mx_EventTile_readAvatars, - .mx_EventTile_emote .mx_EventTile_readAvatars { - top: 5px; - } + &.mx_EventTile_emote { + // add a bit more space for emotes so that avatars don't collide + padding-top: 8px; + .mx_EventTile_avatar { + top: 2px; + } + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 1px; + } + } - .mx_EventTile_info .mx_EventTile_readAvatars { - top: 4px; + &.mx_EventTile_emote.mx_EventTile_continuation { + padding-top: 0; + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + } + + .mx_EventTile_avatar { + top: 2px; + } + + .mx_EventTile_e2eIcon { + top: 3px; + } + + .mx_EventTile_readAvatars { + top: 27px; + } + + .mx_EventTile_continuation .mx_EventTile_readAvatars, + .mx_EventTile_emote .mx_EventTile_readAvatars { + top: 5px; + } + + .mx_EventTile_info .mx_EventTile_readAvatars { + top: 4px; + } + + .mx_EventTile_content .markdown-body { + p, ul, ol, dl, blockquote, pre, table { + margin-bottom: 4px; // 1/4 of the non-compact margin-bottom + } + } } .mx_RoomView_MessageList h2 { margin-top: 6px; } - - .mx_EventTile_content .markdown-body { - p, ul, ol, dl, blockquote, pre, table { - margin-bottom: 4px; // 1/4 of the non-compact margin-bottom - } - } } diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 7b223be3a4..c1cda7bf24 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -214,8 +214,12 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/video.svg'); } +.mx_MessageComposer_emoji::before { + mask-image: url('$(res)/img/feather-customised/emoji3.custom.svg'); +} + .mx_MessageComposer_stickers::before { - mask-image: url('$(res)/img/feather-customised/face.svg'); + mask-image: url('$(res)/img/feather-customised/sticker.custom.svg'); } .mx_MessageComposer_formatting { diff --git a/res/css/views/settings/_UpdateCheckButton.scss b/res/css/views/settings/_UpdateCheckButton.scss new file mode 100644 index 0000000000..f35a023ac1 --- /dev/null +++ b/res/css/views/settings/_UpdateCheckButton.scss @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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. +*/ + +.mx_UpdateCheckButton_summary { + margin-left: 16px; + + .mx_AccessibleButton_kind_link { + padding: 0; + } +} diff --git a/res/img/feather-customised/emoji3.custom.svg b/res/img/feather-customised/emoji3.custom.svg new file mode 100644 index 0000000000..d91ba1c132 --- /dev/null +++ b/res/img/feather-customised/emoji3.custom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-customised/sticker.custom.svg b/res/img/feather-customised/sticker.custom.svg new file mode 100644 index 0000000000..691e3b3925 --- /dev/null +++ b/res/img/feather-customised/sticker.custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/@types/common.ts b/src/@types/common.ts new file mode 100644 index 0000000000..26e5317aa3 --- /dev/null +++ b/src/@types/common.ts @@ -0,0 +1,19 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +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. +*/ + +// Based on https://stackoverflow.com/a/53229857/3532235 +export type Without = {[P in Exclude] ? : never} +export type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U; diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index d4a6c34daf..c5f58f7f0c 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -21,6 +21,19 @@ import {MatrixClient} from "matrix-js-sdk/src/client"; import dis from './dispatcher/dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; import {ActionPayload} from "./dispatcher/payloads"; +import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; +import {Action} from "./dispatcher/actions"; +import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; + +export enum UpdateCheckStatus { + Checking = "CHECKING", + Error = "ERROR", + NotAvailable = "NOTAVAILABLE", + Downloading = "DOWNLOADING", + Ready = "READY", +} + +const UPDATE_DEFER_KEY = "mx_defer_update"; /** * Base class for classes that provide platform-specific functionality @@ -56,6 +69,53 @@ export default abstract class BasePlatform { this.errorDidOccur = errorDidOccur; } + /** + * Whether we can call checkForUpdate on this platform build + */ + async canSelfUpdate(): Promise { + return false; + } + + startUpdateCheck() { + hideUpdateToast(); + localStorage.removeItem(UPDATE_DEFER_KEY); + dis.dispatch({ + action: Action.CheckUpdates, + status: UpdateCheckStatus.Checking, + }); + } + + /** + * Update the currently running app to the latest available version + * and replace this instance of the app with the new version. + */ + installUpdate() { + } + + /** + * Check if the version update has been deferred and that deferment is still in effect + * @param newVersion the version string to check + */ + protected shouldShowUpdate(newVersion: string): boolean { + try { + const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)); + return newVersion !== version || Date.now() > deferUntil; + } catch (e) { + return true; + } + } + + /** + * Ignore the pending update and don't prompt about this version + * until the next morning (8am). + */ + deferUpdate(newVersion: string) { + const date = new Date(Date.now() + 24 * 60 * 60 * 1000); + date.setHours(8, 0, 0, 0); // set to next 8am + localStorage.setItem(UPDATE_DEFER_KEY, JSON.stringify([newVersion, date.getTime()])); + hideUpdateToast(); + } + /** * Returns true if the platform supports displaying * notifications, otherwise false. @@ -180,4 +240,35 @@ export default abstract class BasePlatform { onKeyDown(ev: KeyboardEvent): boolean { return false; // no shortcuts implemented } + + /** + * Get a previously stored pickle key. The pickle key is used for + * encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the previously stored pickle key, or null if no + * pickle key has been stored. + */ + async getPickleKey(userId: string, deviceId: string): Promise { + return null; + } + + /** + * Create and store a pickle key for encrypting libolm objects. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + * @returns {string|null} the pickle key, or null if the platform does not + * support storing pickle keys. + */ + async createPickleKey(userId: string, deviceId: string): Promise { + return null; + } + + /** + * Delete a previously stored pickle key from storage. + * @param {string} userId the user ID for the user that the pickle key is for. + * @param {string} userId the device ID that the pickle key is for. + */ + async destroyPickleKey(userId: string, deviceId: string): Promise { + } } diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index bf9435914c..249ad8381c 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -27,6 +27,7 @@ import Modal from './Modal'; import RoomViewStore from './stores/RoomViewStore'; import encrypt from "browser-encrypt-attachment"; import extractPngChunks from "png-chunks-extract"; +import Spinner from "./components/views/elements/Spinner"; // Polyfill for Canvas.toBlob API using Canvas.toDataURL import "blueimp-canvas-to-blob"; @@ -399,7 +400,11 @@ export default class ContentMessages { if (!shouldUpload) return; } - await this.ensureMediaConfigFetched(); + if (!this.mediaConfig) { // hot-path optimization to not flash a spinner if we don't need to + const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); + await this.ensureMediaConfigFetched(); + modal.close(); + } const tooBigFiles = []; const okFiles = []; diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index 2928137f9d..e7ae3217bb 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -22,6 +22,7 @@ import { _t } from './languageHandler'; import {MatrixClientPeg} from './MatrixClientPeg'; import GroupStore from './stores/GroupStore'; import {allSettled} from "./utils/promise"; +import StyledCheckbox from './components/views/elements/StyledCheckbox'; export function showGroupInviteDialog(groupId) { return new Promise((resolve, reject) => { @@ -61,12 +62,12 @@ export function showGroupAddRoomDialog(groupId) {
{ _t("Which rooms would you like to add to this community?") }
; - const checkboxContainer = ; + const checkboxContainer = + { _t("Show these rooms to non-members on the community page and room list?") } + ; const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog"); Modal.createTrackedDialog('Add Rooms to Group', '', AddressPickerDialog, { diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 22c5d48317..0494628472 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -298,6 +298,8 @@ async function _restoreFromLocalStorage(opts) { return false; } + const pickleKey = await PlatformPeg.get().getPickleKey(userId, deviceId); + console.log(`Restoring session for ${userId}`); await _doSetLoggedIn({ userId: userId, @@ -306,6 +308,7 @@ async function _restoreFromLocalStorage(opts) { homeserverUrl: hsUrl, identityServerUrl: isUrl, guest: isGuest, + pickleKey: pickleKey, }, false); return true; } else { @@ -348,9 +351,13 @@ async function _handleLoadSessionFailure(e) { * * @returns {Promise} promise which resolves to the new MatrixClient once it has been started */ -export function setLoggedIn(credentials) { +export async function setLoggedIn(credentials) { stopMatrixClient(); - return _doSetLoggedIn(credentials, true); + const pickleKey = credentials.userId && credentials.deviceId + ? await PlatformPeg.get().createPickleKey(credentials.userId, credentials.deviceId) + : null; + + return _doSetLoggedIn(Object.assign({}, credentials, {pickleKey}), true); } /** @@ -516,7 +523,9 @@ export function logout() { } _isLoggingOut = true; - MatrixClientPeg.get().logout().then(onLoggedOut, + const client = MatrixClientPeg.get(); + PlatformPeg.get().destroyPickleKey(client.getUserId(), client.getDeviceId()); + client.logout().then(onLoggedOut, (err) => { // Just throwing an error here is going to be very unhelpful // if you're trying to log out because your server's down and @@ -575,10 +584,12 @@ async function startMatrixClient(startSyncing=true) { // to work). dis.dispatch({action: 'will_start_client'}, true); + // reset things first just in case + TypingStore.sharedInstance().reset(); + ToastStore.sharedInstance().reset(); + Notifier.start(); UserActivity.sharedInstance().start(); - TypingStore.sharedInstance().reset(); // just in case - ToastStore.sharedInstance().reset(); DMRoomMap.makeShared().start(); IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.start(); diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 891a5fb9c6..c6ee6c546f 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -41,6 +41,7 @@ export interface IMatrixClientCreds { deviceId: string, accessToken: string, guest: boolean, + pickleKey?: string, } // TODO: Move this to the js-sdk @@ -251,6 +252,7 @@ class _MatrixClientPeg implements IMatrixClientPeg { accessToken: creds.accessToken, userId: creds.userId, deviceId: creds.deviceId, + pickleKey: creds.pickleKey, timelineSupport: true, forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'), diff --git a/src/Notifier.js b/src/Notifier.js index 2ffa92452b..cd328ba565 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -26,6 +26,9 @@ import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import { + hideToast as hideNotificationsToast, +} from "./toasts/DesktopNotificationsToast"; /* * Dispatches: @@ -278,12 +281,7 @@ const Notifier = { Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden); - // XXX: why are we dispatching this here? - // this is nothing to do with notifier_enabled - dis.dispatch({ - action: "notifier_enabled", - value: this.isEnabled(), - }); + hideNotificationsToast(); // update the info to localStorage for persistent settings if (persistent && global.localStorage) { diff --git a/src/PasswordReset.js b/src/PasswordReset.js index 320599f6d9..9472ddc633 100644 --- a/src/PasswordReset.js +++ b/src/PasswordReset.js @@ -84,8 +84,14 @@ export default class PasswordReset { try { await this.client.setPassword({ + // Note: Though this sounds like a login type for identity servers only, it + // has a dual purpose of being used for homeservers too. type: "m.login.email.identity", + // TODO: Remove `threepid_creds` once servers support proper UIA + // See https://github.com/matrix-org/synapse/issues/5665 + // See https://github.com/matrix-org/matrix-doc/issues/2220 threepid_creds: creds, + threepidCreds: creds, }, this.password); } catch (err) { if (err.httpStatus === 401) { diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index f5a8db4e04..5bb25b8f19 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -199,7 +199,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { type: 'm.id.user', user: MatrixClientPeg.get().getUserId(), }, - // https://github.com/matrix-org/synapse/issues/5665 + // TODO: Remove `user` once servers support proper UIA + // See https://github.com/matrix-org/synapse/issues/5665 user: MatrixClientPeg.get().getUserId(), password: this.state.accountPassword, }); diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index f502b4da4b..1ad38c6f04 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -43,6 +43,15 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import PlatformPeg from "../../PlatformPeg"; import { RoomListStoreTempProxy } from "../../stores/room-list/RoomListStoreTempProxy"; import { DefaultTagID } from "../../stores/room-list/models"; +import { + showToast as showSetPasswordToast, + hideToast as hideSetPasswordToast +} from "../../toasts/SetPasswordToast"; +import { + showToast as showServerLimitToast, + hideToast as hideServerLimitToast +} from "../../toasts/ServerLimitToast"; + // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. // NB. this is just for server notices rather than pinned messages in general. @@ -65,10 +74,6 @@ interface IProps { initialEventPixelOffset: number; leftDisabled: boolean; rightDisabled: boolean; - showCookieBar: boolean; - hasNewVersion: boolean; - userHasGeneratedPassword: boolean; - showNotifierToolbar: boolean; page_type: string; autoJoin: boolean; thirdPartyInvite?: object; @@ -76,7 +81,6 @@ interface IProps { currentRoomId: string; ConferenceHandler?: object; collapseLhs: boolean; - checkingForUpdate: boolean; config: { piwik: { policyUrl: string; @@ -86,10 +90,8 @@ interface IProps { currentUserId?: string; currentGroupId?: string; currentGroupIsNew?: boolean; - version?: string; - newVersion?: string; - newVersionReleaseNotes?: string; } + interface IState { mouseDown?: { x: number; @@ -97,8 +99,6 @@ interface IState { }; syncErrorData: any; useCompactLayout: boolean; - serverNoticeEvents: MatrixEvent[]; - userHasGeneratedPassword: boolean; } /** @@ -141,11 +141,8 @@ class LoggedInView extends React.PureComponent { this.state = { mouseDown: undefined, syncErrorData: undefined, - userHasGeneratedPassword: false, // use compact timeline view useCompactLayout: SettingsStore.getValue('useCompactLayout'), - // any currently active server notice events - serverNoticeEvents: [], }; // stash the MatrixClient in case we log out before we are unmounted @@ -179,18 +176,6 @@ class LoggedInView extends React.PureComponent { this._loadResizerPreferences(); } - componentDidUpdate(prevProps, prevState) { - // attempt to guess when a banner was opened or closed - if ( - (prevProps.showCookieBar !== this.props.showCookieBar) || - (prevProps.hasNewVersion !== this.props.hasNewVersion) || - (prevState.userHasGeneratedPassword !== this.state.userHasGeneratedPassword) || - (prevProps.showNotifierToolbar !== this.props.showNotifierToolbar) - ) { - this.props.resizeNotifier.notifyBannersChanged(); - } - } - componentWillUnmount() { document.removeEventListener('keydown', this._onNativeKeyDown, false); this._matrixClient.removeListener("accountData", this.onAccountData); @@ -220,9 +205,11 @@ class LoggedInView extends React.PureComponent { }; _setStateFromSessionStore = () => { - this.setState({ - userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()), - }); + if (this._sessionStore.getCachedPassword()) { + showSetPasswordToast(); + } else { + hideSetPasswordToast(); + } }; _createResizer() { @@ -294,6 +281,8 @@ class LoggedInView extends React.PureComponent { if (oldSyncState === 'PREPARED' && syncState === 'SYNCING') { this._updateServerNoticeEvents(); + } else { + this._calculateServerLimitToast(data); } }; @@ -304,11 +293,24 @@ class LoggedInView extends React.PureComponent { } }; + _calculateServerLimitToast(syncErrorData, usageLimitEventContent?) { + const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED"; + if (error) { + usageLimitEventContent = syncErrorData.error.data; + } + + if (usageLimitEventContent) { + showServerLimitToast(usageLimitEventContent.limit_type, usageLimitEventContent.admin_contact, error); + } else { + hideServerLimitToast(); + } + } + _updateServerNoticeEvents = async () => { const roomLists = RoomListStoreTempProxy.getRoomLists(); if (!roomLists[DefaultTagID.ServerNotice]) return []; - const pinnedEvents = []; + const events = []; for (const room of roomLists[DefaultTagID.ServerNotice]) { const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", ""); @@ -318,12 +320,18 @@ class LoggedInView extends React.PureComponent { for (const eventId of pinnedEventIds) { const timeline = await this._matrixClient.getEventTimeline(room.getUnfilteredTimelineSet(), eventId, 0); const event = timeline.getEvents().find(ev => ev.getId() === eventId); - if (event) pinnedEvents.push(event); + if (event) events.push(event); } } - this.setState({ - serverNoticeEvents: pinnedEvents, + + const usageLimitEvent = events.find((e) => { + return ( + e && e.getType() === 'm.room.message' && + e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' + ); }); + + this._calculateServerLimitToast(this.state.syncErrorData, usageLimitEvent && usageLimitEvent.getContent()); }; _onPaste = (ev) => { @@ -599,12 +607,6 @@ class LoggedInView extends React.PureComponent { const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); const ToastContainer = sdk.getComponent('structures.ToastContainer'); - const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); - const CookieBar = sdk.getComponent('globals.CookieBar'); - const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); - const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); - const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); - const ServerLimitBar = sdk.getComponent('globals.ServerLimitBar'); let pageElement; @@ -648,46 +650,7 @@ class LoggedInView extends React.PureComponent { break; } - const usageLimitEvent = this.state.serverNoticeEvents.find((e) => { - return ( - e && e.getType() === 'm.room.message' && - e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' - ); - }); - - let topBar; - if (this.state.syncErrorData && this.state.syncErrorData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { - topBar = ; - } else if (usageLimitEvent) { - topBar = ; - } else if (this.props.showCookieBar && - this.props.config.piwik && - navigator.doNotTrack !== "1" - ) { - const policyUrl = this.props.config.piwik.policyUrl || null; - topBar = ; - } else if (this.props.hasNewVersion) { - topBar = ; - } else if (this.props.checkingForUpdate) { - topBar = ; - } else if (this.state.userHasGeneratedPassword) { - topBar = ; - } else if (this.props.showNotifierToolbar) { - topBar = ; - } - let bodyClasses = 'mx_MatrixChat'; - if (topBar) { - bodyClasses += ' mx_MatrixChat_toolbarShowing'; - } if (this.state.useCompactLayout) { bodyClasses += ' mx_MatrixChat_useCompactLayout'; } @@ -702,7 +665,6 @@ class LoggedInView extends React.PureComponent { onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp} > - { topBar }
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 1200fcaf7a..058a7ba50b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -58,8 +58,8 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils"; import DMRoomMap from '../../utils/DMRoomMap'; import { countRoomsWithNotif } from '../../RoomNotifs'; -import { ThemeWatcher } from "../../theme"; -import { FontWatcher } from '../../FontWatcher'; +import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; +import { FontWatcher } from '../../settings/watchers/FontWatcher'; import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer, IDeferred } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; @@ -67,6 +67,11 @@ import * as StorageManager from "../../utils/StorageManager"; import type LoggedInViewType from "./LoggedInView"; import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload"; import { Action } from "../../dispatcher/actions"; +import { + showToast as showAnalyticsToast, + hideToast as hideAnalyticsToast +} from "../../toasts/AnalyticsToast"; +import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; /** constants for MatrixChat.state.view */ export enum Views { @@ -168,12 +173,6 @@ interface IState { leftDisabled: boolean; middleDisabled: boolean; // the right panel's disabled state is tracked in its store. - version?: string; - newVersion?: string; - hasNewVersion: boolean; - newVersionReleaseNotes?: string; - checkingForUpdate?: string; // updateCheckStatusEnum - showCookieBar: boolean; // Parameters used in the registration dance with the IS register_client_secret?: string; register_session_id?: string; @@ -183,7 +182,6 @@ interface IState { hideToSRUsers: boolean; syncError?: Error; resizeNotifier: ResizeNotifier; - showNotifierToolbar: boolean; serverConfig?: ValidatedServerConfig; ready: boolean; thirdPartyInvite?: object; @@ -227,17 +225,10 @@ export default class MatrixChat extends React.PureComponent { leftDisabled: false, middleDisabled: false, - hasNewVersion: false, - newVersionReleaseNotes: null, - checkingForUpdate: null, - - showCookieBar: false, - hideToSRUsers: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. resizeNotifier: new ResizeNotifier(), - showNotifierToolbar: false, ready: false, }; @@ -338,12 +329,6 @@ export default class MatrixChat extends React.PureComponent { }); } - if (SettingsStore.getValue("showCookieBar")) { - this.setState({ - showCookieBar: true, - }); - } - if (SettingsStore.getValue("analyticsOptIn")) { Analytics.enable(); } @@ -685,9 +670,6 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'view_my_groups'}); } break; - case 'notifier_enabled': - this.setState({showNotifierToolbar: Notifier.shouldShowToolbar()}); - break; case 'hide_left_panel': this.setState({ collapseLhs: true, @@ -735,15 +717,6 @@ export default class MatrixChat extends React.PureComponent { case 'client_started': this.onClientStarted(); break; - case 'new_version': - this.onVersion( - payload.currentVersion, payload.newVersion, - payload.releaseNotes, - ); - break; - case 'check_updates': - this.setState({ checkingForUpdate: payload.value }); - break; case 'send_event': this.onSendEvent(payload.room_id, payload.event); break; @@ -760,19 +733,13 @@ export default class MatrixChat extends React.PureComponent { case 'accept_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, true); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); Analytics.enable(); break; case 'reject_cookies': SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, false); SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false); - - this.setState({ - showCookieBar: false, - }); + hideAnalyticsToast(); break; } }; @@ -1261,6 +1228,10 @@ export default class MatrixChat extends React.PureComponent { } StorageManager.tryPersistStorage(); + + if (SettingsStore.getValue("showCookieBar") && this.props.config.piwik && navigator.doNotTrack !== "1") { + showAnalyticsToast(this.props.config.piwik && this.props.config.piwik.policyUrl); + } } private showScreenAfterLogin() { @@ -1388,10 +1359,13 @@ export default class MatrixChat extends React.PureComponent { this.firstSyncComplete = true; this.firstSyncPromise.resolve(); + if (Notifier.shouldShowToolbar()) { + showNotificationsToast(); + } + dis.dispatch({action: 'focus_composer'}); this.setState({ ready: true, - showNotifierToolbar: Notifier.shouldShowToolbar(), }); }); cli.on('Call.incoming', function(call) { @@ -1833,16 +1807,6 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("settings"); }; - onVersion(current: string, latest: string, releaseNotes?: string) { - this.setState({ - version: current, - newVersion: latest, - hasNewVersion: current !== latest, - newVersionReleaseNotes: releaseNotes, - checkingForUpdate: null, - }); - } - onSendEvent(roomId: string, event: MatrixEvent) { const cli = MatrixClientPeg.get(); if (!cli) { @@ -2037,7 +2001,6 @@ export default class MatrixChat extends React.PureComponent { onCloseAllSettings={this.onCloseAllSettings} onRegistered={this.onRegistered} currentRoomId={this.state.currentRoomId} - showCookieBar={this.state.showCookieBar} /> ); } else { diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 93e4668f66..2069255ea2 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -108,6 +108,9 @@ export default class MessagePanel extends React.Component { // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }; // Force props to be loaded for useIRCLayout @@ -119,7 +122,6 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -172,8 +174,6 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); - - this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); } componentDidMount() { @@ -183,7 +183,6 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); - SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -202,17 +201,6 @@ export default class MessagePanel extends React.Component { }); }; - onLayoutChange = () => { - this.setState({ - useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), - }); - } - - useIRCLayout(ircLayoutSelected) { - // if room is null we are not in a normal room list - return ircLayoutSelected && this.props.room; - } - /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -614,7 +602,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} - useIRCLayout={this.state.useIRCLayout} + useIRCLayout={this.props.useIRCLayout} /> , @@ -797,8 +785,6 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, - "mx_IRCLayout": this.state.useIRCLayout, - "mx_GroupLayout": !this.state.useIRCLayout, }, ); @@ -813,11 +799,11 @@ export default class MessagePanel extends React.Component { } let ircResizer = null; - if (this.state.useIRCLayout) { + if (this.props.useIRCLayout) { ircResizer = ; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 39cd497098..c87f4cc4dd 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -165,6 +165,8 @@ export default createReactClass({ canReact: false, canReply: false, + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), }; }, @@ -195,6 +197,8 @@ export default createReactClass({ this._roomView = createRef(); this._searchResultsPanel = createRef(); + + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); }, _onReadReceiptsChange: function() { @@ -535,6 +539,14 @@ export default createReactClass({ // no need to do this as Dir & Settings are now overlays. It just burnt CPU. // console.log("Tinter.tint from RoomView.unmount"); // Tinter.tint(); // reset colourscheme + + SettingsStore.unwatchSetting(this._layoutWatcherRef); + }, + + onLayoutChange: function() { + this.setState({ + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + }); }, _onRightPanelStoreUpdate: function() { @@ -1996,6 +2008,13 @@ export default createReactClass({ highlightedEventId = this.state.initialEventId; } + const messagePanelClassNames = classNames( + "mx_RoomView_messagePanel", + { + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, + }); + // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); const messagePanel = ( ); let topUnreadMessagesBar = null; diff --git a/src/components/structures/TagPanelButtons.js b/src/components/structures/TagPanelButtons.js deleted file mode 100644 index 4b00da3cbf..0000000000 --- a/src/components/structures/TagPanelButtons.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2019 New Vector Ltd. - -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 createReactClass from 'create-react-class'; -import * as sdk from '../../index'; -import dis from '../../dispatcher/dispatcher'; -import Modal from '../../Modal'; -import { _t } from '../../languageHandler'; - -const TagPanelButtons = createReactClass({ - displayName: 'TagPanelButtons', - - - componentDidMount: function() { - this._dispatcherRef = dis.register(this._onAction); - }, - - componentWillUnmount() { - if (this._dispatcherRef) { - dis.unregister(this._dispatcherRef); - this._dispatcherRef = null; - } - }, - - _onAction(payload) { - if (payload.action === "show_redesign_feedback_dialog") { - const RedesignFeedbackDialog = - sdk.getComponent("views.dialogs.RedesignFeedbackDialog"); - Modal.createTrackedDialog('Report bugs & give feedback', '', RedesignFeedbackDialog); - } - }, - - render() { - const GroupsButton = sdk.getComponent('elements.GroupsButton'); - const ActionButton = sdk.getComponent("elements.ActionButton"); - - return (
- - -
); - }, -}); -export default TagPanelButtons; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index da1369c45f..95dc42fcee 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -112,6 +112,9 @@ const TimelinePanel = createReactClass({ // whether to show reactions for an event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }, statics: { @@ -1447,6 +1450,7 @@ const TimelinePanel = createReactClass({ getRelationsForEvent={this.getRelationsForEvent} editState={this.state.editState} showReactions={this.props.showReactions} + useIRCLayout={this.props.useIRCLayout} /> ); }, diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 7be88f9d44..6349614d72 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -247,9 +247,8 @@ export default createReactClass({ // do SSO instead. If we've already started the UI Auth process though, we don't // need to. if (!this.state.doingUIAuth) { - await this._makeRegisterRequest({}); - // This should never succeed since we specified an empty - // auth object. + await this._makeRegisterRequest(null); + // This should never succeed since we specified no auth object. console.log("Expecting 401 from register request but got success!"); } } catch (e) { diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index 655452fcee..f6bc1b8ae7 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -355,6 +355,7 @@ export const TermsAuthEntry = createReactClass({ allChecked = allChecked && checked; checkboxes.push( + // XXX: replace with StyledCheckbox