From f324f676d37ae5d330beedc77268bbb954fe2cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:14:53 +0100 Subject: [PATCH 01/89] EventIndex: Add a method to get the current disk usage of the index. --- src/indexing/BaseEventIndexManager.js | 7 +++++++ src/indexing/EventIndex.js | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/indexing/BaseEventIndexManager.js b/src/indexing/BaseEventIndexManager.js index 5e8ca668ad..733dc05dd6 100644 --- a/src/indexing/BaseEventIndexManager.js +++ b/src/indexing/BaseEventIndexManager.js @@ -117,6 +117,13 @@ export default class BaseEventIndexManager { throw new Error("Unimplemented"); } + /** + * Get the disk usage of the index + */ + async indexSize(): Promise { + throw new Error("Unimplemented"); + } + /** * Commit the previously queued up events to the index. * diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index c912e31fa5..ae738e5d4d 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -406,4 +406,9 @@ export default class EventIndex { const indexManager = PlatformPeg.get().getEventIndexingManager(); return indexManager.searchEventIndex(searchArgs); } + + async indexSize() { + const indexManager = PlatformPeg.get().getEventIndexingManager(); + return indexManager.indexSize(); + } } From b7b66cfd9aad22f3a00e3b3126a708d1d81e0447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:15:55 +0100 Subject: [PATCH 02/89] EventIndex: Use the sleep method from our utils. --- src/indexing/EventIndex.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index ae738e5d4d..f034df888c 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -16,6 +16,7 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import {MatrixClientPeg} from "../MatrixClientPeg"; +import {sleep} from "../utils/promise"; /* * Event indexing class that wraps the platform specific event indexing. @@ -180,12 +181,6 @@ export default class EventIndex { } async crawlerFunc() { - // TODO either put this in a better place or find a library provided - // method that does this. - const sleep = async (ms) => { - return new Promise(resolve => setTimeout(resolve, ms)); - }; - let cancelled = false; console.log("EventIndex: Started crawler function"); From 47156351a6d9ffe9ee2110c63c8667fc043c8936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:25:34 +0100 Subject: [PATCH 03/89] EventIndex: Use a setting for the crawler sleep time. --- src/indexing/EventIndex.js | 24 ++++++++++++++++++++---- src/settings/Settings.js | 5 +++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index f034df888c..1e15fcaa5a 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -16,6 +16,8 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import {MatrixClientPeg} from "../MatrixClientPeg"; +import SettingsStore from '../settings/SettingsStore'; +import {SettingLevel} from "../settings/SettingsStore"; import {sleep} from "../utils/promise"; /* @@ -24,9 +26,9 @@ import {sleep} from "../utils/promise"; export default class EventIndex { constructor() { this.crawlerCheckpoints = []; - // The time that the crawler will wait between /rooms/{room_id}/messages - // requests - this._crawlerTimeout = 3000; + // The time in ms that the crawler will wait loop iterations if there + // have not been any checkpoints to consume in the last iteration. + this._crawlerIdleTime = 5000; // The maximum number of events our crawler should fetch in a single // crawl. this._eventsPerCrawl = 100; @@ -194,11 +196,22 @@ export default class EventIndex { cancelled = true; }; + let idle = false; + while (!cancelled) { // This is a low priority task and we don't want to spam our // homeserver with /messages requests so we set a hefty timeout // here. - await sleep(this._crawlerTimeout); + let sleepTime = SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'); + + // Don't let the user configure a lower sleep time than 100 ms. + sleepTime = Math.max(sleepTime, 100); + + if (idle) { + sleepTime = this._crawlerIdleTime; + } + + await sleep(sleepTime); console.log("EventIndex: Running the crawler loop."); @@ -211,9 +224,12 @@ export default class EventIndex { /// There is no checkpoint available currently, one may appear if // a sync with limited room timelines happens, so go back to sleep. if (checkpoint === undefined) { + idle = true; continue; } + idle = false; + console.log("EventIndex: crawling using checkpoint", checkpoint); // We have a checkpoint, let us fetch some messages, again, very diff --git a/src/settings/Settings.js b/src/settings/Settings.js index eacf63e55d..e967becf98 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -486,4 +486,9 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: RIGHT_PANEL_PHASES.GroupMemberList, }, + "crawlerSleepTime": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("How long should the crawler wait between requests"), + default: 3000, + } }; From 0132c3bbe3521ecd2af39bfd526ea41048791619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:31:16 +0100 Subject: [PATCH 04/89] EventIndex: Start the crawler only if it's configured to start. --- src/indexing/EventIndex.js | 4 +++- src/settings/Settings.js | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 1e15fcaa5a..c907e769b5 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -120,7 +120,9 @@ export default class EventIndex { if (eventIndexWasEmpty) await addInitialCheckpoints(); // Start our crawler. - this.startCrawler(); + if (SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling')) { + this.startCrawler(); + } return; } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index e967becf98..1c65c38167 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -490,5 +490,10 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("How long should the crawler wait between requests"), default: 3000, + }, + "enableCrawling" : { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("How long should the crawler wait between requests"), + default: true, } }; From 4fe7752f3cd9d52b97d212afc49fc46c60d318d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:37:07 +0100 Subject: [PATCH 05/89] EventIndex: Add a method to gather the currently crawled rooms. --- src/indexing/EventIndex.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index c907e769b5..1b17cc6d87 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -33,6 +33,7 @@ export default class EventIndex { // crawl. this._eventsPerCrawl = 100; this._crawler = null; + this._currentCheckpoint = null; this.liveEventsForIndex = new Set(); } @@ -213,6 +214,8 @@ export default class EventIndex { sleepTime = this._crawlerIdleTime; } + this._currentCheckpoint = null; + await sleep(sleepTime); console.log("EventIndex: Running the crawler loop."); @@ -230,6 +233,8 @@ export default class EventIndex { continue; } + this._currentCheckpoint = checkpoint; + idle = false; console.log("EventIndex: crawling using checkpoint", checkpoint); @@ -424,4 +429,31 @@ export default class EventIndex { const indexManager = PlatformPeg.get().getEventIndexingManager(); return indexManager.indexSize(); } + + currentlyCrawledRooms() { + let crawlingRooms = new Set(); + let totalRooms = new Set(); + + this.crawlerCheckpoints.forEach((checkpoint, index) => { + crawlingRooms.add(checkpoint.roomId); + }); + + if (this._currentCheckpoint !== null) { + crawlingRooms.add(this._currentCheckpoint.roomId); + } + + const client = MatrixClientPeg.get(); + const rooms = client.getRooms(); + + const isRoomEncrypted = (room) => { + return client.isRoomEncrypted(room.roomId); + }; + + const encryptedRooms = rooms.filter(isRoomEncrypted); + encryptedRooms.forEach((room, index) => { + totalRooms.add(room.roomId); + }); + + return {crawlingRooms, totalRooms} + } } From 928bb69b116fbfb3041d6b2538080b37f503e7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:37:53 +0100 Subject: [PATCH 06/89] EventIndexPeg: Add a helper method to easily start the crawler. --- src/indexing/EventIndexPeg.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/indexing/EventIndexPeg.js b/src/indexing/EventIndexPeg.js index 3746591b1f..a63756ab4e 100644 --- a/src/indexing/EventIndexPeg.js +++ b/src/indexing/EventIndexPeg.js @@ -69,6 +69,11 @@ class EventIndexPeg { return this.index; } + start() { + if (this.index === null) return; + this.index.startCrawler(); + } + stop() { if (this.index === null) return; this.index.stopCrawler(); From 2fe36037371fc723d4d7219e884b8fb77298a8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:39:45 +0100 Subject: [PATCH 07/89] utils: Add an utility function to format bytes. --- src/utils/FormattingUtils.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.js index 1fd7d00feb..9016d62cfb 100644 --- a/src/utils/FormattingUtils.js +++ b/src/utils/FormattingUtils.js @@ -30,6 +30,22 @@ export function formatCount(count) { return (count / 1000000000).toFixed(1) + "B"; // 10B is enough for anyone, right? :S } +/** + * format a size in bytes into a human readable form + * e.g: 1024 -> 1.00 KB + */ +export function formatBytes(bytes, decimals = 2) { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} + /** * format a key into groups of 4 characters, for easier visual inspection * From c397de18bdea962ca8dca8f2f1a98c26aa29941d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 13:43:47 +0100 Subject: [PATCH 08/89] PreferencesUserSettingsTab: Add initial event indexing preferences. --- .../tabs/user/PreferencesUserSettingsTab.js | 91 +++++++++++++++++++ src/i18n/strings/en_EN.json | 9 ++ 2 files changed, 100 insertions(+) diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index db5b95cb4c..d1fed21a1f 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -23,6 +23,8 @@ import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; +import EventIndexPeg from "../../../../../indexing/EventIndexPeg"; +import {formatBytes} from "../../../../../utils/FormattingUtils"; export default class PreferencesUserSettingsTab extends React.Component { static COMPOSER_SETTINGS = [ @@ -70,6 +72,13 @@ export default class PreferencesUserSettingsTab extends React.Component { alwaysShowMenuBarSupported: false, minimizeToTray: true, minimizeToTraySupported: false, + eventIndexSize: 0, + crawlingRooms: 0, + totalCrawlingRooms: 0, + eventIndexingEnabled: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), + crawlerSleepTime: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), autocompleteDelay: SettingsStore.getValueAt(SettingLevel.DEVICE, 'autocompleteDelay').toString(10), readMarkerInViewThresholdMs: @@ -100,6 +109,19 @@ export default class PreferencesUserSettingsTab extends React.Component { minimizeToTray = await platform.getMinimizeToTrayEnabled(); } + let eventIndexSize = 0; + let crawlingRooms = 0; + let totalCrawlingRooms = 0; + + let eventIndex = EventIndexPeg.get(); + + if (eventIndex !== null) { + eventIndexSize = await eventIndex.indexSize(); + let crawledRooms = eventIndex.currentlyCrawledRooms(); + crawlingRooms = crawledRooms.crawlingRooms.size; + totalCrawlingRooms = crawledRooms.totalRooms.size; + } + this.setState({ autoLaunch, autoLaunchSupported, @@ -107,6 +129,9 @@ export default class PreferencesUserSettingsTab extends React.Component { alwaysShowMenuBar, minimizeToTraySupported, minimizeToTray, + eventIndexSize, + crawlingRooms, + totalCrawlingRooms, }); } @@ -137,6 +162,20 @@ export default class PreferencesUserSettingsTab extends React.Component { SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.value); }; + _onEventIndexingEnabledChange = (checked) => { + SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); + + if (checked) EventIndexPeg.start(); + else EventIndexPeg.stop(); + + this.setState({eventIndexingEnabled: checked}); + } + + _onCrawlerSleepTimeChange = (e) => { + this.setState({crawlerSleepTime: e.target.value}); + SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); + } + _renderGroup(settingIds) { const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return settingIds.map(i => ); @@ -167,9 +206,61 @@ export default class PreferencesUserSettingsTab extends React.Component { label={_t('Show tray icon and minimize window to it on close')} />; } + let eventIndexingSettings = null; + let crawlerState; + + if (!this.state.eventIndexingEnabled) { + crawlerState =
{_t("Message downloader is stopped.")}
; + } + else if (this.state.crawlingRooms === 0) { + crawlerState =
{_t("Message downloader is currently idle.")}
; + } else { + crawlerState = ( +
{_t( + "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", + { crawlingRooms: this.state.crawlingRooms, + totalRooms: this.state.totalCrawlingRooms, + })} +
+ ); + } + + if (EventIndexPeg.get() !== null) { + eventIndexingSettings = ( +
+ {_t("Encrypted search")} + { + _t( "To enable search in encrypted rooms, Riot needs to run " + + "a background process to download historical messages " + + "from those rooms to your computer." + ) + } +
+ {_t("Message disk usage:")} {formatBytes(this.state.eventIndexSize, 0)}
+ {crawlerState}
+
+ + + + +
+ ); + } + return (
{_t("Preferences")}
+ + {eventIndexingSettings} +
{_t("Composer")} {this._renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f8b17db7c5..aa8583cf8d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -415,6 +415,7 @@ "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)", "Send read receipts for messages (requires compatible homeserver to disable)": "Send read receipts for messages (requires compatible homeserver to disable)", "Show previews/thumbnails for images": "Show previews/thumbnails for images", + "How long should the crawler wait between requests": "How long should the crawler wait between requests", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", @@ -731,6 +732,14 @@ "Start automatically after system login": "Start automatically after system login", "Always show the window menu bar": "Always show the window menu bar", "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close", + "Message downloader is stopped.": "Message downloader is stopped.", + "Message downloader is currently idle.": "Message downloader is currently idle.", + "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.": "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", + "Encrypted search": "Encrypted search", + "To enable search in encrypted rooms, Riot needs to run a background process to download historical messages from those rooms to your computer.": "To enable search in encrypted rooms, Riot needs to run a background process to download historical messages from those rooms to your computer.", + "Message disk usage:": "Message disk usage:", + "Enable message downloading": "Enable message downloading", + "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Preferences": "Preferences", "Composer": "Composer", "Timeline": "Timeline", From 3c46a563914f0347ebdfed3f80161ea86b4e841b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Nov 2019 15:06:04 +0100 Subject: [PATCH 09/89] EventIndex: Fix some lint errors. --- .../settings/tabs/user/PreferencesUserSettingsTab.js | 11 +++++------ src/indexing/EventIndex.js | 6 +++--- src/settings/Settings.js | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index d1fed21a1f..e47f591dcb 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -113,11 +113,11 @@ export default class PreferencesUserSettingsTab extends React.Component { let crawlingRooms = 0; let totalCrawlingRooms = 0; - let eventIndex = EventIndexPeg.get(); + const eventIndex = EventIndexPeg.get(); if (eventIndex !== null) { eventIndexSize = await eventIndex.indexSize(); - let crawledRooms = eventIndex.currentlyCrawledRooms(); + const crawledRooms = eventIndex.currentlyCrawledRooms(); crawlingRooms = crawledRooms.crawlingRooms.size; totalCrawlingRooms = crawledRooms.totalRooms.size; } @@ -211,8 +211,7 @@ export default class PreferencesUserSettingsTab extends React.Component { if (!this.state.eventIndexingEnabled) { crawlerState =
{_t("Message downloader is stopped.")}
; - } - else if (this.state.crawlingRooms === 0) { + } else if (this.state.crawlingRooms === 0) { crawlerState =
{_t("Message downloader is currently idle.")}
; } else { crawlerState = ( @@ -231,8 +230,8 @@ export default class PreferencesUserSettingsTab extends React.Component { {_t("Encrypted search")} { _t( "To enable search in encrypted rooms, Riot needs to run " + - "a background process to download historical messages " + - "from those rooms to your computer." + "a background process to download historical messages " + + "from those rooms to your computer.", ) }
diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 1b17cc6d87..0d7f43b839 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -431,8 +431,8 @@ export default class EventIndex { } currentlyCrawledRooms() { - let crawlingRooms = new Set(); - let totalRooms = new Set(); + const crawlingRooms = new Set(); + const totalRooms = new Set(); this.crawlerCheckpoints.forEach((checkpoint, index) => { crawlingRooms.add(checkpoint.roomId); @@ -454,6 +454,6 @@ export default class EventIndex { totalRooms.add(room.roomId); }); - return {crawlingRooms, totalRooms} + return {crawlingRooms, totalRooms}; } } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 1c65c38167..817adcfc4d 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -491,9 +491,9 @@ export const SETTINGS = { displayName: _td("How long should the crawler wait between requests"), default: 3000, }, - "enableCrawling" : { + "enableCrawling": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("How long should the crawler wait between requests"), default: true, - } + }, }; From 3b99f7565dbbf453eb615f9034db2dfd574c9128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 17 Jan 2020 17:10:59 +0100 Subject: [PATCH 10/89] PreferencesUserSettingsTab: Move the event index UI into a separate component. --- .../views/settings/EventIndexPanel.js | 131 ++++++++++++++++++ .../tabs/user/PreferencesUserSettingsTab.js | 90 +----------- 2 files changed, 134 insertions(+), 87 deletions(-) create mode 100644 src/components/views/settings/EventIndexPanel.js diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js new file mode 100644 index 0000000000..98ba83f62b --- /dev/null +++ b/src/components/views/settings/EventIndexPanel.js @@ -0,0 +1,131 @@ +/* +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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import * as sdk from '../../../index'; +import { _t } from '../../../languageHandler'; +import Modal from '../../../Modal'; +import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; +import Field from "../elements/Field"; +import {formatBytes} from "../../../utils/FormattingUtils"; +import EventIndexPeg from "../../../indexing/EventIndexPeg"; + +export default class EventIndexPanel extends React.Component { + constructor() { + super(); + + this.state = { + eventIndexSize: 0, + crawlingRooms: 0, + totalCrawlingRooms: 0, + eventIndexingEnabled: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), + crawlerSleepTime: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), + }; + } + + async componentWillMount(): void { + let eventIndexSize = 0; + let crawlingRooms = 0; + let totalCrawlingRooms = 0; + + const eventIndex = EventIndexPeg.get(); + + if (eventIndex !== null) { + eventIndexSize = await eventIndex.indexSize(); + const crawledRooms = eventIndex.currentlyCrawledRooms(); + crawlingRooms = crawledRooms.crawlingRooms.size; + totalCrawlingRooms = crawledRooms.totalRooms.size; + } + + this.setState({ + eventIndexSize, + crawlingRooms, + totalCrawlingRooms, + }); + } + + _onEventIndexingEnabledChange = (checked) => { + SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); + + if (checked) EventIndexPeg.start(); + else EventIndexPeg.stop(); + + this.setState({eventIndexingEnabled: checked}); + } + + _onCrawlerSleepTimeChange = (e) => { + this.setState({crawlerSleepTime: e.target.value}); + SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); + } + + render() { + let eventIndexingSettings = null; + let crawlerState; + + if (!this.state.eventIndexingEnabled) { + crawlerState =
{_t("Message downloader is stopped.")}
; + } else if (this.state.crawlingRooms === 0) { + crawlerState =
{_t("Message downloader is currently idle.")}
; + } else { + crawlerState = ( +
{_t( + "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", + { crawlingRooms: this.state.crawlingRooms, + totalRooms: this.state.totalCrawlingRooms, + })} +
+ ); + } + + if (EventIndexPeg.get() !== null) { + eventIndexingSettings = ( +
+ {_t("Encrypted search")} + { + _t( "To enable search in encrypted rooms, Riot needs to run " + + "a background process to download historical messages " + + "from those rooms to your computer.", + ) + } +
+ {_t("Message disk usage:")} {formatBytes(this.state.eventIndexSize, 0)}
+ {crawlerState}
+
+ + + + +
+ ); + } + + return eventIndexingSettings; + } +} diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index e47f591dcb..5ecafcc5ae 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -23,7 +23,6 @@ import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; -import EventIndexPeg from "../../../../../indexing/EventIndexPeg"; import {formatBytes} from "../../../../../utils/FormattingUtils"; export default class PreferencesUserSettingsTab extends React.Component { @@ -72,13 +71,6 @@ export default class PreferencesUserSettingsTab extends React.Component { alwaysShowMenuBarSupported: false, minimizeToTray: true, minimizeToTraySupported: false, - eventIndexSize: 0, - crawlingRooms: 0, - totalCrawlingRooms: 0, - eventIndexingEnabled: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), - crawlerSleepTime: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), autocompleteDelay: SettingsStore.getValueAt(SettingLevel.DEVICE, 'autocompleteDelay').toString(10), readMarkerInViewThresholdMs: @@ -109,19 +101,6 @@ export default class PreferencesUserSettingsTab extends React.Component { minimizeToTray = await platform.getMinimizeToTrayEnabled(); } - let eventIndexSize = 0; - let crawlingRooms = 0; - let totalCrawlingRooms = 0; - - const eventIndex = EventIndexPeg.get(); - - if (eventIndex !== null) { - eventIndexSize = await eventIndex.indexSize(); - const crawledRooms = eventIndex.currentlyCrawledRooms(); - crawlingRooms = crawledRooms.crawlingRooms.size; - totalCrawlingRooms = crawledRooms.totalRooms.size; - } - this.setState({ autoLaunch, autoLaunchSupported, @@ -129,9 +108,6 @@ export default class PreferencesUserSettingsTab extends React.Component { alwaysShowMenuBar, minimizeToTraySupported, minimizeToTray, - eventIndexSize, - crawlingRooms, - totalCrawlingRooms, }); } @@ -162,26 +138,14 @@ export default class PreferencesUserSettingsTab extends React.Component { SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.value); }; - _onEventIndexingEnabledChange = (checked) => { - SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); - - if (checked) EventIndexPeg.start(); - else EventIndexPeg.stop(); - - this.setState({eventIndexingEnabled: checked}); - } - - _onCrawlerSleepTimeChange = (e) => { - this.setState({crawlerSleepTime: e.target.value}); - SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); - } - _renderGroup(settingIds) { const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return settingIds.map(i => ); } render() { + const EventIndexPanel = sdk.getComponent('views.settings.EventIndexPanel'); + let autoLaunchOption = null; if (this.state.autoLaunchSupported) { autoLaunchOption = ; } - let eventIndexingSettings = null; - let crawlerState; - - if (!this.state.eventIndexingEnabled) { - crawlerState =
{_t("Message downloader is stopped.")}
; - } else if (this.state.crawlingRooms === 0) { - crawlerState =
{_t("Message downloader is currently idle.")}
; - } else { - crawlerState = ( -
{_t( - "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", - { crawlingRooms: this.state.crawlingRooms, - totalRooms: this.state.totalCrawlingRooms, - })} -
- ); - } - - if (EventIndexPeg.get() !== null) { - eventIndexingSettings = ( -
- {_t("Encrypted search")} - { - _t( "To enable search in encrypted rooms, Riot needs to run " + - "a background process to download historical messages " + - "from those rooms to your computer.", - ) - } -
- {_t("Message disk usage:")} {formatBytes(this.state.eventIndexSize, 0)}
- {crawlerState}
-
- - - - -
- ); - } - return (
{_t("Preferences")}
- {eventIndexingSettings} +
{_t("Composer")} From 695b8aff5b4672d3af70db317c023f84c9968aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 17 Jan 2020 17:14:55 +0100 Subject: [PATCH 11/89] EventIndexPanel: Reword the enable/disable setting. --- src/components/views/settings/EventIndexPanel.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 98ba83f62b..bade2d8735 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -114,7 +114,7 @@ export default class EventIndexPanel extends React.Component { + label={_t('Download and index encrypted messages')} /> Date: Mon, 20 Jan 2020 17:42:24 +0100 Subject: [PATCH 12/89] EventIndexPanel: Get more stats for our indexer, not just the size. --- .../views/settings/EventIndexPanel.js | 40 +++++++++++++------ src/i18n/strings/en_EN.json | 18 +++++---- src/indexing/BaseEventIndexManager.js | 13 +++++- src/indexing/EventIndex.js | 27 ++++++++++++- 4 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index bade2d8735..f8c61e092d 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -35,6 +35,9 @@ export default class EventIndexPanel extends React.Component { eventIndexSize: 0, crawlingRooms: 0, totalCrawlingRooms: 0, + eventCount: 0, + roomCount: 0, + currentRoom: null, eventIndexingEnabled: SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), crawlerSleepTime: @@ -44,22 +47,35 @@ export default class EventIndexPanel extends React.Component { async componentWillMount(): void { let eventIndexSize = 0; + let roomCount = 0; + let eventCount = 0; let crawlingRooms = 0; let totalCrawlingRooms = 0; + let currentRoom = null; const eventIndex = EventIndexPeg.get(); if (eventIndex !== null) { - eventIndexSize = await eventIndex.indexSize(); + const stats = await eventIndex.getStats(); + eventIndexSize = stats.size; + roomCount = stats.roomCount; + eventCount = stats.eventCount; + const crawledRooms = eventIndex.currentlyCrawledRooms(); crawlingRooms = crawledRooms.crawlingRooms.size; totalCrawlingRooms = crawledRooms.totalRooms.size; + + const room = eventIndex.currentRoom(); + if (room) currentRoom = room.name; } this.setState({ eventIndexSize, crawlingRooms, totalCrawlingRooms, + eventCount, + roomCount, + currentRoom, }); } @@ -82,16 +98,15 @@ export default class EventIndexPanel extends React.Component { let crawlerState; if (!this.state.eventIndexingEnabled) { - crawlerState =
{_t("Message downloader is stopped.")}
; - } else if (this.state.crawlingRooms === 0) { - crawlerState =
{_t("Message downloader is currently idle.")}
; + crawlerState =
{_t("Message search for encrypted rooms is disabled.")}
; + } else if (this.state.currentRoom === null) { + crawlerState =
{_t("Not currently downloading messages for any room.")}
; } else { crawlerState = (
{_t( - "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", - { crawlingRooms: this.state.crawlingRooms, - totalRooms: this.state.totalCrawlingRooms, - })} + "Downloading mesages for %(currentRoom)s.", + { currentRoom: this.state.currentRoom } + )}
); } @@ -101,13 +116,14 @@ export default class EventIndexPanel extends React.Component {
{_t("Encrypted search")} { - _t( "To enable search in encrypted rooms, Riot needs to run " + - "a background process to download historical messages " + - "from those rooms to your computer.", + _t( "Riot is securely caching encrypted messages locally for them" + + "to appear in search results:" ) }
- {_t("Message disk usage:")} {formatBytes(this.state.eventIndexSize, 0)}
+ {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
+ {_t("Indexed messages:")} {this.state.eventCount}
+ {_t("Number of rooms:")} {this.state.roomCount}
{crawlerState}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1d1636bfb2..a968c145d9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -554,6 +554,16 @@ "Failed to set display name": "Failed to set display name", "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", + "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", + "Not currently downloading messages for any room.": "Not currently downloading messages for any room.", + "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", + "Encrypted search": "Encrypted search", + "Riot is securely caching encrypted messages locally for themto appear in search results:": "Riot is securely caching encrypted messages locally for themto appear in search results:", + "Space used:": "Space used:", + "Indexed messages:": "Indexed messages:", + "Number of rooms:": "Number of rooms:", + "Download and index encrypted messages": "Download and index encrypted messages", + "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", @@ -732,14 +742,6 @@ "Start automatically after system login": "Start automatically after system login", "Always show the window menu bar": "Always show the window menu bar", "Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close", - "Message downloader is stopped.": "Message downloader is stopped.", - "Message downloader is currently idle.": "Message downloader is currently idle.", - "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.": "Currently downloading mesages in %(crawlingRooms)s of %(totalRooms)s rooms.", - "Encrypted search": "Encrypted search", - "To enable search in encrypted rooms, Riot needs to run a background process to download historical messages from those rooms to your computer.": "To enable search in encrypted rooms, Riot needs to run a background process to download historical messages from those rooms to your computer.", - "Message disk usage:": "Message disk usage:", - "Download and index encrypted messages": "Download and index encrypted messages", - "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Preferences": "Preferences", "Composer": "Composer", "Timeline": "Timeline", diff --git a/src/indexing/BaseEventIndexManager.js b/src/indexing/BaseEventIndexManager.js index 733dc05dd6..819b3e65a7 100644 --- a/src/indexing/BaseEventIndexManager.js +++ b/src/indexing/BaseEventIndexManager.js @@ -67,6 +67,12 @@ export interface HistoricEvent { profile: MatrixProfile; } +export interface IndexStats { + size: number; + event_count: number; + room_count: number; +} + /** * Base class for classes that provide platform-specific event indexing. * @@ -118,9 +124,12 @@ export default class BaseEventIndexManager { } /** - * Get the disk usage of the index + * Get statistical information of the index. + * + * @return {Promise} A promise that will resolve to the index + * statistics. */ - async indexSize(): Promise { + async getStats(): Promise { throw new Error("Unimplemented"); } diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 0d7f43b839..d00a0530ba 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -425,9 +425,9 @@ export default class EventIndex { return indexManager.searchEventIndex(searchArgs); } - async indexSize() { + async getStats() { const indexManager = PlatformPeg.get().getEventIndexingManager(); - return indexManager.indexSize(); + return indexManager.getStats(); } currentlyCrawledRooms() { @@ -456,4 +456,27 @@ export default class EventIndex { return {crawlingRooms, totalRooms}; } + + /** + * Get the room that we are currently crawling. + * + * @returns A MatrixRoom that is being currently crawled, null if no room is + * currently being crawled. + */ + currentRoom() { + if (this._currentCheckpoint === null && this.crawlerCheckpoints.length === 0) { + console.log("EventIndex: No current nor any checkpoint"); + return null; + } + + const client = MatrixClientPeg.get(); + + if (this._currentCheckpoint !== null) { + console.log("EventIndex: Current checkpoint available"); + return client.getRoom(this._currentCheckpoint.roomId); + } else { + console.log("EventIndex: No current but have checkpoint available"); + return client.getRoom(this.crawlerCheckpoints[0].roomId); + } + } } From 8de149704e52c4b1872b32efb899bdd5fd0b6707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 20 Jan 2020 17:43:55 +0100 Subject: [PATCH 13/89] EventIndexPanel: Dynamically update the indexer stats. --- .../views/settings/EventIndexPanel.js | 25 +++++++++++++++++++ src/indexing/EventIndex.js | 23 ++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index f8c61e092d..b777957a3a 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -45,6 +45,29 @@ export default class EventIndexPanel extends React.Component { }; } + async updateCurrentRoom(room) { + const eventIndex = EventIndexPeg.get(); + const stats = await eventIndex.getStats(); + let currentRoom = null; + + if (room) currentRoom = room.name; + + this.setState({ + eventIndexSize: stats.size, + roomCount: stats.roomCount, + eventCount: stats.eventCount, + currentRoom: currentRoom, + }); + } + + componentWillUnmount(): void { + const eventIndex = EventIndexPeg.get(); + + if (eventIndex !== null) { + eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom.bind(this)); + } + } + async componentWillMount(): void { let eventIndexSize = 0; let roomCount = 0; @@ -56,6 +79,8 @@ export default class EventIndexPanel extends React.Component { const eventIndex = EventIndexPeg.get(); if (eventIndex !== null) { + eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this)); + const stats = await eventIndex.getStats(); eventIndexSize = stats.size; roomCount = stats.roomCount; diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index d00a0530ba..5676636eed 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -19,6 +19,7 @@ import {MatrixClientPeg} from "../MatrixClientPeg"; import SettingsStore from '../settings/SettingsStore'; import {SettingLevel} from "../settings/SettingsStore"; import {sleep} from "../utils/promise"; +import {EventEmitter} from "events"; /* * Event indexing class that wraps the platform specific event indexing. @@ -35,6 +36,7 @@ export default class EventIndex { this._crawler = null; this._currentCheckpoint = null; this.liveEventsForIndex = new Set(); + this._eventEmitter = new EventEmitter(); } async init() { @@ -185,6 +187,10 @@ export default class EventIndex { indexManager.addEventToIndex(e, profile); } + emitNewCheckpoint() { + this._eventEmitter.emit("changedCheckpoint", this.currentRoom()); + } + async crawlerFunc() { let cancelled = false; @@ -214,7 +220,10 @@ export default class EventIndex { sleepTime = this._crawlerIdleTime; } - this._currentCheckpoint = null; + if (this._currentCheckpoint !== null) { + this._currentCheckpoint = null; + this.emitNewCheckpoint(); + } await sleep(sleepTime); @@ -234,6 +243,7 @@ export default class EventIndex { } this._currentCheckpoint = checkpoint; + this.emitNewCheckpoint(); idle = false; @@ -465,18 +475,23 @@ export default class EventIndex { */ currentRoom() { if (this._currentCheckpoint === null && this.crawlerCheckpoints.length === 0) { - console.log("EventIndex: No current nor any checkpoint"); return null; } const client = MatrixClientPeg.get(); if (this._currentCheckpoint !== null) { - console.log("EventIndex: Current checkpoint available"); return client.getRoom(this._currentCheckpoint.roomId); } else { - console.log("EventIndex: No current but have checkpoint available"); return client.getRoom(this.crawlerCheckpoints[0].roomId); } } + + on(eventName, callback) { + this._eventEmitter.on(eventName, callback); + } + + removeListener(eventName, callback) { + this._eventEmitter.removeListener(eventName, callback); + } } From 4913d579e37c5bcb0c39d6291fb1e43110c4ffc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 09:24:20 +0100 Subject: [PATCH 14/89] EventIndexPanel: Reword the crawler state if no room is being crawled. --- src/components/views/settings/EventIndexPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index b777957a3a..b3c75cd336 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -125,7 +125,7 @@ export default class EventIndexPanel extends React.Component { if (!this.state.eventIndexingEnabled) { crawlerState =
{_t("Message search for encrypted rooms is disabled.")}
; } else if (this.state.currentRoom === null) { - crawlerState =
{_t("Not currently downloading messages for any room.")}
; + crawlerState =
{_t("Not downloading messages for any room.")}
; } else { crawlerState = (
{_t( From 908a00a13dc936d9bab61c165d9fe941999f8924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 10:06:04 +0100 Subject: [PATCH 15/89] EventIndexPanel: Move the panel from the preferences to the security tab. --- src/components/views/settings/EventIndexPanel.js | 14 ++++++++++++-- .../tabs/user/PreferencesUserSettingsTab.js | 4 ---- .../settings/tabs/user/SecurityUserSettingsTab.js | 9 +++++++++ src/i18n/strings/en_EN.json | 5 +++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index b3c75cd336..5b8029a09a 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -138,8 +138,7 @@ export default class EventIndexPanel extends React.Component { if (EventIndexPeg.get() !== null) { eventIndexingSettings = ( -
- {_t("Encrypted search")} +
{ _t( "Riot is securely caching encrypted messages locally for them" + "to appear in search results:" @@ -165,6 +164,17 @@ export default class EventIndexPanel extends React.Component { onChange={this._onCrawlerSleepTimeChange} />
); + } else { + eventIndexingSettings = ( +
+ { + _t( "Riot can't securely cache encrypted messages locally" + + "while running in a web browser. Use Riot Desktop for" + + "encrypted messages to appear in search results." + ) + } +
+ ); } return eventIndexingSettings; diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 5ecafcc5ae..8cbaba037d 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -144,8 +144,6 @@ export default class PreferencesUserSettingsTab extends React.Component { } render() { - const EventIndexPanel = sdk.getComponent('views.settings.EventIndexPanel'); - let autoLaunchOption = null; if (this.state.autoLaunchSupported) { autoLaunchOption =
{_t("Preferences")}
- -
{_t("Composer")} {this._renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)} diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 5eadfc234a..8ef9983efd 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -242,6 +242,7 @@ export default class SecurityUserSettingsTab extends React.Component { render() { const DevicesPanel = sdk.getComponent('views.settings.DevicesPanel'); const SettingsFlag = sdk.getComponent('views.elements.SettingsFlag'); + const EventIndexPanel = sdk.getComponent('views.settings.EventIndexPanel'); const KeyBackupPanel = sdk.getComponent('views.settings.KeyBackupPanel'); const keyBackup = ( @@ -253,6 +254,13 @@ export default class SecurityUserSettingsTab extends React.Component {
); + const eventIndex = ( +
+ {_t("Encrypted search")} + +
+ ); + // XXX: There's no such panel in the current cross-signing designs, but // it's useful to have for testing the feature. If there's no interest // in having advanced details here once all flows are implemented, we @@ -281,6 +289,7 @@ export default class SecurityUserSettingsTab extends React.Component {
{keyBackup} + {eventIndex} {crossSigning} {this._renderCurrentDeviceInfo()}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a968c145d9..f78e3594ee 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -555,15 +555,15 @@ "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", - "Not currently downloading messages for any room.": "Not currently downloading messages for any room.", + "Not downloading messages for any room.": "Not downloading messages for any room.", "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", - "Encrypted search": "Encrypted search", "Riot is securely caching encrypted messages locally for themto appear in search results:": "Riot is securely caching encrypted messages locally for themto appear in search results:", "Space used:": "Space used:", "Indexed messages:": "Indexed messages:", "Number of rooms:": "Number of rooms:", "Download and index encrypted messages": "Download and index encrypted messages", "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", + "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", @@ -759,6 +759,7 @@ "Accept all %(invitedRooms)s invites": "Accept all %(invitedRooms)s invites", "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", "Key backup": "Key backup", + "Encrypted search": "Encrypted search", "Cross-signing": "Cross-signing", "Security & Privacy": "Security & Privacy", "Devices": "Devices", From 6b85284632fad2d83f38d9f71959e8afcac1a76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 13:20:30 +0100 Subject: [PATCH 16/89] EventIndexPanel: Move the bulk of the event index info into a modal. --- .../dialogs/eventindex/ManageEventIndex.js | 226 ++++++++++++++++++ .../views/settings/EventIndexPanel.js | 93 ++----- src/i18n/strings/en_EN.json | 25 +- 3 files changed, 259 insertions(+), 85 deletions(-) create mode 100644 src/async-components/views/dialogs/eventindex/ManageEventIndex.js diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js new file mode 100644 index 0000000000..23aa61c33a --- /dev/null +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -0,0 +1,226 @@ +/* +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. +*/ + +import React from 'react'; +import * as sdk from '../../../../index'; +import {MatrixClientPeg} from '../../../../MatrixClientPeg'; +import PropTypes from 'prop-types'; +import { _t } from '../../../../languageHandler'; + +import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; +import LabelledToggleSwitch from "../../../../components/views/elements/LabelledToggleSwitch"; +import Field from "../../../../components/views/elements/Field"; +import {formatBytes} from "../../../../utils/FormattingUtils"; +import EventIndexPeg from "../../../../indexing/EventIndexPeg"; +import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; + + +/* + * Walks the user through the process of creating an e2e key backup + * on the server. + */ +export default class ManageEventIndex extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + } + + constructor(props) { + super(props); + + this.state = { + eventIndexSize: 0, + crawlingRooms: 0, + totalCrawlingRooms: 0, + eventCount: 0, + roomCount: 0, + currentRoom: null, + eventIndexingEnabled: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), + crawlerSleepTime: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), + }; + + } + + async updateCurrentRoom(room) { + const eventIndex = EventIndexPeg.get(); + const stats = await eventIndex.getStats(); + let currentRoom = null; + + if (room) currentRoom = room.name; + + this.setState({ + eventIndexSize: stats.size, + roomCount: stats.roomCount, + eventCount: stats.eventCount, + currentRoom: currentRoom, + }); + } + + componentWillUnmount(): void { + const eventIndex = EventIndexPeg.get(); + + if (eventIndex !== null) { + eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom.bind(this)); + } + } + + async componentWillMount(): void { + let eventIndexSize = 0; + let roomCount = 0; + let eventCount = 0; + let crawlingRooms = 0; + let totalCrawlingRooms = 0; + let currentRoom = null; + + const eventIndex = EventIndexPeg.get(); + + if (eventIndex !== null) { + eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this)); + + const stats = await eventIndex.getStats(); + eventIndexSize = stats.size; + roomCount = stats.roomCount; + eventCount = stats.eventCount; + + const crawledRooms = eventIndex.currentlyCrawledRooms(); + crawlingRooms = crawledRooms.crawlingRooms.size; + totalCrawlingRooms = crawledRooms.totalRooms.size; + + const room = eventIndex.currentRoom(); + if (room) currentRoom = room.name; + } + + this.setState({ + eventIndexSize, + crawlingRooms, + totalCrawlingRooms, + eventCount, + roomCount, + currentRoom, + }); + } + + _onEventIndexingEnabledChange = (checked) => { + SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); + + if (checked) EventIndexPeg.start(); + else EventIndexPeg.stop(); + + this.setState({eventIndexingEnabled: checked}); + } + + _onCrawlerSleepTimeChange = (e) => { + this.setState({crawlerSleepTime: e.target.value}); + SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); + } + + _onDisable = () => { + this.props.onFinished(false); + } + + _onDone = () => { + this.props.onFinished(true); + } + + render() { + let eventIndexingSettings = null; + let crawlerState; + + if (!this.state.eventIndexingEnabled) { + crawlerState =
{_t("Message search for encrypted rooms is disabled.")}
; + } else if (this.state.currentRoom === null) { + crawlerState =
{_t("Not downloading messages for any room.")}
; + } else { + crawlerState = ( +
{_t( + "Downloading mesages for %(currentRoom)s.", + { currentRoom: this.state.currentRoom } + )} +
+ ); + } + + if (EventIndexPeg.get() !== null) { + eventIndexingSettings = ( +
+ { + _t( "Riot is securely caching encrypted messages locally for them " + + "to appear in search results:" + ) + } +
+ {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
+ {_t("Indexed messages:")} {this.state.eventCount}
+ {_t("Number of rooms:")} {this.state.roomCount}
+ {crawlerState}
+
+ + + + +
+ ); + } else { + eventIndexingSettings = ( +
+ { + _t( "Riot can't securely cache encrypted messages locally" + + "while running in a web browser. Use Riot Desktop for" + + "encrypted messages to appear in search results." + ) + } +
+ ); + } + + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + let buttons; + + buttons =
+
+ + {_t("Disable")} + + + {_t("Done")} + +
+
; + + return ( + {}} + title={_t("Message search")} + > +
+ {eventIndexingSettings} +
+
+ {buttons} +
+
+ ); + } +} diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 5b8029a09a..fd3facbc6b 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -21,6 +21,7 @@ import classNames from 'classnames'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; +import AccessibleButton from "../elements/AccessibleButton"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import Field from "../elements/Field"; @@ -33,30 +34,17 @@ export default class EventIndexPanel extends React.Component { this.state = { eventIndexSize: 0, - crawlingRooms: 0, - totalCrawlingRooms: 0, - eventCount: 0, roomCount: 0, - currentRoom: null, - eventIndexingEnabled: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), - crawlerSleepTime: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), }; } async updateCurrentRoom(room) { const eventIndex = EventIndexPeg.get(); const stats = await eventIndex.getStats(); - let currentRoom = null; - - if (room) currentRoom = room.name; this.setState({ eventIndexSize: stats.size, roomCount: stats.roomCount, - eventCount: stats.eventCount, - currentRoom: currentRoom, }); } @@ -71,10 +59,6 @@ export default class EventIndexPanel extends React.Component { async componentWillMount(): void { let eventIndexSize = 0; let roomCount = 0; - let eventCount = 0; - let crawlingRooms = 0; - let totalCrawlingRooms = 0; - let currentRoom = null; const eventIndex = EventIndexPeg.get(); @@ -84,84 +68,41 @@ export default class EventIndexPanel extends React.Component { const stats = await eventIndex.getStats(); eventIndexSize = stats.size; roomCount = stats.roomCount; - eventCount = stats.eventCount; - - const crawledRooms = eventIndex.currentlyCrawledRooms(); - crawlingRooms = crawledRooms.crawlingRooms.size; - totalCrawlingRooms = crawledRooms.totalRooms.size; - - const room = eventIndex.currentRoom(); - if (room) currentRoom = room.name; } this.setState({ eventIndexSize, - crawlingRooms, - totalCrawlingRooms, - eventCount, roomCount, - currentRoom, }); } - _onEventIndexingEnabledChange = (checked) => { - SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); + _onManage = async () => { + Modal.createTrackedDialogAsync('Message search', 'Message search', + import('../../../async-components/views/dialogs/eventindex/ManageEventIndex'), + { + onFinished: () => {}, + }, null, /* priority = */ false, /* static = */ true, + ); - if (checked) EventIndexPeg.start(); - else EventIndexPeg.stop(); - - this.setState({eventIndexingEnabled: checked}); - } - - _onCrawlerSleepTimeChange = (e) => { - this.setState({crawlerSleepTime: e.target.value}); - SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); } render() { let eventIndexingSettings = null; - let crawlerState; - - if (!this.state.eventIndexingEnabled) { - crawlerState =
{_t("Message search for encrypted rooms is disabled.")}
; - } else if (this.state.currentRoom === null) { - crawlerState =
{_t("Not downloading messages for any room.")}
; - } else { - crawlerState = ( -
{_t( - "Downloading mesages for %(currentRoom)s.", - { currentRoom: this.state.currentRoom } - )} -
- ); - } if (EventIndexPeg.get() !== null) { eventIndexingSettings = (
- { - _t( "Riot is securely caching encrypted messages locally for them" + - "to appear in search results:" - ) - }
- {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
- {_t("Indexed messages:")} {this.state.eventCount}
- {_t("Number of rooms:")} {this.state.roomCount}
- {crawlerState}
+ {_t( "Securely cache encrypted messages locally for them " + + "to appear in search results, using ") + } {formatBytes(this.state.eventIndexSize, 0)} + {_t( " to store messages from ")} {this.state.roomCount} {_t("rooms.")} +
+
+ + {_t("Manage")} +
- - - -
); } else { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f78e3594ee..6b1eda0b0c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -554,15 +554,10 @@ "Failed to set display name": "Failed to set display name", "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", - "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", - "Not downloading messages for any room.": "Not downloading messages for any room.", - "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", - "Riot is securely caching encrypted messages locally for themto appear in search results:": "Riot is securely caching encrypted messages locally for themto appear in search results:", - "Space used:": "Space used:", - "Indexed messages:": "Indexed messages:", - "Number of rooms:": "Number of rooms:", - "Download and index encrypted messages": "Download and index encrypted messages", - "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", + "Securely cache encrypted messages locally for them to appear in search results, using ": "Securely cache encrypted messages locally for them to appear in search results, using ", + " to store messages from ": " to store messages from ", + "rooms.": "rooms.", + "Manage": "Manage", "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", @@ -2040,6 +2035,18 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "This device has detected that your recovery passphrase and key for Secure Messages have been removed.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", + "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", + "Not downloading messages for any room.": "Not downloading messages for any room.", + "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", + "Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot is securely caching encrypted messages locally for them to appear in search results:", + "Space used:": "Space used:", + "Indexed messages:": "Indexed messages:", + "Number of rooms:": "Number of rooms:", + "Download and index encrypted messages": "Download and index encrypted messages", + "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", + "Disable or enable": "Disable or enable", + "Disable": "Disable", + "Message search": "Message search", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" From 8e26268079ca83954a78336859b679bb0937b533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 13:38:20 +0100 Subject: [PATCH 17/89] SecurityUserSettingsTab: Rename encrypted search section. --- .../views/settings/tabs/user/SecurityUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 8ef9983efd..7b22dd15e2 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -256,7 +256,7 @@ export default class SecurityUserSettingsTab extends React.Component { const eventIndex = (
- {_t("Encrypted search")} + {_t("Message search")}
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6b1eda0b0c..b2ac55ba04 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -754,7 +754,7 @@ "Accept all %(invitedRooms)s invites": "Accept all %(invitedRooms)s invites", "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", "Key backup": "Key backup", - "Encrypted search": "Encrypted search", + "Message search": "Message search", "Cross-signing": "Cross-signing", "Security & Privacy": "Security & Privacy", "Devices": "Devices", @@ -2044,9 +2044,7 @@ "Number of rooms:": "Number of rooms:", "Download and index encrypted messages": "Download and index encrypted messages", "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", - "Disable or enable": "Disable or enable", "Disable": "Disable", - "Message search": "Message search", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" From a2892f5b02a8998a7d19985b59f65d246d99c99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 16:40:32 +0100 Subject: [PATCH 18/89] EventIndex: Fix some lint issues. --- src/components/views/settings/EventIndexPanel.js | 9 +-------- .../settings/tabs/user/PreferencesUserSettingsTab.js | 1 - src/indexing/EventIndex.js | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index fd3facbc6b..8ed4b114e7 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -15,16 +15,10 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; import AccessibleButton from "../elements/AccessibleButton"; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; -import Field from "../elements/Field"; import {formatBytes} from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; @@ -83,7 +77,6 @@ export default class EventIndexPanel extends React.Component { onFinished: () => {}, }, null, /* priority = */ false, /* static = */ true, ); - } render() { @@ -111,7 +104,7 @@ export default class EventIndexPanel extends React.Component { { _t( "Riot can't securely cache encrypted messages locally" + "while running in a web browser. Use Riot Desktop for" + - "encrypted messages to appear in search results." + "encrypted messages to appear in search results.", ) }
diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 8cbaba037d..bd1b7c2ca4 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -23,7 +23,6 @@ import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; -import {formatBytes} from "../../../../../utils/FormattingUtils"; export default class PreferencesUserSettingsTab extends React.Component { static COMPOSER_SETTINGS = [ diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 5676636eed..435f67447d 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -470,8 +470,8 @@ export default class EventIndex { /** * Get the room that we are currently crawling. * - * @returns A MatrixRoom that is being currently crawled, null if no room is - * currently being crawled. + * @returns {Room} A MatrixRoom that is being currently crawled, null + * if no room is currently being crawled. */ currentRoom() { if (this._currentCheckpoint === null && this.crawlerCheckpoints.length === 0) { From 47ea453abfbd6ac5ce7f4e474d84eb7c8a3d4083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jan 2020 16:58:41 +0100 Subject: [PATCH 19/89] ManageEventIndex: Fix a couple of lint issues. --- .../views/dialogs/eventindex/ManageEventIndex.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 23aa61c33a..8623856b2e 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import * as sdk from '../../../../index'; -import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; @@ -52,7 +51,6 @@ export default class ManageEventIndex extends React.Component { crawlerSleepTime: SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), }; - } async updateCurrentRoom(room) { @@ -146,10 +144,8 @@ export default class ManageEventIndex extends React.Component { crawlerState =
{_t("Not downloading messages for any room.")}
; } else { crawlerState = ( -
{_t( - "Downloading mesages for %(currentRoom)s.", - { currentRoom: this.state.currentRoom } - )} +
+ {_t("Downloading mesages for %(currentRoom)s.", { currentRoom: this.state.currentRoom })}
); } @@ -159,7 +155,7 @@ export default class ManageEventIndex extends React.Component {
{ _t( "Riot is securely caching encrypted messages locally for them " + - "to appear in search results:" + "to appear in search results:", ) }
@@ -188,7 +184,7 @@ export default class ManageEventIndex extends React.Component { { _t( "Riot can't securely cache encrypted messages locally" + "while running in a web browser. Use Riot Desktop for" + - "encrypted messages to appear in search results." + "encrypted messages to appear in search results.", ) }
@@ -196,9 +192,7 @@ export default class ManageEventIndex extends React.Component { } const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - let buttons; - - buttons =
+ const buttons =
{_t("Disable")} From b2fc4a1c4de9251284c392c3d1efe3ae06fd222c Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 21 Jan 2020 18:41:43 +0000 Subject: [PATCH 20/89] Style bridge settings tab according to design Signed-off-by: Half-Shot --- .../views/dialogs/_RoomSettingsDialog.scss | 58 ++++++- .../views/dialogs/RoomSettingsDialog.js | 7 +- .../settings/tabs/room/BridgeSettingsTab.js | 156 ++++++++++-------- src/i18n/strings/en_EN.json | 11 +- 4 files changed, 144 insertions(+), 88 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index aa66e97f9e..0e8deb018e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -63,9 +63,59 @@ limitations under the License. .mx_RoomSettingsDialog_BridgeList li { list-style-type: none; padding: 5px; - margin-bottom: 5px; - border-width: 1px 0px; - border-color: #dee1f3; - border-style: solid; + margin-bottom: 8px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-radius: 5px; + + .protocol-icon { + float: left; + margin-right: 30px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-style: solid; + } + span { + /* Correct letter placement */ + left: auto; + } + } + + h3 { + margin-top: 0; + margin-bottom: 4px; + font-size: 16pt; + } + + .column-icon { + float: left; + } + + .column-data { + display: inline-block; + width: 85%; + } + + .workspace-channel-details { + margin-top: 0; + color: $primary-fg-color; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; + } + } diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index a99141870b..76faf60eef 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -54,9 +54,6 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; - const featureFlag = SettingsStore.isFeatureEnabled("feature_bridge_state"); - const shouldShowBridgeIcon = featureFlag && - BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( _td("General"), @@ -79,9 +76,9 @@ export default class RoomSettingsDialog extends React.Component { , )); - if (shouldShowBridgeIcon) { + if (SettingsStore.isFeatureEnabled("feature_bridge_state")) { tabs.push(new Tab( - _td("Bridge Info"), + _td("Bridges"), "mx_RoomSettingsDialog_bridgesIcon", , )); diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 19c19d3bc6..8090cf7d4d 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -33,6 +33,21 @@ export default class BridgeSettingsTab extends React.Component { roomId: PropTypes.string.isRequired, }; + constructor() { + super(); + + this.state = { + showMoreCard: null, + }; + } + + + _showMoreDetails(eventId) { + this.setState({ + showMoreCard: eventId, + }); + } + _renderBridgeCard(event, room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { @@ -45,90 +60,59 @@ export default class BridgeSettingsTab extends React.Component { let creator = null; if (content.creator) { - creator =

{ _t("This bridge was provisioned by ", {}, { + creator = _t("This bridge was provisioned by .", {}, { user: , - })}

; + }); } - const bot = (

{_t("This bridge is managed by .", {}, { + const bot = _t("This bridge is managed by .", {}, { user: , - })}

); - let channelLink = channelName; - if (channel.external_url) { - channelLink = {channelName}; - } - - let networkLink = networkName; - if (network && network.external_url) { - networkLink = {networkName}; - } - - const chanAndNetworkInfo = ( - _t("Bridged into , on ", {}, { - channelLink, - networkLink, - protocolName, - }) - ); - - let networkIcon = null; - if (networkName && network.avatar) { - const avatarUrl = getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - network.avatar, 32, 32, "crop", - ); - networkIcon = ; - } - - let channelIcon = null; - if (channel.avatar) { - const avatarUrl = getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - channel.avatar, 32, 32, "crop", - ); - channelIcon = ; - } - - const heading = _t("Connected to on ", { }, { - channelIcon, - channelName, - networkName, - networkIcon, }); - return (
  • -
    -

    {heading}

    -

    {_t("Connected via %(protocolName)s", { protocolName })}

    -
    - {creator} - {bot} -

    {chanAndNetworkInfo}

    -
    + const avatarUrl = network.avatar ? getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + network.avatar, 32, 32, "crop", + ) : null; + + const networkIcon = ; + + const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { + networkName, + channelName, + }); + const id = event.getId(); + const isVisible = this.state.showMoreCard === id; + const metadataClassname = "metadata " + (isVisible ? "visible" : ""); + return (
  • +
    + {networkIcon} +
    +
    +

    {protocolName}

    +

    + {workspaceChannelDetails} +

    +

    + {creator} {bot} +

    + this._showMoreDetails(isVisible ? null : id)}>Show { isVisible ? "less" : "more" }
  • ); } @@ -151,14 +135,40 @@ export default class BridgeSettingsTab extends React.Component { const client = MatrixClientPeg.get(); const room = client.getRoom(this.props.roomId); + let content = null; + + if (bridgeEvents.length > 0) { + content =
    +

    {_t( + "This room is bridging messages to the following platforms. " + + "Learn more.", {}, + { + // TODO: We don't have this link yet: this will prevent the translators + // having to re-translate the string when we do. + a: sub => '', + }, + )}

    +
      + { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } +
    +
    + } else { + content =

    {_t( + "This room isn’t bridging messages to any platforms. " + + "Learn more.", {}, + { + // TODO: We don't have this link yet: this will prevent the translators + // having to re-translate the string when we do. + a: sub => '', + }, + )}

    + } + return (
    -
    {_t("Bridge Info")}
    +
    {_t("Bridges")}
    -

    { _t("Below is a list of bridges connected to this room.") }

    -
      - { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } -
    + {content}
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f0eab6b12d..e4ab764989 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -781,13 +781,12 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", - "This bridge was provisioned by ": "This bridge was provisioned by ", + "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", - "Bridged into , on ": "Bridged into , on ", - "Connected to on ": "Connected to on ", - "Connected via %(protocolName)s": "Connected via %(protocolName)s", - "Bridge Info": "Bridge Info", - "Below is a list of bridges connected to this room.": "Below is a list of bridges connected to this room.", + "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "This room is bridging messages to the following platforms. Learn more.": "This room is bridging messages to the following platforms. Learn more.", + "This room isn’t bridging messages to any platforms. Learn more.": "This room isn’t bridging messages to any platforms. Learn more.", + "Bridges": "Bridges", "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", From 4627e3b2825c5b547d1ccfe249ac74b75d5497d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 11:02:44 +0100 Subject: [PATCH 21/89] EventIndex: Refactor out the addInitialCheckpoints method. --- src/indexing/EventIndex.js | 94 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 435f67447d..09abb5d209 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -70,62 +70,62 @@ export default class EventIndex { client.removeListener('Room.timelineReset', this.onTimelineReset); } + /** Get crawler checkpoints for the encrypted rooms and store them in the index. + */ + async addInitialCheckpoints() { + const indexManager = PlatformPeg.get().getEventIndexingManager(); + const client = MatrixClientPeg.get(); + const rooms = client.getRooms(); + + const isRoomEncrypted = (room) => { + return client.isRoomEncrypted(room.roomId); + }; + + // We only care to crawl the encrypted rooms, non-encrypted. + // rooms can use the search provided by the homeserver. + const encryptedRooms = rooms.filter(isRoomEncrypted); + + console.log("EventIndex: Adding initial crawler checkpoints"); + + // Gather the prev_batch tokens and create checkpoints for + // our message crawler. + await Promise.all(encryptedRooms.map(async (room) => { + const timeline = room.getLiveTimeline(); + const token = timeline.getPaginationToken("b"); + + console.log("EventIndex: Got token for indexer", + room.roomId, token); + + const backCheckpoint = { + roomId: room.roomId, + token: token, + direction: "b", + }; + + const forwardCheckpoint = { + roomId: room.roomId, + token: token, + direction: "f", + }; + + await indexManager.addCrawlerCheckpoint(backCheckpoint); + await indexManager.addCrawlerCheckpoint(forwardCheckpoint); + this.crawlerCheckpoints.push(backCheckpoint); + this.crawlerCheckpoints.push(forwardCheckpoint); + })); + } + onSync = async (state, prevState, data) => { const indexManager = PlatformPeg.get().getEventIndexingManager(); if (prevState === "PREPARED" && state === "SYNCING") { - const addInitialCheckpoints = async () => { - const client = MatrixClientPeg.get(); - const rooms = client.getRooms(); - - const isRoomEncrypted = (room) => { - return client.isRoomEncrypted(room.roomId); - }; - - // We only care to crawl the encrypted rooms, non-encrypted. - // rooms can use the search provided by the homeserver. - const encryptedRooms = rooms.filter(isRoomEncrypted); - - console.log("EventIndex: Adding initial crawler checkpoints"); - - // Gather the prev_batch tokens and create checkpoints for - // our message crawler. - await Promise.all(encryptedRooms.map(async (room) => { - const timeline = room.getLiveTimeline(); - const token = timeline.getPaginationToken("b"); - - console.log("EventIndex: Got token for indexer", - room.roomId, token); - - const backCheckpoint = { - roomId: room.roomId, - token: token, - direction: "b", - }; - - const forwardCheckpoint = { - roomId: room.roomId, - token: token, - direction: "f", - }; - - await indexManager.addCrawlerCheckpoint(backCheckpoint); - await indexManager.addCrawlerCheckpoint(forwardCheckpoint); - this.crawlerCheckpoints.push(backCheckpoint); - this.crawlerCheckpoints.push(forwardCheckpoint); - })); - }; - // If our indexer is empty we're most likely running Riot the // first time with indexing support or running it with an // initial sync. Add checkpoints to crawl our encrypted rooms. const eventIndexWasEmpty = await indexManager.isEventIndexEmpty(); - if (eventIndexWasEmpty) await addInitialCheckpoints(); + if (eventIndexWasEmpty) await this.addInitialCheckpoints(); - // Start our crawler. - if (SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling')) { - this.startCrawler(); - } + this.startCrawler(); return; } From 5fd121d2afdf7b18ebff311b2221539a492fd20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 11:44:56 +0100 Subject: [PATCH 22/89] ManageEventIndex: Remove the unused stats. --- .../dialogs/eventindex/ManageEventIndex.js | 10 ------- src/indexing/EventIndex.js | 27 ------------------- 2 files changed, 37 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 8623856b2e..d9a0cdcb5d 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -41,8 +41,6 @@ export default class ManageEventIndex extends React.Component { this.state = { eventIndexSize: 0, - crawlingRooms: 0, - totalCrawlingRooms: 0, eventCount: 0, roomCount: 0, currentRoom: null, @@ -80,8 +78,6 @@ export default class ManageEventIndex extends React.Component { let eventIndexSize = 0; let roomCount = 0; let eventCount = 0; - let crawlingRooms = 0; - let totalCrawlingRooms = 0; let currentRoom = null; const eventIndex = EventIndexPeg.get(); @@ -94,18 +90,12 @@ export default class ManageEventIndex extends React.Component { roomCount = stats.roomCount; eventCount = stats.eventCount; - const crawledRooms = eventIndex.currentlyCrawledRooms(); - crawlingRooms = crawledRooms.crawlingRooms.size; - totalCrawlingRooms = crawledRooms.totalRooms.size; - const room = eventIndex.currentRoom(); if (room) currentRoom = room.name; } this.setState({ eventIndexSize, - crawlingRooms, - totalCrawlingRooms, eventCount, roomCount, currentRoom, diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 09abb5d209..a10f4aff71 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -440,33 +440,6 @@ export default class EventIndex { return indexManager.getStats(); } - currentlyCrawledRooms() { - const crawlingRooms = new Set(); - const totalRooms = new Set(); - - this.crawlerCheckpoints.forEach((checkpoint, index) => { - crawlingRooms.add(checkpoint.roomId); - }); - - if (this._currentCheckpoint !== null) { - crawlingRooms.add(this._currentCheckpoint.roomId); - } - - const client = MatrixClientPeg.get(); - const rooms = client.getRooms(); - - const isRoomEncrypted = (room) => { - return client.isRoomEncrypted(room.roomId); - }; - - const encryptedRooms = rooms.filter(isRoomEncrypted); - encryptedRooms.forEach((room, index) => { - totalRooms.add(room.roomId); - }); - - return {crawlingRooms, totalRooms}; - } - /** * Get the room that we are currently crawling. * From 0d545ed3359a665860354e750c114bffbf36b425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 11:47:49 +0100 Subject: [PATCH 23/89] EventIndexPeg: Small refactor and change the init logic. This changes the way the event index is initialized, if it's disabled in the settings it will not be initialized at all, before only the crawler loop was not being started. --- src/indexing/EventIndexPeg.js | 57 +++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/src/indexing/EventIndexPeg.js b/src/indexing/EventIndexPeg.js index a63756ab4e..c8c8cbefe9 100644 --- a/src/indexing/EventIndexPeg.js +++ b/src/indexing/EventIndexPeg.js @@ -21,17 +21,19 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import EventIndex from "../indexing/EventIndex"; -import SettingsStore from '../settings/SettingsStore'; +import SettingsStore, {SettingLevel} from '../settings/SettingsStore'; class EventIndexPeg { constructor() { this.index = null; + this._supportIsInstalled = false; } /** - * Create a new EventIndex and initialize it if the platform supports it. + * Initialize the EventIndexPeg and if event indexing is enabled initialize + * the event index. * - * @return {Promise} A promise that will resolve to true if an + * @return {Promise} A promise that will resolve to true if an * EventIndex was successfully initialized, false otherwise. */ async init() { @@ -40,12 +42,32 @@ class EventIndexPeg { } const indexManager = PlatformPeg.get().getEventIndexingManager(); - if (!indexManager || await indexManager.supportsEventIndexing() !== true) { - console.log("EventIndex: Platform doesn't support event indexing,", - "not initializing."); + if (!indexManager) { + console.log("EventIndex: Platform doesn't support event indexing, not initializing."); return false; } + this._supportIsInstalled = await indexManager.supportsEventIndexing(); + + if (!this.supportIsInstalled()) { + console.log("EventIndex: Event indexing isn't installed for the platform, not initializing."); + return false; + } + + if (!SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing')) { + console.log("EventIndex: Event indexing is disabled, not initializing"); + return false; + } + + return this.initEventIndex(); + } + + /* Initialize the event index. + * + * @returns {boolean} True if the event index was succesfully initialized, + * false otherwise. + */ + async initEventIndex() { const index = new EventIndex(); try { @@ -60,6 +82,29 @@ class EventIndexPeg { return true; } + /** + * Check if the current platform has support for event indexing. + * + * @return {boolean} True if it has support, false otherwise. Note that this + * does not mean that support is installed. + */ + platformHasSupport(): boolean { + return PlatformPeg.get().getEventIndexingManager() !== null; + } + + /** + * Check if event indexing support is installed for the platfrom. + * + * Event indexing might require additional optional modules to be installed, + * this tells us if those are installed. Note that this should only be + * called after the init() method was called. + * + * @return {boolean} True if support is installed, false otherwise. + */ + supportIsInstalled(): boolean { + return this._supportIsInstalled; + } + /** * Get the current event index. * From 9bee024da71b7faa06b3c1cec379b0c692d9b895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 12:24:06 +0100 Subject: [PATCH 24/89] ManageEventIndex: Remove some useless divs and add the enable case. --- .../dialogs/eventindex/ManageEventIndex.js | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index d9a0cdcb5d..754d1b8516 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -116,7 +116,11 @@ export default class ManageEventIndex extends React.Component { SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); } - _onDisable = () => { + _onDisable = async () => { + this.props.onFinished(false); + } + + _onEnable = async () => { this.props.onFinished(false); } @@ -126,17 +130,16 @@ export default class ManageEventIndex extends React.Component { render() { let eventIndexingSettings = null; + let buttons; let crawlerState; if (!this.state.eventIndexingEnabled) { - crawlerState =
    {_t("Message search for encrypted rooms is disabled.")}
    ; + crawlerState = _t("Message search for encrypted rooms is disabled."); } else if (this.state.currentRoom === null) { - crawlerState =
    {_t("Not downloading messages for any room.")}
    ; + crawlerState = _t("Not downloading messages for any room."); } else { crawlerState = ( -
    - {_t("Downloading mesages for %(currentRoom)s.", { currentRoom: this.state.currentRoom })} -
    + _t("Downloading mesages for %(currentRoom)s.", { currentRoom: this.state.currentRoom }) ); } @@ -154,18 +157,30 @@ export default class ManageEventIndex extends React.Component { {_t("Number of rooms:")} {this.state.roomCount}
    {crawlerState}
    +
    + ); - - - + buttons = ( +
    + + {_t("Disable")} + + + {_t("Done")} + +
    + ); + } else if (!this.state.eventIndexingEnabled && this.state.eventIndexingInstalled) { + eventIndexingSettings = ( +
    + {_t( "Securely cache encrypted messages locally for them to appear in search results.")} +
    + ); + buttons = ( +
    + + {_t("Enable")} +
    ); } else { @@ -182,28 +197,14 @@ export default class ManageEventIndex extends React.Component { } const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const buttons =
    -
    - - {_t("Disable")} - - - {_t("Done")} - -
    -
    ; return ( {}} + onFinished={this.props.onFinished} title={_t("Message search")} > -
    - {eventIndexingSettings} -
    -
    - {buttons} -
    + {eventIndexingSettings} + {buttons}
    ); } From 4953f32cbae89967bfbea894a8d25740a3b7124a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 12:24:54 +0100 Subject: [PATCH 25/89] ManageEventIndex: Rename the enable crawler setting. --- .../views/dialogs/eventindex/ManageEventIndex.js | 2 +- src/settings/Settings.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 754d1b8516..76e41fe4b8 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -45,7 +45,7 @@ export default class ManageEventIndex extends React.Component { roomCount: 0, currentRoom: null, eventIndexingEnabled: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableCrawling'), + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), crawlerSleepTime: SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), }; diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 817adcfc4d..68b26dbae1 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -491,9 +491,9 @@ export const SETTINGS = { displayName: _td("How long should the crawler wait between requests"), default: 3000, }, - "enableCrawling": { + "enableEventIndexing": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, - displayName: _td("How long should the crawler wait between requests"), + displayName: _td("Enable message search in encrypted rooms"), default: true, }, }; From 947ea9823d6bb22ea57ed854e4e5ace81673bb0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 13:32:27 +0100 Subject: [PATCH 26/89] Settings: Remove the crawler sleep time setting. --- .../views/dialogs/eventindex/ManageEventIndex.js | 16 ---------------- src/indexing/EventIndex.js | 3 ++- src/settings/Settings.js | 5 ----- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 76e41fe4b8..be7df25381 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -46,8 +46,6 @@ export default class ManageEventIndex extends React.Component { currentRoom: null, eventIndexingEnabled: SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), - crawlerSleepTime: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'), }; } @@ -102,20 +100,6 @@ export default class ManageEventIndex extends React.Component { }); } - _onEventIndexingEnabledChange = (checked) => { - SettingsStore.setValue("enableCrawling", null, SettingLevel.DEVICE, checked); - - if (checked) EventIndexPeg.start(); - else EventIndexPeg.stop(); - - this.setState({eventIndexingEnabled: checked}); - } - - _onCrawlerSleepTimeChange = (e) => { - this.setState({crawlerSleepTime: e.target.value}); - SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); - } - _onDisable = async () => { this.props.onFinished(false); } diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index a10f4aff71..6d38f1683f 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -30,6 +30,7 @@ export default class EventIndex { // The time in ms that the crawler will wait loop iterations if there // have not been any checkpoints to consume in the last iteration. this._crawlerIdleTime = 5000; + this._crawlerSleepTime = 3000; // The maximum number of events our crawler should fetch in a single // crawl. this._eventsPerCrawl = 100; @@ -211,7 +212,7 @@ export default class EventIndex { // This is a low priority task and we don't want to spam our // homeserver with /messages requests so we set a hefty timeout // here. - let sleepTime = SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'); + let sleepTime = this._crawlerSleepTime; // Don't let the user configure a lower sleep time than 100 ms. sleepTime = Math.max(sleepTime, 100); diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 68b26dbae1..44f38f232f 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -486,11 +486,6 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: RIGHT_PANEL_PHASES.GroupMemberList, }, - "crawlerSleepTime": { - supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, - displayName: _td("How long should the crawler wait between requests"), - default: 3000, - }, "enableEventIndexing": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("Enable message search in encrypted rooms"), From 4aa0658ac8e36f671fc6d715091cfc392e311ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 13:33:09 +0100 Subject: [PATCH 27/89] SecurityUserSettingsTab: Put the event index settings behind the feature flag. --- .../settings/tabs/user/SecurityUserSettingsTab.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 7b22dd15e2..eb5f346714 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -254,12 +254,15 @@ export default class SecurityUserSettingsTab extends React.Component {
    ); - const eventIndex = ( -
    - {_t("Message search")} - -
    - ); + let eventIndex; + if (SettingsStore.isFeatureEnabled("feature_event_indexing")) { + eventIndex = ( +
    + {_t("Message search")} + +
    + ); + } // XXX: There's no such panel in the current cross-signing designs, but // it's useful to have for testing the feature. If there's no interest From 64c4ad2eb9474dbacd681954682f677fb97d71cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 13:33:55 +0100 Subject: [PATCH 28/89] ManageEventIndex: Hook up the disable event index button. --- .../dialogs/eventindex/DisableEventIndex.js | 74 +++++++++++++++++++ .../dialogs/eventindex/ManageEventIndex.js | 6 +- src/i18n/strings/en_EN.json | 8 +- 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/async-components/views/dialogs/eventindex/DisableEventIndex.js diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js new file mode 100644 index 0000000000..159f8b3d95 --- /dev/null +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -0,0 +1,74 @@ +/* +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. +*/ + +import React from 'react'; +import * as sdk from '../../../../index'; +import PropTypes from 'prop-types'; +import { _t } from '../../../../languageHandler'; + +import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; +import LabelledToggleSwitch from "../../../../components/views/elements/LabelledToggleSwitch"; +import Field from "../../../../components/views/elements/Field"; +import {formatBytes} from "../../../../utils/FormattingUtils"; +import EventIndexPeg from "../../../../indexing/EventIndexPeg"; +import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; + + +/* + * Walks the user through the process of creating an e2e key backup + * on the server. + */ +export default class ManageEventIndex extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + } + + constructor(props) { + super(props); + + this.state = { + eventIndexingEnabled: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), + }; + } + + _onDisable = async () => { + const eventIndex = EventIndexPeg.get(); + await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); + await EventIndexPeg.deleteEventIndex(); + this.props.onFinished(true); + } + + render() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); + + return ( + + {_t("If disabled, messages form encrypted rooms won't appear in search results")} + + + ); + } +} diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index be7df25381..cf0e2bccc6 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -19,6 +19,7 @@ import * as sdk from '../../../../index'; import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; +import Modal from '../../../../Modal'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import LabelledToggleSwitch from "../../../../components/views/elements/LabelledToggleSwitch"; import Field from "../../../../components/views/elements/Field"; @@ -101,7 +102,10 @@ export default class ManageEventIndex extends React.Component { } _onDisable = async () => { - this.props.onFinished(false); + Modal.createTrackedDialogAsync("Disable message search", "Disable message search", + import("./DisableEventIndex"), + null, null, /* priority = */ false, /* static = */ true, + ); } _onEnable = async () => { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b2ac55ba04..31ba5581f3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -416,6 +416,7 @@ "Send read receipts for messages (requires compatible homeserver to disable)": "Send read receipts for messages (requires compatible homeserver to disable)", "Show previews/thumbnails for images": "Show previews/thumbnails for images", "How long should the crawler wait between requests": "How long should the crawler wait between requests", + "Enable message search in encrypted rooms": "Enable message search in encrypted rooms", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", @@ -2035,6 +2036,8 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "This device has detected that your recovery passphrase and key for Secure Messages have been removed.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", + "If disabled, messages form encrypted rooms won't appear in search results": "If disabled, messages form encrypted rooms won't appear in search results", + "Disable": "Disable", "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", "Not downloading messages for any room.": "Not downloading messages for any room.", "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", @@ -2042,9 +2045,8 @@ "Space used:": "Space used:", "Indexed messages:": "Indexed messages:", "Number of rooms:": "Number of rooms:", - "Download and index encrypted messages": "Download and index encrypted messages", - "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", - "Disable": "Disable", + "Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.", + "Enable": "Enable", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" From c251031dfbf2d432e6818f4c70c0f228f9a15eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 14:25:47 +0100 Subject: [PATCH 29/89] DisableEventIndex: Return back to the user settings after disabling. --- .../views/dialogs/eventindex/DisableEventIndex.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js index 159f8b3d95..3dcbeab454 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import * as sdk from '../../../../index'; import PropTypes from 'prop-types'; +import dis from "../../../../dispatcher"; import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; @@ -49,7 +50,8 @@ export default class ManageEventIndex extends React.Component { const eventIndex = EventIndexPeg.get(); await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); - this.props.onFinished(true); + this.props.onFinished(); + dis.dispatch({ action: 'view_user_settings' }); } render() { From 5ac37c8694f8f42c6cadef7225195b35eaea5cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 14:26:35 +0100 Subject: [PATCH 30/89] ManageEventIndex: Remove the enable button, that one goes somewhere else. --- .../dialogs/eventindex/ManageEventIndex.js | 86 ++++++------------- 1 file changed, 25 insertions(+), 61 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index cf0e2bccc6..76fee228bf 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -45,8 +45,6 @@ export default class ManageEventIndex extends React.Component { eventCount: 0, roomCount: 0, currentRoom: null, - eventIndexingEnabled: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), }; } @@ -108,22 +106,14 @@ export default class ManageEventIndex extends React.Component { ); } - _onEnable = async () => { - this.props.onFinished(false); - } - _onDone = () => { this.props.onFinished(true); } render() { - let eventIndexingSettings = null; - let buttons; let crawlerState; - if (!this.state.eventIndexingEnabled) { - crawlerState = _t("Message search for encrypted rooms is disabled."); - } else if (this.state.currentRoom === null) { + if (this.state.currentRoom === null) { crawlerState = _t("Not downloading messages for any room."); } else { crawlerState = ( @@ -131,58 +121,32 @@ export default class ManageEventIndex extends React.Component { ); } - if (EventIndexPeg.get() !== null) { - eventIndexingSettings = ( -
    - { - _t( "Riot is securely caching encrypted messages locally for them " + - "to appear in search results:", - ) - } -
    - {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
    - {_t("Indexed messages:")} {this.state.eventCount}
    - {_t("Number of rooms:")} {this.state.roomCount}
    - {crawlerState}
    -
    + const eventIndexingSettings = ( +
    + { + _t( "Riot is securely caching encrypted messages locally for them " + + "to appear in search results:", + ) + } +
    + {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
    + {_t("Indexed messages:")} {this.state.eventCount}
    + {_t("Number of rooms:")} {this.state.roomCount}
    + {crawlerState}
    - ); +
    + ); - buttons = ( -
    - - {_t("Disable")} - - - {_t("Done")} - -
    - ); - } else if (!this.state.eventIndexingEnabled && this.state.eventIndexingInstalled) { - eventIndexingSettings = ( -
    - {_t( "Securely cache encrypted messages locally for them to appear in search results.")} -
    - ); - buttons = ( -
    - - {_t("Enable")} - -
    - ); - } else { - eventIndexingSettings = ( -
    - { - _t( "Riot can't securely cache encrypted messages locally" + - "while running in a web browser. Use Riot Desktop for" + - "encrypted messages to appear in search results.", - ) - } -
    - ); - } + const buttons = ( +
    + + {_t("Disable")} + + + {_t("Done")} + +
    + ); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); From 981acec0d2030efabec4cf4959aab352f0637c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 14:27:46 +0100 Subject: [PATCH 31/89] EventIndexPanel: Show the enable button if event indexing is disabled. --- .../views/settings/EventIndexPanel.js | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 8ed4b114e7..7cd0dbe753 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -18,6 +18,7 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; +import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; import {formatBytes} from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; @@ -29,6 +30,8 @@ export default class EventIndexPanel extends React.Component { this.state = { eventIndexSize: 0, roomCount: 0, + eventIndexingEnabled: + SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), }; } @@ -51,10 +54,15 @@ export default class EventIndexPanel extends React.Component { } async componentWillMount(): void { + this.updateState(); + } + + async updateState() { let eventIndexSize = 0; let roomCount = 0; const eventIndex = EventIndexPeg.get(); + const eventIndexingEnabled = SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'); if (eventIndex !== null) { eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this)); @@ -67,6 +75,7 @@ export default class EventIndexPanel extends React.Component { this.setState({ eventIndexSize, roomCount, + eventIndexingEnabled, }); } @@ -79,6 +88,14 @@ export default class EventIndexPanel extends React.Component { ); } + _onEnable = async () => { + await EventIndexPeg.initEventIndex(); + await EventIndexPeg.get().addInitialCheckpoints(); + await EventIndexPeg.get().startCrawler(); + await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, true); + await this.updateState(); + } + render() { let eventIndexingSettings = null; @@ -98,12 +115,25 @@ export default class EventIndexPanel extends React.Component {
    ); + } else if (!this.state.eventIndexingEnabled && EventIndexPeg.supportIsInstalled()) { + eventIndexingSettings = ( +
    +
    + {_t( "Securely cache encrypted messages locally for them to appear in search results.")} +
    +
    + + {_t("Enable")} + +
    +
    + ); } else { eventIndexingSettings = (
    { - _t( "Riot can't securely cache encrypted messages locally" + - "while running in a web browser. Use Riot Desktop for" + + _t( "Riot can't securely cache encrypted messages locally " + + "while running in a web browser. Use Riot Desktop for " + "encrypted messages to appear in search results.", ) } From 3073ce55883dc96b2ed88ad1e586920a4fd2f4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 15:05:40 +0100 Subject: [PATCH 32/89] DisableEventIndex: Set the correct button kind and add a spinner. --- .../dialogs/eventindex/DisableEventIndex.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js index 3dcbeab454..6d0f9a43e8 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -41,12 +41,15 @@ export default class ManageEventIndex extends React.Component { super(props); this.state = { - eventIndexingEnabled: - SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'), + disabling: false, }; } _onDisable = async () => { + this.setState({ + disabling: true, + }); + const eventIndex = EventIndexPeg.get(); await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); @@ -64,12 +67,15 @@ export default class ManageEventIndex extends React.Component { title={_t("Are you sure?")} > {_t("If disabled, messages form encrypted rooms won't appear in search results")} - +
    + + {_t("Cancel")} + + + {_t("Disable")} + + {this.state.enabling ? :
    } +
    ); } From 251661388a9dee3d3f9ce394820ebb3fbd607513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 15:06:10 +0100 Subject: [PATCH 33/89] ManageEventIndex: Set the button kind to danger. --- .../views/dialogs/eventindex/ManageEventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 76fee228bf..b53b4810aa 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -139,7 +139,7 @@ export default class ManageEventIndex extends React.Component { const buttons = (
    - + {_t("Disable")} From a5a149933a0dd98e7ca86d08a382a9ff6b0cc48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 15:06:38 +0100 Subject: [PATCH 34/89] EventIndexPanel: Add a spinner when the index is being enabled. --- .../views/settings/EventIndexPanel.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 7cd0dbe753..f93ab489c7 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import { _t } from '../../../languageHandler'; +import * as sdk from '../../../index'; import Modal from '../../../Modal'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; @@ -28,6 +29,7 @@ export default class EventIndexPanel extends React.Component { super(); this.state = { + enabling: false, eventIndexSize: 0, roomCount: 0, eventIndexingEnabled: @@ -58,11 +60,12 @@ export default class EventIndexPanel extends React.Component { } async updateState() { - let eventIndexSize = 0; - let roomCount = 0; - const eventIndex = EventIndexPeg.get(); const eventIndexingEnabled = SettingsStore.getValueAt(SettingLevel.DEVICE, 'enableEventIndexing'); + const enabling = false; + + let eventIndexSize = 0; + let roomCount = 0; if (eventIndex !== null) { eventIndex.on("changedCheckpoint", this.updateCurrentRoom.bind(this)); @@ -73,6 +76,7 @@ export default class EventIndexPanel extends React.Component { } this.setState({ + enabling, eventIndexSize, roomCount, eventIndexingEnabled, @@ -89,6 +93,10 @@ export default class EventIndexPanel extends React.Component { } _onEnable = async () => { + this.setState({ + enabling: true, + }); + await EventIndexPeg.initEventIndex(); await EventIndexPeg.get().addInitialCheckpoints(); await EventIndexPeg.get().startCrawler(); @@ -98,6 +106,7 @@ export default class EventIndexPanel extends React.Component { render() { let eventIndexingSettings = null; + const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); if (EventIndexPeg.get() !== null) { eventIndexingSettings = ( @@ -119,12 +128,15 @@ export default class EventIndexPanel extends React.Component { eventIndexingSettings = (
    - {_t( "Securely cache encrypted messages locally for them to appear in search results.")} + {_t( "Securely cache encrypted messages locally for them to " + + "appear in search results.")}
    - + {_t("Enable")} + {this.state.enabling ? :
    }
    ); From 381fe95f67919457cd1cdfa75ba7dd895dd69b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 23 Jan 2020 15:22:26 +0100 Subject: [PATCH 35/89] EventIndex: Fix some lint errors. --- .../views/dialogs/eventindex/DisableEventIndex.js | 6 +----- .../views/dialogs/eventindex/ManageEventIndex.js | 3 --- src/indexing/EventIndex.js | 2 -- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js index 6d0f9a43e8..064f55edc0 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -21,9 +21,6 @@ import dis from "../../../../dispatcher"; import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; -import LabelledToggleSwitch from "../../../../components/views/elements/LabelledToggleSwitch"; -import Field from "../../../../components/views/elements/Field"; -import {formatBytes} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; @@ -50,7 +47,6 @@ export default class ManageEventIndex extends React.Component { disabling: true, }); - const eventIndex = EventIndexPeg.get(); await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); this.props.onFinished(); @@ -59,7 +55,7 @@ export default class ManageEventIndex extends React.Component { render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); + const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); return ( Date: Thu, 23 Jan 2020 15:32:43 +0100 Subject: [PATCH 36/89] Update the translation file. --- src/i18n/strings/en_EN.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 31ba5581f3..3973ae442e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -415,7 +415,6 @@ "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)", "Send read receipts for messages (requires compatible homeserver to disable)": "Send read receipts for messages (requires compatible homeserver to disable)", "Show previews/thumbnails for images": "Show previews/thumbnails for images", - "How long should the crawler wait between requests": "How long should the crawler wait between requests", "Enable message search in encrypted rooms": "Enable message search in encrypted rooms", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", @@ -559,7 +558,9 @@ " to store messages from ": " to store messages from ", "rooms.": "rooms.", "Manage": "Manage", - "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locallywhile running in a web browser. Use Riot Desktop forencrypted messages to appear in search results.", + "Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.", + "Enable": "Enable", + "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", @@ -2038,15 +2039,12 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "If disabled, messages form encrypted rooms won't appear in search results": "If disabled, messages form encrypted rooms won't appear in search results", "Disable": "Disable", - "Message search for encrypted rooms is disabled.": "Message search for encrypted rooms is disabled.", "Not downloading messages for any room.": "Not downloading messages for any room.", "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", "Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot is securely caching encrypted messages locally for them to appear in search results:", "Space used:": "Space used:", "Indexed messages:": "Indexed messages:", "Number of rooms:": "Number of rooms:", - "Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.", - "Enable": "Enable", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" From 86a098fcd983d546b18f1a1eba9e2d1e1f1e6f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:35:31 +0100 Subject: [PATCH 37/89] DisableEventIndex: Remove a blank line and rewrite a doc comment. --- .../views/dialogs/eventindex/DisableEventIndex.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js index 064f55edc0..5720c26e40 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -24,10 +24,8 @@ import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; - /* - * Walks the user through the process of creating an e2e key backup - * on the server. + * Allows the user to disable the Event Index. */ export default class ManageEventIndex extends React.Component { static propTypes = { From d30fd3eac04ecedd1bbd8dda8b0a38b49f77444d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:39:56 +0100 Subject: [PATCH 38/89] DisableEventIndex: Rename the class. --- .../views/dialogs/eventindex/DisableEventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js index 5720c26e40..820bcbe047 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndex.js @@ -27,7 +27,7 @@ import AccessibleButton from "../../../../components/views/elements/AccessibleBu /* * Allows the user to disable the Event Index. */ -export default class ManageEventIndex extends React.Component { +export default class DisableEventIndexDialog extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, } From 4ea2d4f90eff09481a75366740274cecb5326b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:45:29 +0100 Subject: [PATCH 39/89] ManageEventIndex: Rewrite the docs and rename the dialog class. --- .../views/dialogs/eventindex/ManageEventIndex.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index d0f3c2cf50..5d75f9b168 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -24,12 +24,10 @@ import {formatBytes} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; - /* - * Walks the user through the process of creating an e2e key backup - * on the server. + * Allows the user to introspect the event index state and disable it. */ -export default class ManageEventIndex extends React.Component { +export default class ManageEventIndexDialog extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, } From f763ae3c7bec35d78097249e58859cb55bfaadda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:50:58 +0100 Subject: [PATCH 40/89] DisableEventIndex: Rename the file to contain the Dialog suffix. --- .../{DisableEventIndex.js => DisableEventIndexDialog.js} | 0 .../views/dialogs/eventindex/ManageEventIndex.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/async-components/views/dialogs/eventindex/{DisableEventIndex.js => DisableEventIndexDialog.js} (100%) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndex.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js similarity index 100% rename from src/async-components/views/dialogs/eventindex/DisableEventIndex.js rename to src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js index 5d75f9b168..c9c4fc8ae6 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndex.js @@ -96,7 +96,7 @@ export default class ManageEventIndexDialog extends React.Component { _onDisable = async () => { Modal.createTrackedDialogAsync("Disable message search", "Disable message search", - import("./DisableEventIndex"), + import("./DisableEventIndexDialog"), null, null, /* priority = */ false, /* static = */ true, ); } From 93facca47963ce17ae706f80150333a98ff4dfbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:54:46 +0100 Subject: [PATCH 41/89] ManageEventIndex: Rename the file to contain the Dialog suffix. --- .../{ManageEventIndex.js => ManageEventIndexDialog.js} | 0 src/components/views/settings/EventIndexPanel.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/async-components/views/dialogs/eventindex/{ManageEventIndex.js => ManageEventIndexDialog.js} (100%) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndex.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js similarity index 100% rename from src/async-components/views/dialogs/eventindex/ManageEventIndex.js rename to src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index f93ab489c7..785ce2a4ca 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -85,7 +85,7 @@ export default class EventIndexPanel extends React.Component { _onManage = async () => { Modal.createTrackedDialogAsync('Message search', 'Message search', - import('../../../async-components/views/dialogs/eventindex/ManageEventIndex'), + import('../../../async-components/views/dialogs/eventindex/ManageEventIndexDialog'), { onFinished: () => {}, }, null, /* priority = */ false, /* static = */ true, From b59863781f703997058ab8bd4af9acab349d8e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:58:17 +0100 Subject: [PATCH 42/89] DisableEventIndexDialog: Fix a typo. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 820bcbe047..7f46b54f65 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -60,7 +60,7 @@ export default class DisableEventIndexDialog extends React.Component { onFinished={this.props.onFinished} title={_t("Are you sure?")} > - {_t("If disabled, messages form encrypted rooms won't appear in search results")} + {_t("If disabled, messages from encrypted rooms won't appear in search results.")}
    {_t("Cancel")} From 72a58d0c2c274ee6b7a874205e3a8ee13ade4f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 09:59:26 +0100 Subject: [PATCH 43/89] DisableEventIndexDialog: Properly indent the content of the BaseDialog. --- .../eventindex/DisableEventIndexDialog.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 7f46b54f65..7097ffb3bf 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -60,16 +60,16 @@ export default class DisableEventIndexDialog extends React.Component { onFinished={this.props.onFinished} title={_t("Are you sure?")} > - {_t("If disabled, messages from encrypted rooms won't appear in search results.")} -
    - - {_t("Cancel")} - - - {_t("Disable")} - - {this.state.enabling ? :
    } -
    + {_t("If disabled, messages from encrypted rooms won't appear in search results.")} +
    + + {_t("Cancel")} + + + {_t("Disable")} + + {this.state.enabling ? :
    } +
    ); } From cba7764784e1b60365cfcac0ea8f5ea3310dcaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:00:28 +0100 Subject: [PATCH 44/89] DisableEventIndexDialog: Use the DialogButtons element for the buttons. --- .../eventindex/DisableEventIndexDialog.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 7097ffb3bf..2e0f97c705 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -53,7 +53,8 @@ export default class DisableEventIndexDialog extends React.Component { render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); + const Spinner = sdk.getComponent('elements.Spinner'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return ( {_t("If disabled, messages from encrypted rooms won't appear in search results.")} -
    - - {_t("Cancel")} - - - {_t("Disable")} - - {this.state.enabling ? :
    } -
    + {this.state.disabling ? :
    } + + ); } From 71024d14188cb70965eb69b64c7f8fcf384a39eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:03:40 +0100 Subject: [PATCH 45/89] ManageEventIndexDialog: Properly indent the content of the BaseDialog. --- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index c9c4fc8ae6..f435cf050f 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -150,8 +150,8 @@ export default class ManageEventIndexDialog extends React.Component { onFinished={this.props.onFinished} title={_t("Message search")} > - {eventIndexingSettings} - {buttons} + {eventIndexingSettings} + {buttons} ); } From 128c0b73004e27cb6d5ff16da01f48a99785d72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:11:53 +0100 Subject: [PATCH 46/89] ManageEventIndexDialog: Use formatCount to format the message and room count. --- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index f435cf050f..a8fc7dce3a 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; import Modal from '../../../../Modal'; -import {formatBytes} from "../../../../utils/FormattingUtils"; +import {formatBytes, formatCount} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; @@ -125,8 +125,8 @@ export default class ManageEventIndexDialog extends React.Component { }
    {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
    - {_t("Indexed messages:")} {this.state.eventCount}
    - {_t("Number of rooms:")} {this.state.roomCount}
    + {_t("Indexed messages:")} {formatCount(this.state.eventCount)}
    + {_t("Number of rooms:")} {formatCount(this.state.roomCount)}
    {crawlerState}
    From 660240e2c047a964f2556cd2d63145f15ddc963e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:13:09 +0100 Subject: [PATCH 47/89] EventIndexPanel: Use formatCount to format the room count. --- src/components/views/settings/EventIndexPanel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 785ce2a4ca..13063d9305 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -21,7 +21,7 @@ import * as sdk from '../../../index'; import Modal from '../../../Modal'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; -import {formatBytes} from "../../../utils/FormattingUtils"; +import {formatBytes, formatCount} from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; export default class EventIndexPanel extends React.Component { @@ -115,7 +115,7 @@ export default class EventIndexPanel extends React.Component { {_t( "Securely cache encrypted messages locally for them " + "to appear in search results, using ") } {formatBytes(this.state.eventIndexSize, 0)} - {_t( " to store messages from ")} {this.state.roomCount} {_t("rooms.")} + {_t( " to store messages from ")} {formatCount(this.state.roomCount)} {_t("rooms.")}
    From d9e933c915534456fead4641841e5b5ff9632a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:15:57 +0100 Subject: [PATCH 48/89] EventIndex: Style fixes for the docstrings. --- src/indexing/EventIndex.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index d466c6acba..53f47148b9 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -69,7 +69,8 @@ export default class EventIndex { client.removeListener('Room.timelineReset', this.onTimelineReset); } - /** Get crawler checkpoints for the encrypted rooms and store them in the index. + /** + * Get crawler checkpoints for the encrypted rooms and store them in the index. */ async addInitialCheckpoints() { const indexManager = PlatformPeg.get().getEventIndexingManager(); @@ -80,7 +81,7 @@ export default class EventIndex { return client.isRoomEncrypted(room.roomId); }; - // We only care to crawl the encrypted rooms, non-encrypted. + // We only care to crawl the encrypted rooms, non-encrypted // rooms can use the search provided by the homeserver. const encryptedRooms = rooms.filter(isRoomEncrypted); From 825b6f7b7d6848b4963dcbfc335c829779dfd40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:16:49 +0100 Subject: [PATCH 49/89] EventIndexPeg: Style fix for a docstring. --- src/indexing/EventIndexPeg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/indexing/EventIndexPeg.js b/src/indexing/EventIndexPeg.js index c8c8cbefe9..067aea3a7f 100644 --- a/src/indexing/EventIndexPeg.js +++ b/src/indexing/EventIndexPeg.js @@ -62,7 +62,8 @@ class EventIndexPeg { return this.initEventIndex(); } - /* Initialize the event index. + /** + * Initialize the event index. * * @returns {boolean} True if the event index was succesfully initialized, * false otherwise. From 6f919eaeec0aaf96bd5cea50e9a714f3b2c023d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:27:56 +0100 Subject: [PATCH 50/89] DisableEventIndexDialog: Use the correct spinner. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 2e0f97c705..f031c5a11f 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -62,7 +62,7 @@ export default class DisableEventIndexDialog extends React.Component { title={_t("Are you sure?")} > {_t("If disabled, messages from encrypted rooms won't appear in search results.")} - {this.state.disabling ? :
    } + {this.state.disabling ? :
    } Date: Fri, 24 Jan 2020 11:28:33 +0100 Subject: [PATCH 51/89] EventIndex: Subclass the event emitter instead of putting one in a property. --- src/indexing/EventIndex.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 53f47148b9..85e75cfc82 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -22,8 +22,9 @@ import {EventEmitter} from "events"; /* * Event indexing class that wraps the platform specific event indexing. */ -export default class EventIndex { +export default class EventIndex extends EventEmitter { constructor() { + super(); this.crawlerCheckpoints = []; // The time in ms that the crawler will wait loop iterations if there // have not been any checkpoints to consume in the last iteration. @@ -35,7 +36,6 @@ export default class EventIndex { this._crawler = null; this._currentCheckpoint = null; this.liveEventsForIndex = new Set(); - this._eventEmitter = new EventEmitter(); } async init() { @@ -188,7 +188,7 @@ export default class EventIndex { } emitNewCheckpoint() { - this._eventEmitter.emit("changedCheckpoint", this.currentRoom()); + this.emit("changedCheckpoint", this.currentRoom()); } async crawlerFunc() { @@ -459,12 +459,4 @@ export default class EventIndex { return client.getRoom(this.crawlerCheckpoints[0].roomId); } } - - on(eventName, callback) { - this._eventEmitter.on(eventName, callback); - } - - removeListener(eventName, callback) { - this._eventEmitter.removeListener(eventName, callback); - } } From ffe5d411db9a7ae02ead2f5e78a8741be08ec5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:44:56 +0100 Subject: [PATCH 52/89] EventIndexPanel: Add a link to the download page of Riot Desktop. --- src/components/views/settings/EventIndexPanel.js | 8 ++++++-- src/i18n/strings/en_EN.json | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 13063d9305..a6f21fa114 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -145,8 +145,12 @@ export default class EventIndexPanel extends React.Component {
    { _t( "Riot can't securely cache encrypted messages locally " + - "while running in a web browser. Use Riot Desktop for " + - "encrypted messages to appear in search results.", + "while running in a web browser. Use Riot Desktop " + + "for encrypted messages to appear in search results.", + {}, + { + 'riotLink': (sub) => {sub}, + } ) }
    diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3973ae442e..fceb299131 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -560,7 +560,7 @@ "Manage": "Manage", "Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.", "Enable": "Enable", - "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.", + "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.", @@ -2037,7 +2037,7 @@ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "This device has detected that your recovery passphrase and key for Secure Messages have been removed.", "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.", "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", - "If disabled, messages form encrypted rooms won't appear in search results": "If disabled, messages form encrypted rooms won't appear in search results", + "If disabled, messages from encrypted rooms won't appear in search results.": "If disabled, messages from encrypted rooms won't appear in search results.", "Disable": "Disable", "Not downloading messages for any room.": "Not downloading messages for any room.", "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", From 0c3d507455bbf77d956e2f6c303e08ef6d9d8c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:46:46 +0100 Subject: [PATCH 53/89] EventIndex: Cancel the crawler early after a message request. If we're cancelling the crawler nowadays this means that we're likely deleting the index. Processing these messages is wasted effort in that case so break early. --- src/indexing/EventIndex.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 85e75cfc82..723d516a29 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -266,6 +266,11 @@ export default class EventIndex extends EventEmitter { continue; } + if (cancelled) { + this.crawlerCheckpoints.push(checkpoint); + break; + } + if (res.chunk.length === 0) { console.log("EventIndex: Done with the checkpoint", checkpoint); // We got to the start/end of our timeline, lets just From ee133a9c715559e33c058c58ce8776574816fafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:56:19 +0100 Subject: [PATCH 54/89] DisableEventIndexDialog: Remove an unused import. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index f031c5a11f..c2b7e2933e 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -22,7 +22,6 @@ import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; -import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; /* * Allows the user to disable the Event Index. From 029369a04bd08f2fdb8944ff72be50e79050784a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 11:56:43 +0100 Subject: [PATCH 55/89] EventIndexPanel: Small style fix. --- src/components/views/settings/EventIndexPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index a6f21fa114..5edc25bbab 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -150,7 +150,7 @@ export default class EventIndexPanel extends React.Component { {}, { 'riotLink': (sub) => {sub}, - } + }, ) }
    From 97d55f63a362194ce5d9cc6d9bb92f78e1c63157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 15:25:27 +0100 Subject: [PATCH 56/89] DisableEventIndexDialog: Remove the incorrect class on the dialog. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index c2b7e2933e..81920eab7a 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -56,10 +56,7 @@ export default class DisableEventIndexDialog extends React.Component { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return ( - + {_t("If disabled, messages from encrypted rooms won't appear in search results.")} {this.state.disabling ? :
    } Date: Fri, 24 Jan 2020 15:26:24 +0100 Subject: [PATCH 57/89] DisableEventIndexDialog: Use a self-closing tag for the buttons. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 81920eab7a..13278217de 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -65,8 +65,7 @@ export default class DisableEventIndexDialog extends React.Component { primaryButtonClass="danger" onCancel={this.props.onFinished} disabled={this.state.disabling} - > - + /> ); } From 47999c2e468ea98ec5ea99c527b48b7ac54c3fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 15:26:54 +0100 Subject: [PATCH 58/89] EventIndexPanel: Add a separate message for the case where Seshat is missing. --- src/components/views/settings/EventIndexPanel.js | 16 ++++++++++++++++ src/i18n/strings/en_EN.json | 1 + 2 files changed, 17 insertions(+) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 5edc25bbab..321f45699b 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -140,6 +140,22 @@ export default class EventIndexPanel extends React.Component {
    ); + } else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) { + eventIndexingSettings = ( +
    + { + _t( "Riot is missing some components required for securely " + + "caching encrypted messages locally. If you'd like to " + + "experiment with this feature, build a custom Riot Desktop " + + "with search components added.", + {}, + { + 'nativeLink': (sub) => {sub}, + }, + ) + } +
    + ); } else { eventIndexingSettings = (
    diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fceb299131..419aecd528 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -560,6 +560,7 @@ "Manage": "Manage", "Securely cache encrypted messages locally for them to appear in search results.": "Securely cache encrypted messages locally for them to appear in search results.", "Enable": "Enable", + "Riot is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom Riot Desktop with search components added.": "Riot is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom Riot Desktop with search components added.", "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.": "Riot can't securely cache encrypted messages locally while running in a web browser. Use Riot Desktop for encrypted messages to appear in search results.", "Connecting to integration manager...": "Connecting to integration manager...", "Cannot connect to integration manager": "Cannot connect to integration manager", From 2d8477aaa69ac35c451c8a47691a0372cb635175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:13:55 +0100 Subject: [PATCH 59/89] FormattingUtils: Add a formatCountLong method. --- src/utils/FormattingUtils.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.js index 9016d62cfb..b932214530 100644 --- a/src/utils/FormattingUtils.js +++ b/src/utils/FormattingUtils.js @@ -30,6 +30,15 @@ export function formatCount(count) { return (count / 1000000000).toFixed(1) + "B"; // 10B is enough for anyone, right? :S } +/** + * Format a count showing the whole number but making it a bit more readable. + * e.g: 1000 => 1,000 + */ +export function formatCountLong(count) { + const formatter = new Intl.NumberFormat(); + return formatter.format(count) +} + /** * format a size in bytes into a human readable form * e.g: 1024 -> 1.00 KB From ddea7415c7bd954343efc98322a00326b5c1d2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:15:06 +0100 Subject: [PATCH 60/89] EventIndexPanel: Use formatCountLong to format the event and room counts. --- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 6 +++--- src/components/views/settings/EventIndexPanel.js | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index a8fc7dce3a..66cd89f4ab 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; import Modal from '../../../../Modal'; -import {formatBytes, formatCount} from "../../../../utils/FormattingUtils"; +import {formatBytes, formatCountLong} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; @@ -125,8 +125,8 @@ export default class ManageEventIndexDialog extends React.Component { }
    {_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
    - {_t("Indexed messages:")} {formatCount(this.state.eventCount)}
    - {_t("Number of rooms:")} {formatCount(this.state.roomCount)}
    + {_t("Indexed messages:")} {formatCountLong(this.state.eventCount)}
    + {_t("Number of rooms:")} {formatCountLong(this.state.roomCount)}
    {crawlerState}
    diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 321f45699b..851f86f3d4 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -21,7 +21,7 @@ import * as sdk from '../../../index'; import Modal from '../../../Modal'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; -import {formatBytes, formatCount} from "../../../utils/FormattingUtils"; +import {formatBytes, formatCountLong} from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; export default class EventIndexPanel extends React.Component { @@ -115,7 +115,8 @@ export default class EventIndexPanel extends React.Component { {_t( "Securely cache encrypted messages locally for them " + "to appear in search results, using ") } {formatBytes(this.state.eventIndexSize, 0)} - {_t( " to store messages from ")} {formatCount(this.state.roomCount)} {_t("rooms.")} + {_t( " to store messages from ")} + {formatCountLong(this.state.roomCount)} {_t("rooms.")}
    From cd225943ea7a504d8e9ff09eefa6aa098514ee2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:22:09 +0100 Subject: [PATCH 61/89] EventIndexPanel: Shorten a overly long line. --- src/components/views/settings/EventIndexPanel.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 851f86f3d4..479a995bc8 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -142,6 +142,12 @@ export default class EventIndexPanel extends React.Component {
    ); } else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) { + const nativeLink = ( + "https://github.com/vector-im/riot-web/blob/develop/" + + "docs/native-node-modules.md#" + + "adding-seshat-for-search-in-e2e-encrypted-rooms" + ); + eventIndexingSettings = (
    { @@ -151,7 +157,7 @@ export default class EventIndexPanel extends React.Component { "with search components added.", {}, { - 'nativeLink': (sub) => {sub}, + 'nativeLink': (sub) => {sub}, }, ) } From 5d3b916a89bb0c04b38fa24a5e360c93ff178d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:46:46 +0100 Subject: [PATCH 62/89] DialogButtons: Allow setting the cancel button class with a prop. --- src/components/views/elements/DialogButtons.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/DialogButtons.js b/src/components/views/elements/DialogButtons.js index 4e47e73052..2cb25d9c9c 100644 --- a/src/components/views/elements/DialogButtons.js +++ b/src/components/views/elements/DialogButtons.js @@ -40,6 +40,10 @@ export default createReactClass({ // should there be a cancel button? default: true hasCancel: PropTypes.bool, + // The class of the cancel button, only used if a cancel button is + // enabled + cancelButtonClass: PropTypes.node, + // onClick handler for the cancel button. onCancel: PropTypes.func, @@ -69,8 +73,10 @@ export default createReactClass({ primaryButtonClassName += " " + this.props.primaryButtonClass; } let cancelButton; + if (this.props.cancelButton || this.props.hasCancel) { - cancelButton = ; } From 3208ac60c7b6ea453448ada43e08441a371d6758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:47:29 +0100 Subject: [PATCH 63/89] ManageEventIndexDialog: Override the Disable button class to be danger. --- .../eventindex/ManageEventIndexDialog.js | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index 66cd89f4ab..5f6f4985da 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -132,18 +132,8 @@ export default class ManageEventIndexDialog extends React.Component {
    ); - const buttons = ( -
    - - {_t("Disable")} - - - {_t("Done")} - -
    - ); - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return ( {eventIndexingSettings} - {buttons} + ); } From 9f3e5ab1db15b0aa4cf6d45528e9a0f2323f85a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 24 Jan 2020 16:52:26 +0100 Subject: [PATCH 64/89] ManageEventIndexDialog: Remove an unused import. --- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index 5f6f4985da..5c82cc0391 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -22,7 +22,6 @@ import { _t } from '../../../../languageHandler'; import Modal from '../../../../Modal'; import {formatBytes, formatCountLong} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; -import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; /* * Allows the user to introspect the event index state and disable it. From c9a0e93a74f437978eef3266f8b2b279558e9857 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 11:14:32 +0000 Subject: [PATCH 65/89] tidy up borders --- res/css/views/dialogs/_RoomSettingsDialog.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 0e8deb018e..66c34fd73d 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -66,16 +66,16 @@ limitations under the License. margin-bottom: 8px; border-width: 1px 1px; border-color: $primary-hairline-color; + border-style: solid; border-radius: 5px; .protocol-icon { float: left; - margin-right: 30px; + margin-right: 5px; img { border-radius: 5px; border-width: 1px 1px; border-color: $primary-hairline-color; - border-style: solid; } span { /* Correct letter placement */ From c0d1298c4f7296fc066b5721fa4f01695acdc95a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:05:22 +0000 Subject: [PATCH 66/89] Factor out into BridgeTile --- res/css/_components.scss | 1 + .../dialogs/_RoomSettingsDialogBridges.scss | 93 ++++++++++++++ src/components/views/settings/BridgeTile.js | 116 ++++++++++++++++++ .../settings/tabs/room/BridgeSettingsTab.js | 79 +----------- 4 files changed, 212 insertions(+), 77 deletions(-) create mode 100644 res/css/views/dialogs/_RoomSettingsDialogBridges.scss create mode 100644 src/components/views/settings/BridgeTile.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 60f749de9c..f6a680c438 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -66,6 +66,7 @@ @import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; +@import "./views/dialogs/_RoomSettingsDialogBridges.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss"; diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss new file mode 100644 index 0000000000..85d5c76ffc --- /dev/null +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -0,0 +1,93 @@ +/* +Copyright 2020 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. +*/ + +.mx_RoomSettingsDialog_BridgeList { + padding: 0; + + .mx_AccessibleButton { + display: inline; + margin: 0; + padding: 0; + float: left; + } +} + +.mx_RoomSettingsDialog_BridgeList li { + list-style-type: none; + padding: 5px; + margin-bottom: 8px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-style: solid; + border-radius: 5px; + + .protocol-icon { + float: left; + margin-right: 5px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + } + span { + /* Correct letter placement */ + left: auto; + } + } + + h3 { + margin-top: 0; + margin-bottom: 4px; + font-size: 16pt; + color: $primary-fg-color; + } + + .column-icon { + float: left; + padding-right: 10px; + + .noProtocolIcon { + width: 48px; + height: 48px; + background: $settings-profile-placeholder-bg-color; + border-radius: 5px; + } + } + + .column-data { + display: inline-block; + width: 85%; + } + + .workspace-channel-details { + margin-top: 0; + color: $primary-fg-color; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; + } +} diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js new file mode 100644 index 0000000000..330af4a18a --- /dev/null +++ b/src/components/views/settings/BridgeTile.js @@ -0,0 +1,116 @@ +/* +Copyright 2020 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 PropTypes from 'prop-types'; +import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; +import {_t} from "../../../languageHandler"; +import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import Pill from "../elements/Pill"; +import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; +import BaseAvatar from "../avatars/BaseAvatar"; +import AccessibleButton from "../elements/AccessibleButton"; + +export default class BridgeTile extends React.PureComponent { + static propTypes = { + ev: PropTypes.object.isRequired, + room: PropTypes.object.isRequired, + } + + state = { + visible: false + } + + _toggleVisible() { + this.setState({ + visible: !this.state.visible, + }); + } + + render() { + const content = this.props.ev.getContent(); + const { channel, network, protocol } = content; + const protocolName = protocol.displayname || protocol.id; + const channelName = channel.displayname || channel.id; + const networkName = network ? network.displayname || network.id : protocolName; + + let creator = null; + if (content.creator) { + creator = _t("This bridge was provisioned by .", {}, { + user: , + }); + } + + const bot = _t("This bridge is managed by .", {}, { + user: , + }); + + let networkIcon; + + if (protocol.avatar) { + const avatarUrl = getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + protocol.avatar, 64, 64, "crop", + ); + + networkIcon = ; + } else { + networkIcon =
    ; + } + + + const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { + networkName, + channelName, + }); + const id = this.props.ev.getId(); + const metadataClassname = "metadata" + (this.state.visible ? " visible" : ""); + return (
  • +
    + {networkIcon} +
    +
    +

    {protocolName}

    +

    + {workspaceChannelDetails} +

    +

    + {creator} {bot} +

    + + Show { this.state.visible ? "less" : "more" } + +
    +
  • ); + } +} diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 8090cf7d4d..7a859b0594 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -18,10 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../../../languageHandler"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; -import Pill from "../../../elements/Pill"; -import {makeUserPermalink} from "../../../../../utils/permalinks/Permalinks"; -import BaseAvatar from "../../../avatars/BaseAvatar"; -import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; +import BridgeTile from "../../BridgeTile"; const BRIDGE_EVENT_TYPES = [ "uk.half-shot.bridge", @@ -35,17 +32,6 @@ export default class BridgeSettingsTab extends React.Component { constructor() { super(); - - this.state = { - showMoreCard: null, - }; - } - - - _showMoreDetails(eventId) { - this.setState({ - showMoreCard: eventId, - }); } _renderBridgeCard(event, room) { @@ -53,68 +39,7 @@ export default class BridgeSettingsTab extends React.Component { if (!content || !content.channel || !content.protocol) { return null; } - const { channel, network } = content; - const protocolName = content.protocol.displayname || content.protocol.id; - const channelName = channel.displayname || channel.id; - const networkName = network ? network.displayname || network.id : protocolName; - - let creator = null; - if (content.creator) { - creator = _t("This bridge was provisioned by .", {}, { - user: , - }); - } - - const bot = _t("This bridge is managed by .", {}, { - user: , - }); - - const avatarUrl = network.avatar ? getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), - network.avatar, 32, 32, "crop", - ) : null; - - const networkIcon = ; - - const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { - networkName, - channelName, - }); - const id = event.getId(); - const isVisible = this.state.showMoreCard === id; - const metadataClassname = "metadata " + (isVisible ? "visible" : ""); - return (
  • -
    - {networkIcon} -
    -
    -

    {protocolName}

    -

    - {workspaceChannelDetails} -

    -

    - {creator} {bot} -

    - this._showMoreDetails(isVisible ? null : id)}>Show { isVisible ? "less" : "more" } -
    -
  • ); + return } static getBridgeStateEvents(roomId) { From 7e0ab2f0a309157af8c953c88ef183b610e4fce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 27 Jan 2020 15:28:43 +0100 Subject: [PATCH 67/89] DisableEventIndexDialog: Turn the cancel button red. --- res/css/_common.scss | 5 +++++ .../views/dialogs/eventindex/DisableEventIndexDialog.js | 1 + 2 files changed, 6 insertions(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 51d985efb7..77284d0a14 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -414,6 +414,11 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { color: $accent-fg-color; } +.mx_Dialog button.warning, .mx_Dialog input[type="submit"].warning { + border: solid 1px $warning-color; + color: $warning-color; +} + .mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled { background-color: $light-fg-color; border: solid 1px $light-fg-color; diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 13278217de..120b086ef6 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -63,6 +63,7 @@ export default class DisableEventIndexDialog extends React.Component { primaryButton={_t('Disable')} onPrimaryButtonClick={this._onDisable} primaryButtonClass="danger" + cancelButtonClass="warning" onCancel={this.props.onFinished} disabled={this.state.disabling} /> From 4d83288f4ea6ebfe61f6f7f0ef4784a9e175ad86 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:42:46 +0000 Subject: [PATCH 68/89] linting --- src/components/views/settings/BridgeTile.js | 4 ++-- .../views/settings/tabs/room/BridgeSettingsTab.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 330af4a18a..ee9343ec39 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -29,9 +29,9 @@ export default class BridgeTile extends React.PureComponent { ev: PropTypes.object.isRequired, room: PropTypes.object.isRequired, } - + state = { - visible: false + visible: false, } _toggleVisible() { diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 7a859b0594..65c59ff977 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -39,7 +39,7 @@ export default class BridgeSettingsTab extends React.Component { if (!content || !content.channel || !content.protocol) { return null; } - return + return ; } static getBridgeStateEvents(roomId) { @@ -76,8 +76,8 @@ export default class BridgeSettingsTab extends React.Component {
      { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) }
    -
    - } else { +
    ; + } else { content =

    {_t( "This room isn’t bridging messages to any platforms. " + "Learn more.", {}, @@ -86,7 +86,7 @@ export default class BridgeSettingsTab extends React.Component { // having to re-translate the string when we do. a: sub => '', }, - )}

    + )}

    ; } return ( From 5851b10f72af580b2be3357799075ea0fcd1dd82 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Jan 2020 14:44:11 +0000 Subject: [PATCH 69/89] strings --- src/i18n/strings/en_EN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e4ab764989..1afa3a33c9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -521,6 +521,9 @@ "Accept to continue:": "Accept to continue:", "Upload": "Upload", "Remove": "Remove", + "This bridge was provisioned by .": "This bridge was provisioned by .", + "This bridge is managed by .": "This bridge is managed by .", + "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", "Failed to upload profile picture!": "Failed to upload profile picture!", "Upload new:": "Upload new:", "No display name": "No display name", @@ -781,9 +784,6 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", - "This bridge was provisioned by .": "This bridge was provisioned by .", - "This bridge is managed by .": "This bridge is managed by .", - "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", "This room is bridging messages to the following platforms. Learn more.": "This room is bridging messages to the following platforms. Learn more.", "This room isn’t bridging messages to any platforms. Learn more.": "This room isn’t bridging messages to any platforms. Learn more.", "Bridges": "Bridges", From e38f1191a52880ee5b80ee3c68feb02831a8bdda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 27 Jan 2020 15:50:14 +0100 Subject: [PATCH 70/89] ManageEventIndex: Clarify that we're currently not downloading any messages. --- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index 5c82cc0391..b7ea87b1b2 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -108,7 +108,7 @@ export default class ManageEventIndexDialog extends React.Component { let crawlerState; if (this.state.currentRoom === null) { - crawlerState = _t("Not downloading messages for any room."); + crawlerState = _t("Not currently downloading messages for any room."); } else { crawlerState = ( _t("Downloading mesages for %(currentRoom)s.", { currentRoom: this.state.currentRoom }) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 419aecd528..e6e4ad3ed3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2040,7 +2040,7 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "If disabled, messages from encrypted rooms won't appear in search results.": "If disabled, messages from encrypted rooms won't appear in search results.", "Disable": "Disable", - "Not downloading messages for any room.": "Not downloading messages for any room.", + "Not currently downloading messages for any room.": "Not currently downloading messages for any room.", "Downloading mesages for %(currentRoom)s.": "Downloading mesages for %(currentRoom)s.", "Riot is securely caching encrypted messages locally for them to appear in search results:": "Riot is securely caching encrypted messages locally for them to appear in search results:", "Space used:": "Space used:", From ab8ea5226647fcfdcaa7a352f3c711d2d5c536ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 27 Jan 2020 16:50:33 +0100 Subject: [PATCH 71/89] EventIndexPanel: Make sure links get opened in a new tab. --- src/components/views/settings/EventIndexPanel.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 479a995bc8..68faa53e53 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -157,7 +157,8 @@ export default class EventIndexPanel extends React.Component { "with search components added.", {}, { - 'nativeLink': (sub) => {sub}, + 'nativeLink': (sub) => {sub}, }, ) } @@ -172,7 +173,8 @@ export default class EventIndexPanel extends React.Component { "for encrypted messages to appear in search results.", {}, { - 'riotLink': (sub) => {sub}, + 'riotLink': (sub) => {sub}, }, ) } From 805e9abb3981584cd941e59c5c10edc3595c38b9 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 27 Jan 2020 16:00:25 +0000 Subject: [PATCH 72/89] Flip back to develop --- package.json | 2 +- yarn.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f0b7e04c73..78bbb5b4c6 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "is-ip": "^2.0.0", "linkifyjs": "^2.1.6", "lodash": "^4.17.14", - "matrix-js-sdk": "4.0.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", "prop-types": "^15.5.8", diff --git a/yarn.lock b/yarn.lock index 232067a99f..b892ac44f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5760,10 +5760,9 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc" integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw== -matrix-js-sdk@4.0.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "4.0.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-4.0.0.tgz#c81bdc905af2ab1634527e5f542f2f15977d31cf" - integrity sha512-Xbe36xL443qtEBH4xk0k39JabolqZfloK7fwYGMb/PgWO26VOzvw94XWahnIr5w83oxBAF9nFmP+7EnPG6IHnA== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/21e4c597d9633aef606871cf9ffffaf039142be3" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From 1964e18315657f800257e66756f6f6e5505ba20b Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 27 Jan 2020 16:40:56 +0000 Subject: [PATCH 73/89] Fix issue where we don't notice if our own devices shouldn't be trusted --- src/components/structures/RoomView.js | 2 +- src/components/views/rooms/RoomTile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 60fff5f1e3..2d669f9243 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -811,7 +811,7 @@ export default createReactClass({ debuglog("e2e verified", verified, "unverified", unverified); /* Check all verified user devices. */ - for (const userId of verified) { + for (const userId of [...verified, cli.getUserId()]) { const devices = await cli.getStoredDevicesForUser(userId); const anyDeviceNotVerified = devices.some(({deviceId}) => { return !cli.checkDeviceTrust(userId, deviceId).isVerified(); diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 41975fe7b8..41d43476ea 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -166,7 +166,7 @@ export default createReactClass({ }); /* Check all verified user devices. */ - for (const userId of verified) { + for (const userId of [...verified, cli.getUserId()]) { const devices = await cli.getStoredDevicesForUser(userId); const allDevicesVerified = devices.every(({deviceId}) => { return cli.checkDeviceTrust(userId, deviceId).isVerified(); From ff195381e9f17439fba3c7a2ff79e8587d41eaa6 Mon Sep 17 00:00:00 2001 From: stoically Date: Wed, 22 Jan 2020 16:44:47 +0100 Subject: [PATCH 74/89] Use https for recaptcha for all non-http protocols Signed-off-by: stoically --- src/components/views/auth/CaptchaForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/auth/CaptchaForm.js b/src/components/views/auth/CaptchaForm.js index 2da837f029..efcc450067 100644 --- a/src/components/views/auth/CaptchaForm.js +++ b/src/components/views/auth/CaptchaForm.js @@ -62,7 +62,7 @@ export default createReactClass({ console.log("Loading recaptcha script..."); window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();}; let protocol = global.location.protocol; - if (protocol === "vector:") { + if (protocol !== "http:") { protocol = "https:"; } const scriptTag = document.createElement('script'); From 89f110f60a7756f0c27f4e248fc27011b55cd28e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 27 Jan 2020 22:27:11 +0000 Subject: [PATCH 75/89] Add separate component for post-auth security flows Instead of twisting `AuthBody`, this adds a new component for the different styling of post-auth security flows. This also makes them fixed width and adjusts padding to match designs. --- res/css/_components.scss | 3 +- .../structures/auth/_CompleteSecurity.scss | 2 +- res/css/views/auth/_AuthBody.scss | 14 ++----- res/css/views/auth/_CompleteSecurityBody.scss | 42 +++++++++++++++++++ .../structures/auth/CompleteSecurity.js | 6 +-- src/components/structures/auth/E2eSetup.js | 6 +-- src/components/views/auth/AuthBody.js | 21 +--------- .../views/auth/CompleteSecurityBody.js | 27 ++++++++++++ 8 files changed, 83 insertions(+), 38 deletions(-) create mode 100644 res/css/views/auth/_CompleteSecurityBody.scss create mode 100644 src/components/views/auth/CompleteSecurityBody.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 07e92bdc7b..a2cfc94c79 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -36,6 +36,7 @@ @import "./views/auth/_AuthHeader.scss"; @import "./views/auth/_AuthHeaderLogo.scss"; @import "./views/auth/_AuthPage.scss"; +@import "./views/auth/_CompleteSecurityBody.scss"; @import "./views/auth/_CountryDropdown.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_LanguageSelector.scss"; @@ -148,10 +149,10 @@ @import "./views/rooms/_AuxPanel.scss"; @import "./views/rooms/_BasicMessageComposer.scss"; @import "./views/rooms/_E2EIcon.scss"; -@import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; @import "./views/rooms/_MemberDeviceInfo.scss"; diff --git a/res/css/structures/auth/_CompleteSecurity.scss b/res/css/structures/auth/_CompleteSecurity.scss index c258ce4ec7..2bf51d9574 100644 --- a/res/css/structures/auth/_CompleteSecurity.scss +++ b/res/css/structures/auth/_CompleteSecurity.scss @@ -22,7 +22,7 @@ limitations under the License. .mx_CompleteSecurity_headerIcon { width: 24px; height: 24px; - margin: 0 4px; + margin-right: 4px; position: relative; } diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 51b9775811..7c5b008535 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +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. @@ -15,6 +16,9 @@ limitations under the License. */ .mx_AuthBody { + width: 500px; + font-size: 12px; + color: $authpage-secondary-color; background-color: $authpage-body-bg-color; border-radius: 0 4px 4px 0; padding: 25px 60px; @@ -92,16 +96,6 @@ limitations under the License. } } -.mx_AuthBody_noHeader { - border-radius: 4px; -} - -.mx_AuthBody_loginRegister { - width: 500px; - font-size: 12px; - color: $authpage-secondary-color; -} - .mx_AuthBody_editServerDetails { padding-left: 1em; font-size: 12px; diff --git a/res/css/views/auth/_CompleteSecurityBody.scss b/res/css/views/auth/_CompleteSecurityBody.scss new file mode 100644 index 0000000000..c7860fbe74 --- /dev/null +++ b/res/css/views/auth/_CompleteSecurityBody.scss @@ -0,0 +1,42 @@ +/* +Copyright 2019 New Vector Ltd +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_CompleteSecurityBody { + width: 600px; + color: $authpage-primary-color; + background-color: $authpage-body-bg-color; + border-radius: 4px; + padding: 20px; + box-sizing: border-box; + + h2 { + font-size: 24px; + font-weight: 600; + margin-top: 0; + } + + h3 { + font-size: 14px; + font-weight: 600; + } + + a:link, + a:hover, + a:visited { + @mixin mx_Dialog_link; + } +} diff --git a/src/components/structures/auth/CompleteSecurity.js b/src/components/structures/auth/CompleteSecurity.js index 206cdb743e..29d8207d0a 100644 --- a/src/components/structures/auth/CompleteSecurity.js +++ b/src/components/structures/auth/CompleteSecurity.js @@ -112,7 +112,7 @@ export default class CompleteSecurity extends React.Component { render() { const AuthPage = sdk.getComponent("auth.AuthPage"); - const AuthBody = sdk.getComponent("auth.AuthBody"); + const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); const { @@ -204,7 +204,7 @@ export default class CompleteSecurity extends React.Component { return ( - +

    {icon} {title} @@ -212,7 +212,7 @@ export default class CompleteSecurity extends React.Component {
    {body}
    - + ); } diff --git a/src/components/structures/auth/E2eSetup.js b/src/components/structures/auth/E2eSetup.js index 29b4345761..9b390d24cc 100644 --- a/src/components/structures/auth/E2eSetup.js +++ b/src/components/structures/auth/E2eSetup.js @@ -34,16 +34,16 @@ export default class E2eSetup extends React.Component { render() { const AuthPage = sdk.getComponent("auth.AuthPage"); - const AuthBody = sdk.getComponent("auth.AuthBody"); + const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody"); return ( - + - + ); } diff --git a/src/components/views/auth/AuthBody.js b/src/components/views/auth/AuthBody.js index b74b7d866a..9a078efb52 100644 --- a/src/components/views/auth/AuthBody.js +++ b/src/components/views/auth/AuthBody.js @@ -17,29 +17,10 @@ limitations under the License. 'use strict'; import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; export default class AuthBody extends React.PureComponent { - static PropTypes = { - header: PropTypes.bool, - }; - - static defaultProps = { - header: true, - }; - render() { - const classes = { - 'mx_AuthBody': true, - 'mx_AuthBody_noHeader': !this.props.header, - // XXX The login pages all use a smaller fonts size but we don't want this - // for subsequent auth screens like the e2e setup. Doing this a terrible way - // for now. - 'mx_AuthBody_loginRegister': this.props.header, - }; - - return
    + return
    { this.props.children }
    ; } diff --git a/src/components/views/auth/CompleteSecurityBody.js b/src/components/views/auth/CompleteSecurityBody.js new file mode 100644 index 0000000000..d757de9fe0 --- /dev/null +++ b/src/components/views/auth/CompleteSecurityBody.js @@ -0,0 +1,27 @@ +/* +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. +*/ + +'use strict'; + +import React from 'react'; + +export default class CompleteSecurityBody extends React.PureComponent { + render() { + return
    + { this.props.children } +
    ; + } +} From 2a1407a531e74eaea8a729cbd66d8f33d9d5f661 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 27 Jan 2020 15:36:12 -0700 Subject: [PATCH 76/89] Add more logging to settings watchers To try and track leaks versus spam. Fixes https://github.com/vector-im/riot-web/issues/12094 (it's not leaking, probably) --- src/settings/SettingsStore.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 9501bac205..b1d61197a0 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -145,7 +145,7 @@ export default class SettingsStore { callbackFn(originalSettingName, changedInRoomId, atLevel, newValAtLevel, newValue); }; - console.log(`Starting watcher for ${settingName}@${roomId || ''}`); + console.log(`Starting watcher for ${settingName}@${roomId || ''} as ID ${watcherId}`); SettingsStore._watchers[watcherId] = localizedCallback; defaultWatchManager.watchSetting(settingName, roomId, localizedCallback); @@ -159,8 +159,12 @@ export default class SettingsStore { * to cancel. */ static unwatchSetting(watcherReference) { - if (!SettingsStore._watchers[watcherReference]) return; + if (!SettingsStore._watchers[watcherReference]) { + console.warn(`Ending non-existent watcher ID ${watcherReference}`); + return; + } + console.log(`Ending watcher ID ${watcherReference}`); defaultWatchManager.unwatchSetting(SettingsStore._watchers[watcherReference]); delete SettingsStore._watchers[watcherReference]; } From d014c5239be443911d3a3ad0b4650756c9e04a1a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 27 Jan 2020 23:14:02 +0000 Subject: [PATCH 77/89] Add new session verification details dialog This gives more info on the session you're about to verify, including device name and ID. Fixes https://github.com/vector-im/riot-web/issues/11977 --- res/css/_components.scss | 1 + .../dialogs/_NewSessionReviewDialog.scss | 37 ++++++++ .../views/dialogs/NewSessionReviewDialog.js | 92 +++++++++++++++++++ .../views/elements/DialogButtons.js | 2 +- .../views/toasts/NewSessionToast.js | 5 +- src/i18n/strings/en_EN.json | 4 + 6 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 res/css/views/dialogs/_NewSessionReviewDialog.scss create mode 100644 src/components/views/dialogs/NewSessionReviewDialog.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 07e92bdc7b..de56ad77bb 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -65,6 +65,7 @@ @import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss"; +@import "./views/dialogs/_NewSessionReviewDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; diff --git a/res/css/views/dialogs/_NewSessionReviewDialog.scss b/res/css/views/dialogs/_NewSessionReviewDialog.scss new file mode 100644 index 0000000000..7e35fe941e --- /dev/null +++ b/res/css/views/dialogs/_NewSessionReviewDialog.scss @@ -0,0 +1,37 @@ +/* +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_NewSessionReviewDialog_header { + display: flex; + align-items: center; + margin-top: 0; +} + +.mx_NewSessionReviewDialog_headerIcon { + width: 24px; + height: 24px; + margin-right: 4px; + position: relative; +} + +.mx_NewSessionReviewDialog_deviceName { + font-weight: 600; +} + +.mx_NewSessionReviewDialog_deviceID { + font-size: 12px; + color: $notice-secondary-color; +} diff --git a/src/components/views/dialogs/NewSessionReviewDialog.js b/src/components/views/dialogs/NewSessionReviewDialog.js new file mode 100644 index 0000000000..c14f0f5614 --- /dev/null +++ b/src/components/views/dialogs/NewSessionReviewDialog.js @@ -0,0 +1,92 @@ +/* +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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import * as sdk from "../../../index"; +import { _t } from '../../../languageHandler'; +import Modal from "../../../Modal"; + +export default class NewSessionReviewDialog extends React.PureComponent { + static propTypes = { + userId: PropTypes.string.isRequired, + device: PropTypes.object.isRequired, + onFinished: PropTypes.func.isRequired, + } + + onCancelClick = () => { + this.props.onFinished(false); + } + + onContinueClick = () => { + const DeviceVerifyDialog = + sdk.getComponent('views.dialogs.DeviceVerifyDialog'); + const { userId, device } = this.props; + Modal.createTrackedDialog('New Session Verification', 'Starting dialog', DeviceVerifyDialog, { + userId, + device, + }, null, /* priority = */ false, /* static = */ true); + } + + render() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); + + const { device } = this.props; + + const icon = ; + const titleText = _t("New session"); + + const title =

    + {icon} + {titleText} +

    ; + + return ( + +
    +

    {_t( + "Use this session to verify your new one, " + + "granting it access to encrypted messages:", + )}

    +
    +
    + + {device.getDisplayName()} + + ({device.deviceId}) + +
    +
    +

    {_t( + "If you didn’t sign in to this session, " + + "your account may be compromised.", + )}

    + +
    +
    + ); + } +} diff --git a/src/components/views/elements/DialogButtons.js b/src/components/views/elements/DialogButtons.js index ee15bfc3f2..9223b5ade8 100644 --- a/src/components/views/elements/DialogButtons.js +++ b/src/components/views/elements/DialogButtons.js @@ -83,7 +83,7 @@ export default createReactClass({ // primary in the DOM so will get form submissions unless we make it not a submit. type="button" onClick={this._onCancelClick} - className={this.props.cancelButtonClass} + className={this.props.cancelButtonClass} disabled={this.props.disabled} > { this.props.cancelButton || _t("Cancel") } diff --git a/src/components/views/toasts/NewSessionToast.js b/src/components/views/toasts/NewSessionToast.js index 3b60f59131..ed8b15e25f 100644 --- a/src/components/views/toasts/NewSessionToast.js +++ b/src/components/views/toasts/NewSessionToast.js @@ -34,11 +34,12 @@ export default class VerifySessionToast extends React.PureComponent { _onReviewClick = async () => { const cli = MatrixClientPeg.get(); - const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog'); + const NewSessionReviewDialog = + sdk.getComponent('views.dialogs.NewSessionReviewDialog'); const device = await cli.getStoredDevice(cli.getUserId(), this.props.deviceId); - Modal.createTrackedDialog('New Session Verify', 'Starting dialog', DeviceVerifyDialog, { + Modal.createTrackedDialog('New Session Review', 'Starting dialog', NewSessionReviewDialog, { userId: MatrixClientPeg.get().getUserId(), device, }, null, /* priority = */ false, /* static = */ true); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 23ca730d97..7ccce9e7f6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1508,6 +1508,10 @@ "Are you sure you want to sign out?": "Are you sure you want to sign out?", "Your homeserver doesn't seem to support this feature.": "Your homeserver doesn't seem to support this feature.", "Message edits": "Message edits", + "New session": "New session", + "Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:", + "If you didn’t sign in to this session, your account may be compromised.": "If you didn’t sign in to this session, your account may be compromised.", + "This wasn't me": "This wasn't me", "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.", "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.", "Report bugs & give feedback": "Report bugs & give feedback", From bdaf9fd06d2ec85985753eda2d076c7f195dd367 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 10:05:42 +0000 Subject: [PATCH 78/89] i18n --- src/components/views/settings/BridgeTile.js | 2 +- src/i18n/strings/en_EN.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index ee9343ec39..a5672c271c 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -108,7 +108,7 @@ export default class BridgeTile extends React.PureComponent { {creator} {bot}

    - Show { this.state.visible ? "less" : "more" } + { this.state.visible ? _t("Show less") : _t("Show more") }
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1afa3a33c9..270d964e56 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -524,6 +524,8 @@ "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "Show less": "Show less", + "Show more": "Show more", "Failed to upload profile picture!": "Failed to upload profile picture!", "Upload new:": "Upload new:", "No display name": "No display name", @@ -1461,7 +1463,6 @@ "Recent Conversations": "Recent Conversations", "Suggestions": "Suggestions", "Recently Direct Messaged": "Recently Direct Messaged", - "Show more": "Show more", "Direct Messages": "Direct Messages", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.", "Go": "Go", From 67358e06bf5da0d40f58d00c75b589e70624bc79 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 28 Jan 2020 10:10:37 +0000 Subject: [PATCH 79/89] Use annotations and imports --- .../views/dialogs/NewSessionReviewDialog.js | 13 ++++++------- src/components/views/toasts/NewSessionToast.js | 8 ++++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/components/views/dialogs/NewSessionReviewDialog.js b/src/components/views/dialogs/NewSessionReviewDialog.js index c14f0f5614..2d2bcc8f35 100644 --- a/src/components/views/dialogs/NewSessionReviewDialog.js +++ b/src/components/views/dialogs/NewSessionReviewDialog.js @@ -16,10 +16,14 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; -import Modal from "../../../Modal"; +import Modal from '../../../Modal'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; +import DeviceVerifyDialog from './DeviceVerifyDialog'; +import BaseDialog from './BaseDialog'; +import DialogButtons from '../elements/DialogButtons'; +@replaceableComponent("views.dialogs.NewSessionReviewDialog") export default class NewSessionReviewDialog extends React.PureComponent { static propTypes = { userId: PropTypes.string.isRequired, @@ -32,8 +36,6 @@ export default class NewSessionReviewDialog extends React.PureComponent { } onContinueClick = () => { - const DeviceVerifyDialog = - sdk.getComponent('views.dialogs.DeviceVerifyDialog'); const { userId, device } = this.props; Modal.createTrackedDialog('New Session Verification', 'Starting dialog', DeviceVerifyDialog, { userId, @@ -42,9 +44,6 @@ export default class NewSessionReviewDialog extends React.PureComponent { } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent("views.elements.DialogButtons"); - const { device } = this.props; const icon = ; diff --git a/src/components/views/toasts/NewSessionToast.js b/src/components/views/toasts/NewSessionToast.js index ed8b15e25f..80564f3494 100644 --- a/src/components/views/toasts/NewSessionToast.js +++ b/src/components/views/toasts/NewSessionToast.js @@ -16,12 +16,15 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import Modal from "../../../Modal"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import DeviceListener from '../../../DeviceListener'; +import NewSessionReviewDialog from '../dialogs/NewSessionReviewDialog'; +import FormButton from '../elements/FormButton'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; +@replaceableComponent("views.toasts.VerifySessionToast") export default class VerifySessionToast extends React.PureComponent { static propTypes = { toastKey: PropTypes.string.isRequired, @@ -34,8 +37,6 @@ export default class VerifySessionToast extends React.PureComponent { _onReviewClick = async () => { const cli = MatrixClientPeg.get(); - const NewSessionReviewDialog = - sdk.getComponent('views.dialogs.NewSessionReviewDialog'); const device = await cli.getStoredDevice(cli.getUserId(), this.props.deviceId); @@ -46,7 +47,6 @@ export default class VerifySessionToast extends React.PureComponent { }; render() { - const FormButton = sdk.getComponent("elements.FormButton"); return (
    {_t("Review & verify your new session")}
    From 785277d4b8d44ad011bb79cf0db2369584ee0730 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 11:17:51 +0000 Subject: [PATCH 80/89] Review bits for travis --- .../views/dialogs/_RoomSettingsDialog.scss | 63 ------------------- .../dialogs/_RoomSettingsDialogBridges.scss | 7 ++- src/components/views/settings/BridgeTile.js | 13 ++-- .../settings/tabs/room/BridgeSettingsTab.js | 8 +-- src/i18n/strings/en_EN.json | 3 +- 5 files changed, 14 insertions(+), 80 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 66c34fd73d..2a4e62f9aa 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -56,66 +56,3 @@ limitations under the License. mask-position: center; } -.mx_RoomSettingsDialog_BridgeList { - padding: 0; -} - -.mx_RoomSettingsDialog_BridgeList li { - list-style-type: none; - padding: 5px; - margin-bottom: 8px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - border-style: solid; - border-radius: 5px; - - .protocol-icon { - float: left; - margin-right: 5px; - img { - border-radius: 5px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - } - span { - /* Correct letter placement */ - left: auto; - } - } - - h3 { - margin-top: 0; - margin-bottom: 4px; - font-size: 16pt; - } - - .column-icon { - float: left; - } - - .column-data { - display: inline-block; - width: 85%; - } - - .workspace-channel-details { - margin-top: 0; - color: $primary-fg-color; - } - - .metadata { - color: $muted-fg-color; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-bottom: 0; - } - - .metadata.visible { - overflow-y: visible; - text-overflow: ellipsis; - white-space: normal; - } - -} - diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index 85d5c76ffc..ab54fb777b 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -1,5 +1,5 @@ /* -Copyright 2020 New Vector Ltd. +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. @@ -21,7 +21,6 @@ limitations under the License. display: inline; margin: 0; padding: 0; - float: left; } } @@ -75,6 +74,10 @@ limitations under the License. .workspace-channel-details { margin-top: 0; color: $primary-fg-color; + + .channel { + margin-left: 15px; + } } .metadata { diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index a5672c271c..dca23723aa 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -1,5 +1,5 @@ /* -Copyright 2020 New Vector Ltd +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. @@ -23,7 +23,8 @@ import Pill from "../elements/Pill"; import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import BaseAvatar from "../avatars/BaseAvatar"; import AccessibleButton from "../elements/AccessibleButton"; - +import {replaceableComponent} from "../../../utils/replaceableComponent"; +@replaceableComponent("views.settings.BridgeTile") export default class BridgeTile extends React.PureComponent { static propTypes = { ev: PropTypes.object.isRequired, @@ -88,11 +89,6 @@ export default class BridgeTile extends React.PureComponent { networkIcon =
    ; } - - const workspaceChannelDetails = _t("Workspace: %(networkName)s Channel: %(channelName)s", { - networkName, - channelName, - }); const id = this.props.ev.getId(); const metadataClassname = "metadata" + (this.state.visible ? " visible" : ""); return (
  • @@ -102,7 +98,8 @@ export default class BridgeTile extends React.PureComponent {

    {protocolName}

    - {workspaceChannelDetails} + {_t("Workspace: %(networkName)s", {networkName})} + {_t("Channel: %(channelName)s", {channelName})}

    {creator} {bot} diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 65c59ff977..12a72ab8f9 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -30,10 +30,6 @@ export default class BridgeSettingsTab extends React.Component { roomId: PropTypes.string.isRequired, }; - constructor() { - super(); - } - _renderBridgeCard(event, room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { @@ -70,7 +66,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => '', + a: sub => sub, }, )}

      @@ -84,7 +80,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => '', + a: sub => sub, }, )}

      ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 270d964e56..61dcd90638 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -523,7 +523,8 @@ "Remove": "Remove", "This bridge was provisioned by .": "This bridge was provisioned by .", "This bridge is managed by .": "This bridge is managed by .", - "Workspace: %(networkName)s Channel: %(channelName)s": "Workspace: %(networkName)s Channel: %(channelName)s", + "Workspace: %(networkName)s": "Workspace: %(networkName)s", + "Channel: %(channelName)s": "Channel: %(channelName)s", "Show less": "Show less", "Show more": "Show more", "Failed to upload profile picture!": "Failed to upload profile picture!", From 71233a5affbfc8a793cd74e5ae9d6c3fd99dea63 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 11:33:51 +0000 Subject: [PATCH 81/89] liney liney come back we need you --- src/components/views/settings/BridgeTile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index dca23723aa..6902639879 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -24,6 +24,7 @@ import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import BaseAvatar from "../avatars/BaseAvatar"; import AccessibleButton from "../elements/AccessibleButton"; import {replaceableComponent} from "../../../utils/replaceableComponent"; + @replaceableComponent("views.settings.BridgeTile") export default class BridgeTile extends React.PureComponent { static propTypes = { From 9cf59ab16d6b08b0ae40a76e300e0b5d0e4612f7 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 28 Jan 2020 12:30:39 +0000 Subject: [PATCH 82/89] Enable cross-signing lab when key in storage When we're starting a new session and find the cross-signing keys in secret storage, auto-enable the lab for the new session. Fixes https://github.com/vector-im/riot-web/issues/12100 --- src/components/structures/MatrixChat.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 133d74db45..1b4d0e9609 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1834,6 +1834,7 @@ export default createReactClass({ this._accountPassword = null; this._accountPasswordTimer = null; }, 60 * 5 * 1000); + // Wait for the client to be logged in (but not started) // which is enough to ask the server about account data. const loggedIn = new Promise(resolve => { @@ -1867,6 +1868,9 @@ export default createReactClass({ } if (masterKeyInStorage) { + // Auto-enable cross-signing for the new session when key found in + // secret storage. + SettingsStore.setFeatureEnabled("feature_cross_signing", true); this.setStateForNewView({ view: VIEWS.COMPLETE_SECURITY }); } else if (SettingsStore.isFeatureEnabled("feature_cross_signing")) { // This will only work if the feature is set to 'enable' in the config, From 21405b8f25ab36dc1967b53bdd349e872165dd18 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 12:44:14 +0000 Subject: [PATCH 83/89] Fix skinning and babel tagets --- babel.config.js | 4 ++-- src/Skinner.js | 14 ++++++++------ src/utils/replaceableComponent.ts | 8 ++++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/babel.config.js b/babel.config.js index c83be72518..944d9051bb 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,7 +4,7 @@ module.exports = { ["@babel/preset-env", { "targets": { "browsers": [ - "last 2 versions" + "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" ] }, "modules": "commonjs" @@ -14,7 +14,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", { "legacy": true }], + ["@babel/plugin-proposal-decorators", {"legacy": false, decoratorsBeforeExport: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", diff --git a/src/Skinner.js b/src/Skinner.js index 3baecc9fb3..1e121b8808 100644 --- a/src/Skinner.js +++ b/src/Skinner.js @@ -20,6 +20,7 @@ class Skinner { } getComponent(name) { + if (!name) throw new Error(`Invalid component name: ${name}`); if (this.components === null) { throw new Error( "Attempted to get a component before a skin has been loaded."+ @@ -43,12 +44,6 @@ class Skinner { // Check the skin first let comp = doLookup(this.components); - // If that failed, check against our own components - if (!comp) { - // Lazily load our own components because they might end up calling .getComponent() - comp = doLookup(require("./component-index").components); - } - // Just return nothing instead of erroring - the consumer should be smart enough to // handle this at this point. if (!comp) { @@ -75,6 +70,13 @@ class Skinner { const comp = skinObject.components[compKeys[i]]; this.addComponent(compKeys[i], comp); } + + // Now that we have a skin, load our components too + const idx = require("./component-index"); + if (!idx || !idx.components) throw new Error("Invalid react-sdk component index"); + for (const c in idx.components) { + if (!this.components[c]) this.components[c] = idx.components[c]; + } } addComponent(name, comp) { diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 9f617b27f3..92272e533c 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,9 +32,13 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string, origComponent: React.Component) { +export function replaceableComponent(name: string) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. - return () => sdk.getComponent(name) || origComponent; + return (origComponent) => { + const c = sdk.getComponent(name) || origComponent; + c.kind = "class"; // appeases babel + return c; + }; } From d0c28adfb1a13a54e78a7b9c825f824a784db28c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 12:53:37 +0000 Subject: [PATCH 84/89] Appease the linter --- src/Skinner.js | 2 +- src/utils/replaceableComponent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Skinner.js b/src/Skinner.js index 1e121b8808..87c5a7be7f 100644 --- a/src/Skinner.js +++ b/src/Skinner.js @@ -42,7 +42,7 @@ class Skinner { }; // Check the skin first - let comp = doLookup(this.components); + const comp = doLookup(this.components); // Just return nothing instead of erroring - the consumer should be smart enough to // handle this at this point. diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 92272e533c..281ff4c1ac 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,7 +32,7 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string) { +export function replaceableComponent(name: string) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. From a4778cc7e3371f12c32ec991a684c681911e0d1e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 14:18:12 +0000 Subject: [PATCH 85/89] Remove legacy --- babel.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/babel.config.js b/babel.config.js index 944d9051bb..333e5301af 100644 --- a/babel.config.js +++ b/babel.config.js @@ -14,7 +14,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", {"legacy": false, decoratorsBeforeExport: true}], + ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", From 894568bf7aff3578970e6d577d28a14c19d2bce2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 14:19:06 +0000 Subject: [PATCH 86/89] Stop using deprecated stuff --- babel.config.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/babel.config.js b/babel.config.js index 333e5301af..3c0c3fcb85 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,12 +2,9 @@ module.exports = { "sourceMaps": "inline", "presets": [ ["@babel/preset-env", { - "targets": { - "browsers": [ - "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" - ] - }, - "modules": "commonjs" + "targets": [ + "last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" + ], }], "@babel/preset-typescript", "@babel/preset-flow", From 85bcad0ea0e0a667de343efa81dac64e63c2cf85 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 14:42:58 +0000 Subject: [PATCH 87/89] Styling for Nad --- .../dialogs/_RoomSettingsDialogBridges.scss | 100 ++++++++++-------- src/components/views/settings/BridgeTile.js | 2 +- .../settings/tabs/room/BridgeSettingsTab.js | 6 +- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index ab54fb777b..d77e019cbf 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -33,64 +33,80 @@ limitations under the License. border-style: solid; border-radius: 5px; - .protocol-icon { - float: left; - margin-right: 5px; - img { - border-radius: 5px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - } - span { - /* Correct letter placement */ - left: auto; - } - } - - h3 { - margin-top: 0; - margin-bottom: 4px; - font-size: 16pt; - color: $primary-fg-color; - } - .column-icon { float: left; padding-right: 10px; + * { + border-radius: 5px; + border: 1px solid $input-darker-bg-color; + } + .noProtocolIcon { width: 48px; height: 48px; - background: $settings-profile-placeholder-bg-color; + background: $input-darker-bg-color; border-radius: 5px; } + + .protocol-icon { + float: left; + margin-right: 5px; + img { + border-radius: 5px; + border-width: 1px 1px; + border-color: $primary-hairline-color; + } + span { + /* Correct letter placement */ + left: auto; + } + } } .column-data { display: inline-block; width: 85%; - } - .workspace-channel-details { - margin-top: 0; - color: $primary-fg-color; + > h3 { + margin-top: 0px; + margin-bottom: 0px; + font-size: 16pt; + color: $primary-fg-color; + } - .channel { - margin-left: 15px; + > * { + margin-top: 4px; + margin-bottom: 0; + } + + .workspace-channel-details { + color: $primary-fg-color; + font-weight: 600; + + .channel { + margin-left: 5px; + } + } + + .showMore { + display: block; + text-align: left; + margin-top: 10px; + } + + .metadata { + color: $muted-fg-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + + .metadata.visible { + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; } } - - .metadata { - color: $muted-fg-color; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-bottom: 0; - } - - .metadata.visible { - overflow-y: visible; - text-overflow: ellipsis; - white-space: normal; - } } diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 6902639879..1759c0f58d 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -105,7 +105,7 @@ export default class BridgeTile extends React.PureComponent {

      {creator} {bot}

      - + { this.state.visible ? _t("Show less") : _t("Show more") }
    diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 12a72ab8f9..d66732de55 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -25,6 +25,8 @@ const BRIDGE_EVENT_TYPES = [ // m.bridge ]; +const BRIDGES_LINK = "https://matrix.org/bridges/"; + export default class BridgeSettingsTab extends React.Component { static propTypes = { roomId: PropTypes.string.isRequired, @@ -66,7 +68,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => sub, + a: sub => {sub}, }, )}

      @@ -80,7 +82,7 @@ export default class BridgeSettingsTab extends React.Component { { // TODO: We don't have this link yet: this will prevent the translators // having to re-translate the string when we do. - a: sub => sub, + a: sub => {sub}, }, )}

      ; } From bfaa9d56fbf368eb3ffb4c985794633fd8014833 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Jan 2020 16:05:27 +0000 Subject: [PATCH 88/89] prefixes --- res/css/views/dialogs/_RoomSettingsDialogBridges.scss | 2 +- src/components/views/settings/BridgeTile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index d77e019cbf..a1793cc75e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -89,7 +89,7 @@ limitations under the License. } } - .showMore { + .mx_showMore { display: block; text-align: left; margin-top: 10px; diff --git a/src/components/views/settings/BridgeTile.js b/src/components/views/settings/BridgeTile.js index 1759c0f58d..5b74e44c9e 100644 --- a/src/components/views/settings/BridgeTile.js +++ b/src/components/views/settings/BridgeTile.js @@ -105,7 +105,7 @@ export default class BridgeTile extends React.PureComponent {

      {creator} {bot}

      - + { this.state.visible ? _t("Show less") : _t("Show more") }
  • From 330b489fd528b1f80bdbdd1ba1de091fec573cf7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jan 2020 16:44:30 +0000 Subject: [PATCH 89/89] Switch back to legacy decorators Empirically the build is fine with these, but it is unfortunate that we have to reply on deprecated semantics. TypeScript should help fix this. --- babel.config.js | 2 +- src/utils/replaceableComponent.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/babel.config.js b/babel.config.js index 3c0c3fcb85..d5a97d56ce 100644 --- a/babel.config.js +++ b/babel.config.js @@ -11,7 +11,7 @@ module.exports = { "@babel/preset-react" ], "plugins": [ - ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport: true}], + ["@babel/plugin-proposal-decorators", {legacy: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", diff --git a/src/utils/replaceableComponent.ts b/src/utils/replaceableComponent.ts index 281ff4c1ac..9f617b27f3 100644 --- a/src/utils/replaceableComponent.ts +++ b/src/utils/replaceableComponent.ts @@ -32,13 +32,9 @@ import * as sdk from '../index'; * with a skinned version. If no skinned version is available, this component * will be used. */ -export function replaceableComponent(name: string) { +export function replaceableComponent(name: string, origComponent: React.Component) { // Decorators return a function to override the class (origComponent). This // ultimately assumes that `getComponent()` won't throw an error and instead // return a falsey value like `null` when the skin doesn't have a component. - return (origComponent) => { - const c = sdk.getComponent(name) || origComponent; - c.kind = "class"; // appeases babel - return c; - }; + return () => sdk.getComponent(name) || origComponent; }