From 1f2bf0485ebee982ca5cdb50cac3f525b26b07fc Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 18 Apr 2020 14:52:41 +0300 Subject: [PATCH 001/181] Implement widget API for signaling the widget to gracefully terminate In theory, widgets could use iframe unload/beforeunload events for cleanup, but in practice browsers have restrictions on what can be done in those events which may not give sufficient time for clean termination. Signed-off-by: Pauli Virtanen --- src/WidgetMessaging.js | 13 +++++++++++++ src/widgets/WidgetApi.ts | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index 5f877bd48a..6a20c340a5 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -87,6 +87,19 @@ export default class WidgetMessaging { }); } + /** + * Tells the widget that it should terminate now. + * It is not necessarily called in all instances before the widget is removed, + * and the client may force termination with a timeout. + * @returns {Promise<*>} Resolves when widget has acknowledged the message. + */ + terminate() { + return this.messageToWidget({ + api: OUTBOUND_API_NAME, + action: KnownWidgetActions.Terminate, + }); + } + /** * Request a screenshot from a widget * @return {Promise} To be resolved with screenshot data when it has been generated diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index 05237d258f..e08476edb5 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -34,6 +34,7 @@ export enum KnownWidgetActions { ReceiveOpenIDCredentials = "openid_credentials", SetAlwaysOnScreen = "set_always_on_screen", ClientReady = "im.vector.ready", + Terminate = "im.vector.terminate", } export type WidgetAction = KnownWidgetActions | string; @@ -68,6 +69,8 @@ export class WidgetApi { private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {}; private readyPromise: Promise; private readyPromiseResolve: () => void; + private terminatePromise: Promise; + private terminatePromiseResolve: () => void; /** * Set this to true if your widget is expecting a ready message from the client. False otherwise (default). @@ -78,6 +81,7 @@ export class WidgetApi { this.origin = new URL(currentUrl).origin; this.readyPromise = new Promise(resolve => this.readyPromiseResolve = resolve); + this.terminatePromise = new Promise(resolve => this.terminatePromiseResolve = resolve); window.addEventListener("message", event => { if (event.origin !== this.origin) return; // ignore: invalid origin @@ -98,6 +102,12 @@ export class WidgetApi { // Automatically acknowledge so we can move on this.replyToRequest(payload, {}); + } else if (payload.action === KnownWidgetActions.Terminate) { + // Reply after resolving + this.terminatePromise.then(() => { + this.replyToRequest(payload, {}); + }); + this.terminatePromiseResolve(); } else { console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`); } @@ -116,6 +126,10 @@ export class WidgetApi { return this.readyPromise; } + public addTerminateCallback(action) { + this.terminatePromise = this.terminatePromise.then(action); + } + private replyToRequest(payload: ToWidgetRequest, reply: any) { if (!window.parent) return; From 4fac7810517795061ec6a750b49d46ed31ebf059 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 18 Apr 2020 14:53:48 +0300 Subject: [PATCH 002/181] Tell widgets to terminate gracefully in AppTile:_endWidgetActions If the widget fails to terminate in two seconds, proceed with disposing the iframe nevertheless. This allows e.g. Jitsi to hangup the conference when minimizing the widget, instead of abrupt disconnect that can leave ghost users behind. Signed-off-by: Pauli Virtanen --- src/components/views/elements/AppTile.js | 74 ++++++++++++++---------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 58bfda8d22..5176753037 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -340,23 +340,30 @@ export default class AppTile extends React.Component { /** * Ends all widget interaction, such as cancelling calls and disabling webcams. * @private + * @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed. */ _endWidgetActions() { - // HACK: This is a really dirty way to ensure that Jitsi cleans up - // its hold on the webcam. Without this, the widget holds a media - // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 - if (this._appFrame.current) { - // In practice we could just do `+= ''` to trick the browser - // into thinking the URL changed, however I can foresee this - // being optimized out by a browser. Instead, we'll just point - // the iframe at a page that is reasonably safe to use in the - // event the iframe doesn't wink away. - // This is relative to where the Riot instance is located. - this._appFrame.current.src = 'about:blank'; - } + const timeout = 2000; + const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout)); + const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); - // Delete the widget from the persisted store for good measure. - PersistedElement.destroyElement(this._persistKey); + return Promise.race([messaging.terminate(), timeoutPromise]).finally(() => { + // HACK: This is a really dirty way to ensure that Jitsi cleans up + // its hold on the webcam. Without this, the widget holds a media + // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 + if (this._appFrame.current) { + // In practice we could just do `+= ''` to trick the browser + // into thinking the URL changed, however I can foresee this + // being optimized out by a browser. Instead, we'll just point + // the iframe at a page that is reasonably safe to use in the + // event the iframe doesn't wink away. + // This is relative to where the Riot instance is located. + this._appFrame.current.src = 'about:blank'; + } + + // Delete the widget from the persisted store for good measure. + PersistedElement.destroyElement(this._persistKey); + }); } /* If user has permission to modify widgets, delete the widget, @@ -380,21 +387,21 @@ export default class AppTile extends React.Component { } this.setState({deleting: true}); - this._endWidgetActions(); + this._endWidgetActions().then(() => { + WidgetUtils.setRoomWidget( + this.props.room.roomId, + this.props.app.id, + ).catch((e) => { + console.error('Failed to delete widget', e); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - WidgetUtils.setRoomWidget( - this.props.room.roomId, - this.props.app.id, - ).catch((e) => { - console.error('Failed to delete widget', e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - - Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { - title: _t('Failed to remove widget'), - description: _t('An error ocurred whilst trying to remove the widget from the room'), + Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { + title: _t('Failed to remove widget'), + description: _t('An error ocurred whilst trying to remove the widget from the room'), + }); + }).finally(() => { + this.setState({deleting: false}); }); - }).finally(() => { - this.setState({deleting: false}); }); }, }); @@ -545,13 +552,18 @@ export default class AppTile extends React.Component { if (this.props.userWidget) { this._onMinimiseClick(); } else { + let promise; if (this.props.show) { // if we were being shown, end the widget as we're about to be minimized. - this._endWidgetActions(); + promise = this._endWidgetActions(); + } else { + promise = Promise.resolve(); } - dis.dispatch({ - action: 'appsDrawer', - show: !this.props.show, + promise.then(() => { + dis.dispatch({ + action: 'appsDrawer', + show: !this.props.show, + }); }); } } From 94745e9407a5a772febf74608b536f5d70797417 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 18 Apr 2020 16:57:19 +0300 Subject: [PATCH 003/181] Minimize widget immediately, and end it later Signed-off-by: Pauli Virtanen --- src/components/views/elements/AppTile.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 5176753037..100a31bdcc 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -39,6 +39,7 @@ import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import {aboveLeftOf, ContextMenu, ContextMenuButton} from "../../structures/ContextMenu"; import PersistedElement from "./PersistedElement"; import {WidgetType} from "../../../widgets/WidgetType"; +import {sleep} from "../../../utils/promise"; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; const ENABLE_REACT_PERF = false; @@ -344,10 +345,9 @@ export default class AppTile extends React.Component { */ _endWidgetActions() { const timeout = 2000; - const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout)); const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); - return Promise.race([messaging.terminate(), timeoutPromise]).finally(() => { + return Promise.race([messaging.terminate(), sleep(timeout)]).finally(() => { // HACK: This is a really dirty way to ensure that Jitsi cleans up // its hold on the webcam. Without this, the widget holds a media // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 @@ -552,18 +552,13 @@ export default class AppTile extends React.Component { if (this.props.userWidget) { this._onMinimiseClick(); } else { - let promise; if (this.props.show) { // if we were being shown, end the widget as we're about to be minimized. - promise = this._endWidgetActions(); - } else { - promise = Promise.resolve(); + this._endWidgetActions(); } - promise.then(() => { - dis.dispatch({ - action: 'appsDrawer', - show: !this.props.show, - }); + dis.dispatch({ + action: 'appsDrawer', + show: !this.props.show, }); } } From 352ea29d1733569ad86e7ffbfb960e4d05bde55d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 18 Apr 2020 17:04:56 +0300 Subject: [PATCH 004/181] Implement widget ReceiveTerminate capability Signed-off-by: Pauli Virtanen --- src/components/views/elements/AppTile.js | 14 +++++++++++--- src/utils/WidgetUtils.js | 1 + src/widgets/WidgetApi.ts | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 100a31bdcc..18be0eeb67 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -344,10 +344,18 @@ export default class AppTile extends React.Component { * @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed. */ _endWidgetActions() { - const timeout = 2000; - const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); + let promise; - return Promise.race([messaging.terminate(), sleep(timeout)]).finally(() => { + if (this._hasCapability('m.receive_terminate')) { + // Wait for widget to terminate within a timeout + const timeout = 2000; + const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); + promise = Promise.race([messaging.terminate(), sleep(timeout)]); + } else { + promise = Promise.resolve(); + } + + return promise.finally(() => { // HACK: This is a really dirty way to ensure that Jitsi cleans up // its hold on the webcam. Without this, the widget holds a media // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 6a0556c2b3..11dd5a88f7 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -420,6 +420,7 @@ export default class WidgetUtils { if (WidgetType.JITSI.matches(appType)) { capWhitelist.push(Capability.AlwaysOnScreen); } + capWhitelist.push(Capability.ReceiveTerminate); return capWhitelist; } diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index e08476edb5..6a29955713 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -23,6 +23,7 @@ export enum Capability { Screenshot = "m.capability.screenshot", Sticker = "m.sticker", AlwaysOnScreen = "m.always_on_screen", + ReceiveTerminate = "m.receive_terminate", } export enum KnownWidgetActions { From cf4137d4b23e5af5baf3c3a1f056da789e51d8b7 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 22 Apr 2020 19:20:28 +0300 Subject: [PATCH 005/181] Make WidgetAPI an EventEmitter + use for terminate + cleanups Use EventEmitter for emitting events, rename terminate event code, plus misc cleanups from review. Signed-off-by: Pauli Virtanen --- src/WidgetMessaging.js | 2 -- src/components/views/elements/AppTile.js | 22 +++++++++--------- src/widgets/WidgetApi.ts | 29 ++++++++++++++---------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index 6a20c340a5..375e7dd8a3 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -89,8 +89,6 @@ export default class WidgetMessaging { /** * Tells the widget that it should terminate now. - * It is not necessarily called in all instances before the widget is removed, - * and the client may force termination with a timeout. * @returns {Promise<*>} Resolves when widget has acknowledged the message. */ terminate() { diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 18be0eeb67..057643c725 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -346,7 +346,7 @@ export default class AppTile extends React.Component { _endWidgetActions() { let promise; - if (this._hasCapability('m.receive_terminate')) { + if (this._hasCapability('im.vector.receive_terminate')) { // Wait for widget to terminate within a timeout const timeout = 2000; const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); @@ -396,20 +396,20 @@ export default class AppTile extends React.Component { this.setState({deleting: true}); this._endWidgetActions().then(() => { - WidgetUtils.setRoomWidget( + return WidgetUtils.setRoomWidget( this.props.room.roomId, this.props.app.id, - ).catch((e) => { - console.error('Failed to delete widget', e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + ); + }).catch((e) => { + console.error('Failed to delete widget', e); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { - title: _t('Failed to remove widget'), - description: _t('An error ocurred whilst trying to remove the widget from the room'), - }); - }).finally(() => { - this.setState({deleting: false}); + Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { + title: _t('Failed to remove widget'), + description: _t('An error ocurred whilst trying to remove the widget from the room'), }); + }).finally(() => { + this.setState({deleting: false}); }); }, }); diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index 6a29955713..c5420dca38 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -18,12 +18,13 @@ limitations under the License. // https://github.com/turt2live/matrix-dimension/blob/4f92d560266635e5a3c824606215b84e8c0b19f5/web/app/shared/services/scalar/scalar-widget.api.ts import { randomString } from "matrix-js-sdk/src/randomstring"; +import { EventEmitter } from "events"; export enum Capability { Screenshot = "m.capability.screenshot", Sticker = "m.sticker", AlwaysOnScreen = "m.always_on_screen", - ReceiveTerminate = "m.receive_terminate", + ReceiveTerminate = "im.vector.receive_terminate", } export enum KnownWidgetActions { @@ -64,14 +65,17 @@ export interface FromWidgetRequest extends WidgetRequest { /** * Handles Riot <--> Widget interactions for embedded/standalone widgets. + * + * Emitted events: + * - terminate(wait): client requested the widget to terminate. + * Call the argument 'wait(promise)' to postpone the finalization until + * the given promise resolves. */ -export class WidgetApi { +export class WidgetApi extends EventEmitter { private origin: string; private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {}; private readyPromise: Promise; private readyPromiseResolve: () => void; - private terminatePromise: Promise; - private terminatePromiseResolve: () => void; /** * Set this to true if your widget is expecting a ready message from the client. False otherwise (default). @@ -79,10 +83,11 @@ export class WidgetApi { public expectingExplicitReady = false; constructor(currentUrl: string, private widgetId: string, private requestedCapabilities: string[]) { + super(); + this.origin = new URL(currentUrl).origin; this.readyPromise = new Promise(resolve => this.readyPromiseResolve = resolve); - this.terminatePromise = new Promise(resolve => this.terminatePromiseResolve = resolve); window.addEventListener("message", event => { if (event.origin !== this.origin) return; // ignore: invalid origin @@ -104,11 +109,15 @@ export class WidgetApi { // Automatically acknowledge so we can move on this.replyToRequest(payload, {}); } else if (payload.action === KnownWidgetActions.Terminate) { - // Reply after resolving - this.terminatePromise.then(() => { + // Finalization needs to be async, so postpone with a promise + let finalizePromise = Promise.resolve(); + const wait = promise => { + finalizePromise = finalizePromise.then(value => promise); + } + this.emit('terminate', wait); + Promise.resolve(finalizePromise).then(() => { this.replyToRequest(payload, {}); }); - this.terminatePromiseResolve(); } else { console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`); } @@ -127,10 +136,6 @@ export class WidgetApi { return this.readyPromise; } - public addTerminateCallback(action) { - this.terminatePromise = this.terminatePromise.then(action); - } - private replyToRequest(payload: ToWidgetRequest, reply: any) { if (!window.parent) return; From 798f5d401ba89c96fd1467afe343a7caba0da5a2 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 11 Apr 2020 15:23:32 +0300 Subject: [PATCH 006/181] Ensure active Jitsi conference is closed on widget pop-out Signed-off-by: Pauli Virtanen --- src/components/views/elements/AppTile.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 057643c725..f5664fd613 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -671,6 +671,17 @@ export default class AppTile extends React.Component { } _onPopoutWidgetClick() { + // Ensure Jitsi conferences are closed on pop-out, to not confuse the user to join them + // twice from the same computer, which Jitsi can have problems with (audio echo/gain-loop). + if (WidgetType.JITSI.matches(this.props.app.type) && this.props.show) { + this._endWidgetActions().then(() => { + if (this._appFrame.current) { + // Reload iframe + this._appFrame.current.src = this._getRenderedUrl(); + this.setState({}); + } + }); + } // Using Object.assign workaround as the following opens in a new window instead of a new tab. // window.open(this._getPopoutUrl(), '_blank', 'noopener=yes'); Object.assign(document.createElement('a'), From 38962560acce8fdb09705eee3590ae5331028eea Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Tue, 28 Apr 2020 02:18:43 +0300 Subject: [PATCH 007/181] Style fixes Signed-off-by: Pauli Virtanen --- src/components/views/elements/AppTile.js | 11 ++++++----- src/widgets/WidgetApi.ts | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index f5664fd613..ef0075e800 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -39,6 +39,7 @@ import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import {aboveLeftOf, ContextMenu, ContextMenuButton} from "../../structures/ContextMenu"; import PersistedElement from "./PersistedElement"; import {WidgetType} from "../../../widgets/WidgetType"; +import {Capability} from "../../../widgets/WidgetApi"; import {sleep} from "../../../utils/promise"; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; @@ -344,18 +345,18 @@ export default class AppTile extends React.Component { * @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed. */ _endWidgetActions() { - let promise; + let terminationPromise; - if (this._hasCapability('im.vector.receive_terminate')) { + if (this._hasCapability(Capability.ReceiveTerminate)) { // Wait for widget to terminate within a timeout const timeout = 2000; const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); - promise = Promise.race([messaging.terminate(), sleep(timeout)]); + terminationPromise = Promise.race([messaging.terminate(), sleep(timeout)]); } else { - promise = Promise.resolve(); + terminationPromise = Promise.resolve(); } - return promise.finally(() => { + return terminationPromise.finally(() => { // HACK: This is a really dirty way to ensure that Jitsi cleans up // its hold on the webcam. Without this, the widget holds a media // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351 diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index c5420dca38..795c6648ef 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -111,11 +111,12 @@ export class WidgetApi extends EventEmitter { } else if (payload.action === KnownWidgetActions.Terminate) { // Finalization needs to be async, so postpone with a promise let finalizePromise = Promise.resolve(); - const wait = promise => { + const wait = (promise) => { finalizePromise = finalizePromise.then(value => promise); - } + }; this.emit('terminate', wait); Promise.resolve(finalizePromise).then(() => { + // Acknowledge that we're shut down now this.replyToRequest(payload, {}); }); } else { From c0ac44e4711e55f32501365045e14576665a0aec Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Tue, 2 Jun 2020 17:10:22 +0100 Subject: [PATCH 008/181] Change internal font size from from 15 to 20. --- res/css/_common.scss | 2 +- res/css/_font-sizes.scss | 112 +++++++++++++-------------- src/settings/Settings.js | 2 +- src/settings/watchers/FontWatcher.ts | 3 +- 4 files changed, 60 insertions(+), 59 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index ebeeb381e6..c562c3e95f 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,7 +19,7 @@ limitations under the License. @import "./_font-sizes.scss"; :root { - font-size: 15px; + font-size: 20px; } html { diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index 2d7ab67e40..c4a223d66c 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -14,59 +14,59 @@ See the License for the specific language governing permissions and limitations under the License. */ -$font-1px: 0.067rem; -$font-1-5px: 0.100rem; -$font-2px: 0.133rem; -$font-3px: 0.200rem; -$font-4px: 0.267rem; -$font-5px: 0.333rem; -$font-6px: 0.400rem; -$font-7px: 0.467rem; -$font-8px: 0.533rem; -$font-9px: 0.600rem; -$font-10px: 0.667rem; -$font-10-4px: 0.693rem; -$font-11px: 0.733rem; -$font-12px: 0.800rem; -$font-13px: 0.867rem; -$font-14px: 0.933rem; -$font-15px: 1.000rem; -$font-16px: 1.067rem; -$font-17px: 1.133rem; -$font-18px: 1.200rem; -$font-19px: 1.267rem; -$font-20px: 1.3333333rem; -$font-21px: 1.400rem; -$font-22px: 1.467rem; -$font-23px: 1.533rem; -$font-24px: 1.600rem; -$font-25px: 1.667rem; -$font-26px: 1.733rem; -$font-27px: 1.800rem; -$font-28px: 1.867rem; -$font-29px: 1.933rem; -$font-30px: 2.000rem; -$font-31px: 2.067rem; -$font-32px: 2.133rem; -$font-33px: 2.200rem; -$font-34px: 2.267rem; -$font-35px: 2.333rem; -$font-36px: 2.400rem; -$font-37px: 2.467rem; -$font-38px: 2.533rem; -$font-39px: 2.600rem; -$font-40px: 2.667rem; -$font-41px: 2.733rem; -$font-42px: 2.800rem; -$font-43px: 2.867rem; -$font-44px: 2.933rem; -$font-45px: 3.000rem; -$font-46px: 3.067rem; -$font-47px: 3.133rem; -$font-48px: 3.200rem; -$font-49px: 3.267rem; -$font-50px: 3.333rem; -$font-51px: 3.400rem; -$font-52px: 3.467rem; -$font-88px: 5.887rem; -$font-400px: 26.667rem; +$font-1px: 0.05rem; +$font-1-5px: 0.075rem; +$font-2px: 0.1rem; +$font-3px: 0.15rem; +$font-4px: 0.2rem; +$font-5px: 0.25rem; +$font-6px: 0.3rem; +$font-7px: 0.35rem; +$font-8px: 0.4rem; +$font-9px: 0.45rem; +$font-10px: 0.5rem; +$font-10-4px: 0.52rem; +$font-11px: 0.55rem; +$font-12px: 0.6rem; +$font-13px: 0.65rem; +$font-14px: 0.7rem; +$font-15px: 0.75rem; +$font-16px: 0.8rem; +$font-17px: 0.85rem; +$font-18px: 0.9rem; +$font-19px: 0.95rem; +$font-20px: 1.0rem; +$font-21px: 1.05rem; +$font-22px: 1.1rem; +$font-23px: 1.15rem; +$font-24px: 1.2rem; +$font-25px: 1.25rem; +$font-26px: 1.3rem; +$font-27px: 1.35rem; +$font-28px: 1.4rem; +$font-29px: 1.45rem; +$font-30px: 1.5rem; +$font-31px: 1.55rem; +$font-32px: 1.6rem; +$font-33px: 1.65rem; +$font-34px: 1.7rem; +$font-35px: 1.75rem; +$font-36px: 1.8rem; +$font-37px: 1.85rem; +$font-38px: 1.9rem; +$font-39px: 1.95rem; +$font-40px: 2.0rem; +$font-41px: 2.05rem; +$font-42px: 2.1rem; +$font-43px: 2.15rem; +$font-44px: 2.2rem; +$font-45px: 2.25rem; +$font-46px: 2.3rem; +$font-47px: 2.35rem; +$font-48px: 2.4rem; +$font-49px: 2.45rem; +$font-50px: 2.5rem; +$font-51px: 2.55rem; +$font-52px: 2.6rem; +$font-88px: 4.4rem; +$font-400px: 20rem; diff --git a/src/settings/Settings.js b/src/settings/Settings.js index e6aa112c5f..7cb4421a4f 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -180,7 +180,7 @@ export const SETTINGS = { "fontSize": { displayName: _td("Font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 15, + default: 20, controller: new FontSizeController(), }, "useCustomFontSize": { diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index dce9e77e9e..08a809f869 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -45,7 +45,8 @@ export class FontWatcher implements IWatcher { }; private setRootFontSize = (size) => { - const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); + // Externally we tell the user the font is size 15. Internally we use 20. + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE) + 5; if (fontSize !== size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); From 1f1f61377775014dfbe8303eb7d89d3ed6f5c520 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 2 Jun 2020 19:07:46 -0600 Subject: [PATCH 009/181] Add a focus_composer dispatcher action and use it --- src/ContentMessages.tsx | 3 ++- src/components/structures/LeftPanel.js | 3 ++- src/components/structures/LoggedInView.tsx | 5 +++-- src/components/structures/MatrixChat.tsx | 4 ++-- src/components/structures/RoomStatusBar.js | 5 +++-- src/components/structures/RoomView.js | 7 ++++--- src/components/views/elements/ReplyThread.js | 3 ++- src/components/views/rooms/EditMessageComposer.js | 7 ++++--- src/components/views/rooms/SendMessageComposer.js | 3 ++- src/cryptodevices.js | 3 ++- src/dispatcher/actions.ts | 5 +++++ 11 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 249ad8381c..25445b1c74 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -31,6 +31,7 @@ import Spinner from "./components/views/elements/Spinner"; // Polyfill for Canvas.toBlob API using Canvas.toDataURL import "blueimp-canvas-to-blob"; +import { Action } from "./dispatcher/actions"; const MAX_WIDTH = 800; const MAX_HEIGHT = 600; @@ -529,7 +530,7 @@ export default class ContentMessages { dis.dispatch({action: 'upload_started'}); // Focus the composer view - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); function onProgress(ev) { upload.total = ev.total; diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index a1b4f49c56..837064e822 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -27,6 +27,7 @@ import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import Analytics from "../../Analytics"; import RoomList2 from "../views/rooms/RoomList2"; +import {Action} from "../../dispatcher/actions"; const LeftPanel = createReactClass({ @@ -198,7 +199,7 @@ const LeftPanel = createReactClass({ onSearchCleared: function(source) { if (source === "keyboard") { - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); } this.setState({searchExpanded: false}); }, diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1ad38c6f04..cf985187e3 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -51,6 +51,7 @@ import { showToast as showServerLimitToast, hideToast as hideServerLimitToast } from "../../toasts/ServerLimitToast"; +import { Action } from "../../dispatcher/actions"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -346,7 +347,7 @@ class LoggedInView extends React.PureComponent { // refocusing during a paste event will make the // paste end up in the newly focused element, // so dispatch synchronously before paste happens - dis.dispatch({action: 'focus_composer'}, true); + dis.fire(Action.FocusComposer, true); } }; @@ -496,7 +497,7 @@ class LoggedInView extends React.PureComponent { if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) { // synchronous dispatch so we focus before key generates input - dis.dispatch({action: 'focus_composer'}, true); + dis.fire(Action.FocusComposer, true); ev.stopPropagation(); // we should *not* preventDefault() here as // that would prevent typing in the now-focussed composer diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 058a7ba50b..97fdfa262e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -347,7 +347,7 @@ export default class MatrixChat extends React.PureComponent { Analytics.trackPageChange(durationMs); } if (this.focusComposer) { - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); this.focusComposer = false; } } @@ -1363,7 +1363,7 @@ export default class MatrixChat extends React.PureComponent { showNotificationsToast(); } - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); this.setState({ ready: true, }); diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index ae628fd06a..b550b4d756 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -27,6 +27,7 @@ import Resend from '../../Resend'; import * as cryptodevices from '../../cryptodevices'; import dis from '../../dispatcher/dispatcher'; import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils'; +import {Action} from "../../dispatcher/actions"; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -135,12 +136,12 @@ export default createReactClass({ _onResendAllClick: function() { Resend.resendUnsentEvents(this.props.room); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }, _onCancelAllClick: function() { Resend.cancelUnsentEvents(this.props.room); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }, _onShowDevicesClick: function() { diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c87f4cc4dd..81983b9708 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -55,6 +55,7 @@ import {haveTileForEvent} from "../views/rooms/EventTile"; import RoomContext from "../../contexts/RoomContext"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { shieldStatusForRoom } from '../../utils/ShieldUtils'; +import {Action} from "../../dispatcher/actions"; const DEBUG = false; let debuglog = function() {}; @@ -1171,7 +1172,7 @@ export default createReactClass({ ev.dataTransfer.files, this.state.room.roomId, this.context, ); this.setState({ draggingFile: false }); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }, onDragLeaveOrEnd: function(ev) { @@ -1377,7 +1378,7 @@ export default createReactClass({ event: null, }); } - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }, onLeaveClick: function() { @@ -1488,7 +1489,7 @@ export default createReactClass({ // jump down to the bottom of this room, where new events are arriving jumpToLiveTimeline: function() { this._messagePanel.jumpToLiveTimeline(); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }, // jump up to wherever our read marker is diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index e7f7196ac6..e96d9ced11 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -26,6 +26,7 @@ import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks import SettingsStore from "../../../settings/SettingsStore"; import escapeHtml from "escape-html"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {Action} from "../../../dispatcher/actions"; // This component does no cycle detection, simply because the only way to make such a cycle would be to // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would @@ -290,7 +291,7 @@ export default class ReplyThread extends React.Component { events, }, this.loadNextEvent); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); } render() { diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index b70ef6255c..78c7de887d 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -31,6 +31,7 @@ import {EventStatus} from 'matrix-js-sdk'; import BasicMessageComposer from "./BasicMessageComposer"; import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {Action} from "../../../dispatcher/actions"; function _isReply(mxEvent) { const relatesTo = mxEvent.getContent()["m.relates_to"]; @@ -157,7 +158,7 @@ export default class EditMessageComposer extends React.Component { dis.dispatch({action: 'edit_event', event: nextEvent}); } else { dis.dispatch({action: 'edit_event', event: null}); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); } event.preventDefault(); } @@ -165,7 +166,7 @@ export default class EditMessageComposer extends React.Component { _cancelEdit = () => { dis.dispatch({action: "edit_event", event: null}); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); } _isContentModified(newContent) { @@ -195,7 +196,7 @@ export default class EditMessageComposer extends React.Component { // close the event editing and focus composer dis.dispatch({action: "edit_event", event: null}); - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); }; _cancelPreviousPendingEdit() { diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 3098c62433..25ad192ea4 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -44,6 +44,7 @@ import {Key} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import RateLimitedFunc from '../../../ratelimitedfunc'; +import {Action} from "../../../dispatcher/actions"; function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) { const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent); @@ -364,7 +365,7 @@ export default class SendMessageComposer extends React.Component { onAction = (payload) => { switch (payload.action) { case 'reply_to_event': - case 'focus_composer': + case Action.FocusComposer: this._editorRef && this._editorRef.focus(); break; case 'insert_mention': diff --git a/src/cryptodevices.js b/src/cryptodevices.js index 86b97364f9..0b3dac5434 100644 --- a/src/cryptodevices.js +++ b/src/cryptodevices.js @@ -19,6 +19,7 @@ import * as sdk from './index'; import dis from './dispatcher/dispatcher'; import Modal from './Modal'; import { _t } from './languageHandler'; +import {Action} from "./dispatcher/actions"; /** * Mark all given devices as 'known' @@ -66,7 +67,7 @@ export async function getUnknownDevicesForRoom(matrixClient, room) { } function focusComposer() { - dis.dispatch({action: 'focus_composer'}); + dis.fire(Action.FocusComposer); } /** diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 7e76ea5ccb..71493d6e44 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -53,4 +53,9 @@ export enum Action { * Provide status information for an ongoing update check. Should be used with a CheckUpdatesPayload. */ CheckUpdates = "check_updates", + + /** + * Focuses the user's cursor to the composer. No additional payload information required. + */ + FocusComposer = "focus_composer", } From 6d96d66c031d2cb9753653a079ad3ed9694a7abf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 2 Jun 2020 19:26:07 -0600 Subject: [PATCH 010/181] Split the left panel into new and old for new room list designs Though we consider the "room list" to mean the RoomList component specifically, the room list is actually the entire left panel as far as the user is concerned. The new proposed designs for the room list modify the whole left panel, so we had might as well break it into new and old now instead of later. This "new" left panel is a bare-bones implementation and meant to only provide the absolute basic feature set to function for those who enable the experimental room list, for whatever reason. This is not intended to be a final implementation, or even remotely close to what it could be. An example of this is the lack of breadcrumbs. Given they are likely to change, they are excluded from this temporary skeleton completely. This also includes a purple/pink bar between the tag panel and left panel. This is so we can, if needed, differentiate between people who made the mistake of turning on the experimental room list while the overall aesthetic makes it indistinguishable. Once the designs are moderately approved, we can (and definitely should) remove the hideous indicator. --- res/css/structures/_LeftPanel.scss | 8 ++ src/components/structures/LeftPanel.js | 32 ++--- src/components/structures/LeftPanel2.tsx | 154 +++++++++++++++++++++ src/components/structures/LoggedInView.tsx | 21 ++- 4 files changed, 187 insertions(+), 28 deletions(-) create mode 100644 src/components/structures/LeftPanel2.tsx diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 7d57425f6f..6edee5eed2 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -22,6 +22,14 @@ limitations under the License. flex: 0 0 auto; } +// TODO: Remove temporary indicator of new room list implementation. +// This border is meant to visually distinguish between the two components when the +// user has turned on the new room list implementation, at least until the designs +// themselves give it away. +.mx_LeftPanel2 .mx_LeftPanel { + border-left: 5px #e26dff solid; +} + .mx_LeftPanel_container.collapsed { min-width: unset; /* Collapsed LeftPanel 50px */ diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index 837064e822..05cd97df2a 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -26,7 +26,6 @@ import * as VectorConferenceHandler from '../../VectorConferenceHandler'; import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import Analytics from "../../Analytics"; -import RoomList2 from "../views/rooms/RoomList2"; import {Action} from "../../dispatcher/actions"; @@ -275,28 +274,15 @@ const LeftPanel = createReactClass({ breadcrumbs = (); } - let roomList = null; - if (SettingsStore.isFeatureEnabled("feature_new_room_list")) { - roomList = ; - } else { - roomList = ; - } + const roomList = ; return (
diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx new file mode 100644 index 0000000000..c9a4948539 --- /dev/null +++ b/src/components/structures/LeftPanel2.tsx @@ -0,0 +1,154 @@ +/* +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 * as React from "react"; +import TagPanel from "./TagPanel"; +import classNames from "classnames"; +import dis from "../../dispatcher/dispatcher"; +import AccessibleButton from "../views/elements/AccessibleButton"; +import { _t } from "../../languageHandler"; +import SearchBox from "./SearchBox"; +import RoomList2 from "../views/rooms/RoomList2"; +import TopLeftMenuButton from "./TopLeftMenuButton"; +import { Action } from "../../dispatcher/actions"; + +/******************************************************************* + * CAUTION * + ******************************************************************* + * This is a work in progress implementation and isn't complete or * + * even useful as a component. Please avoid using it until this * + * warning disappears. * + *******************************************************************/ + +interface IProps { + // TODO: Support collapsed state +} + +interface IState { + searchExpanded: boolean; + searchFilter: string; // TODO: Move search into room list? +} + +export default class LeftPanel2 extends React.Component { + // TODO: Properly support TagPanel + // TODO: Properly support searching/filtering + // TODO: Properly support breadcrumbs + // TODO: Properly support TopLeftMenu (User Settings) + // TODO: a11y + // TODO: actually make this useful in general (match design proposals) + // TODO: Fadable support (is this still needed?) + + constructor(props: IProps) { + super(props); + + this.state = { + searchExpanded: false, + searchFilter: "", + }; + } + + private onSearch = (term: string): void => { + this.setState({searchFilter: term}); + }; + + private onSearchCleared = (source: string): void => { + if (source === "keyboard") { + dis.fire(Action.FocusComposer); + } + this.setState({searchExpanded: false}); + } + + private onSearchFocus = (): void => { + this.setState({searchExpanded: true}); + }; + + private onSearchBlur = (event: FocusEvent): void => { + const target = event.target as HTMLInputElement; + if (target.value.length === 0) { + this.setState({searchExpanded: false}); + } + } + + public render(): React.ReactNode { + const tagPanel = ( +
+ +
+ ); + + const exploreButton = ( +
+ dis.dispatch({action: 'view_room_directory'})}> + {_t("Explore")} + +
+ ); + + const searchBox = ( {/*TODO*/}} + onSearch={this.onSearch} + onCleared={this.onSearchCleared} + onFocus={this.onSearchFocus} + onBlur={this.onSearchBlur} + collapsed={false}/>); // TODO: Collapsed support + + // TODO: Improve props for RoomList2 + const roomList = {/*TODO*/}} + resizeNotifier={null} + collapsed={false} + searchFilter={this.state.searchFilter} + onFocus={() => {/*TODO*/}} + onBlur={() => {/*TODO*/}} + />; + + // TODO: Breadcrumbs + // TODO: Conference handling / calls + + const containerClasses = classNames({ + "mx_LeftPanel_container": true, + "mx_fadable": true, + "collapsed": false, // TODO: Collapsed support + "mx_LeftPanel_container_hasTagPanel": true, // TODO: TagPanel support + "mx_fadable_faded": false, + "mx_LeftPanel2": true, // TODO: Remove flag when RoomList2 ships (used as an indicator) + }); + + return ( +
+ {tagPanel} + +
+ ); + } +} diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index cf985187e3..d09706402a 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -52,6 +52,7 @@ import { hideToast as hideServerLimitToast } from "../../toasts/ServerLimitToast"; import { Action } from "../../dispatcher/actions"; +import LeftPanel2 from "./LeftPanel2"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -656,6 +657,20 @@ class LoggedInView extends React.PureComponent { bodyClasses += ' mx_MatrixChat_useCompactLayout'; } + let leftPanel = ( + + ); + if (SettingsStore.isFeatureEnabled("feature_new_room_list")) { + // TODO: Supply props like collapsed and disabled to LeftPanel2 + leftPanel = ( + + ); + } + return (
{
- + { leftPanel } { pageElement }
From d33cd5d6f826ff99313fd68b8dae64618a10f7fc Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 3 Jun 2020 02:57:55 +0000 Subject: [PATCH 011/181] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2303 of 2303 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 0d89863155..9022acca08 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2480,5 +2480,6 @@ "Restart": "重新啟動", "Upgrade your Riot": "升級您的 Riot", "A new version of Riot is available!": "已有新版的 Riot!", - "New version available. Update now.": "有可用的新版本。立刻更新。" + "New version available. Update now.": "有可用的新版本。立刻更新。", + "Emoji picker": "顏文字挑選器" } From a165066d195279a3da4f258937f6380f4738eda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 2 Jun 2020 15:20:44 +0000 Subject: [PATCH 012/181] Translated using Weblate (Estonian) Currently translated at 57.3% (1320 of 2303 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 81 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index d00525b420..eccfec8ef5 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1276,5 +1276,84 @@ "Keys restored": "Krüptimise võtmed on taastatud", "Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s sessiooni dekrüptimine ei õnnestunud!", "Successfully restored %(sessionCount)s keys": "%(sessionCount)s sessiooni võtme taastamine õnnestus", - "Warning: you should only set up key backup from a trusted computer.": "Hoiatus: sa peaksid võtmete varunduse seadistama vaid usaldusväärsest arvutist." + "Warning: you should only set up key backup from a trusted computer.": "Hoiatus: sa peaksid võtmete varunduse seadistama vaid usaldusväärsest arvutist.", + "eg: @bot:* or example.org": "näiteks: @bot:* või example.org", + "Subscribed lists": "Tellitud loendid", + "Subscribe": "Telli", + "Start automatically after system login": "Käivita automaatselt peale arvutisse sisselogimist", + "Always show the window menu bar": "Näita alati aknas menüüriba", + "Show tray icon and minimize window to it on close": "Näita süsteemisalve ikooni ja Rioti'i akna sulgemisel minimeeri ta salve", + "Preferences": "Eelistused", + "Room list": "Jututubade loend", + "Timeline": "Ajajoon", + "Autocomplete delay (ms)": "Viivitus automaatsel sõnalõpetusel (ms)", + "Server Name": "Serveri nimi", + "Enter password": "Sisesta salasõna", + "Nice, strong password!": "Vahva, see on korralik salasõna!", + "Password is allowed, but unsafe": "Selline salasõna on küll lubatud, kuid üsna ebaturvaline", + "Keep going...": "Jätka...", + "The email field must not be blank.": "E-posti aadressi väli ei tohi olla tühi.", + "The username field must not be blank.": "Kasutajanime väli ei tohi olla tühi.", + "The phone number field must not be blank.": "Telefoninumbri väli ei tohi olla tühi.", + "The password field must not be blank.": "Salasõna väli ei tohi olla tühi.", + "Email": "E-posti aadress", + "Username": "Kasutajanimi", + "Phone": "Telefon", + "Not sure of your password? Set a new one": "Sa ei ole kindel oma salasõnas? Tee uus salasõna", + "Sign in with": "Logi sisse oma kasutajaga", + "No identity server is configured so you cannot add an email address in order to reset your password in the future.": "Ühtegi isikutuvastusserverit pole seadistatud ning sul ei ole võimalik lisada oma e-posti aadressi hilisemaks võimalikuks salasõna muutmiseks.", + "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Kui sa ei sisesta oma e-posti aadressi, siis sa ei saa hiljem oma salasõnamuuta. Kas sa kindlasti soovid seda?", + "Use an email address to recover your account": "Kasuta e-posti aadressi ligipääsu taastamiseks oma kontole", + "Enter email address (required on this homeserver)": "Sisesta e-posti aadress (nõutav selles koduserveris)", + "Doesn't look like a valid email address": "Ei tundu olema korralik e-posti aadress", + "Passwords don't match": "Salasõnad ei klapi", + "Enter phone number (required on this homeserver)": "Sisesta telefoninumber (nõutav selles koduserveris)", + "Doesn't look like a valid phone number": "Ei tundu olema korralik telefoninumber", + "Use lowercase letters, numbers, dashes and underscores only": "Palun kasuta vaid väiketähti, numbreid, sidekriipsu ja alakriipsu", + "Enter username": "Sisesta kasutajanimi", + "Email (optional)": "E-posti aadress (kui soovid)", + "Phone (optional)": "Telefoninumber (kui soovid)", + "Create your Matrix account on %(serverName)s": "Loo oma Matrixi konto %(serverName)s serveris", + "Create your Matrix account on ": "Loo oma Matrixi konto serveris", + "Register": "Registreeru", + "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Seadista e-posti aadress, mis võimaldaks sul vajadusel taastada ligipääsu oma kontole. Lisaks võid kasutada e-posti aadressi või telefoninumbrit, et need inimesed, kes sind tunnevad, saaks sind leida.", + "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Seadista e-posti aadress, mis võimaldaks sul vajadusel taastada ligipääsu oma kontole. Lisaks võid kasutada e-posti aadressi, et need inimesed, kes sind tunnevad, saaks sind leida.", + "Enter your custom homeserver URL What does this mean?": "Sisesta oma koduserveri aadress Mida see tähendab?", + "Homeserver URL": "Koduserveri aadress", + "Enter your custom identity server URL What does this mean?": "Sisesta kohandatud isikutuvastusserver aadress Mida see tähendab?", + "Identity Server URL": "Isikutuvastusserveri aadress", + "Other servers": "Muud serverid", + "Free": "Tasuta teenus", + "Join millions for free on the largest public server": "Liitu tasuta nende miljonitega, kas kasutavad suurimat avalikku Matrix'i serverit", + "Premium": "Tasuline eriteenus", + "Premium hosting for organisations Learn more": "Tasuline Matrix'i majutusteenus organisatsioonidele Loe lisateavet", + "Find other public servers or use a custom server": "Otsi muid avalikke Matrix'i servereid või kasuta enda määratud serverit", + "Sign in to your Matrix account on %(serverName)s": "Logi sisse on Matrix'i kontole %(serverName)s serveris", + "Sign in to your Matrix account on ": "Logi sisse on Matrix'i kontole serveris", + "Sorry, your browser is not able to run Riot.": "Vabandust, aga Riot ei toimi sinu brauseris.", + "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot kasutab mitmeid uusi brauseri-põhiseid tehnoloogiaid ning mitmed neist kas pole veel olemas või on lahendatud sinu brauseris katselisena.", + "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Sinu praeguse brauseriga meie rakenduse välimus ja toimivus võivad olla täitsa valed ning mõni funktsionaalsus ei pruugi toimida üldse. Kui soovid katsetada, siis loomulikult võid jätkata, kuid erinevate tekkivate vigadega pead ise hakkama saama!", + "Fetching third party location failed": "Kolmanda osapoole asukoha tuvastamine ei õnnestunud", + "Unable to look up room ID from server": "Jututoa tunnuse otsimine serverist ei õnnestunud", + "Show sessions, send anyway or cancel.": "Näita sessioone, saada ikkagi või tühista.", + "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Sinu sõnumit ei saadetud, kuna see koduserver on ületanud on ületanud ressursipiirangu. Teenuse kasutamiseks palun võta ühendust serveri haldajaga.", + "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|other": "Saada kõik uuesti või tühista kõigi saatmine. Samuti võid sa valida saatmiseks või saatmise tühistamiseks üksikuid sõnumeid.", + "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Saada sõnum uuesti või tühista sõnumi saatmine.", + "Connectivity to the server has been lost.": "Ühendus sinu serveriga on katkenud.", + "Sent messages will be stored until your connection has returned.": "Saadetud sõnumid salvestatakse seniks, kuni võrguühendus on taastunud.", + "Active call": "Käsilolev kõne", + "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Siin ei leidu kedagi teist! Kas sa tahaksid kutsuda kedagi jututuppa või lõpetada selle tühja jututoa hoiatuse kuvamise?", + "You have %(count)s unread notifications in a prior version of this room.|other": "Sinul on selle jututoa varasemas versioonis %(count)s lugemata teavitust.", + "You have %(count)s unread notifications in a prior version of this room.|one": "Sinul on selle jututoa varasemas versioonis %(count)s lugemata teavitus.", + "Fill screen": "Täida ekraan", + "No identity server is configured: add one in server settings to reset your password.": "Ühtegi isikutuvastusserverit pole seadistatud: salasõna taastamiseks määra see serveri sätetes.", + "Sign in instead": "Pigem logi sisse", + "A verification email will be sent to your inbox to confirm setting your new password.": "Kontrollimaks, et just sina sina ise sisestasid uue salasõna, siis saadame sulle kinnituskirja.", + "Send Reset Email": "Saada salasõna taastamise e-kiri", + "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Saatsime kirja %(emailAddress)s aadressile. Kui sa oled klõpsinud kirjas olnud linki, siis jätka alljärgnevaga.", + "I have verified my email address": "Ma olen teinud läbi oma e-posti aadressi kontrolli", + "Your password has been reset.": "Sinu salasõna on muudetud.", + "Dismiss read marker and jump to bottom": "Ära arvesta loetud sõnumite järjehoidjat ning mine kõige lõppu", + "Jump to oldest unread message": "Mine vanima lugemata sõnumi juurde", + "Upload a file": "Lae fail üles" } From 89c33b2c389960eef07bb60683ccc6396aed6349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 3 Jun 2020 06:39:17 +0000 Subject: [PATCH 013/181] Translated using Weblate (French) Currently translated at 100.0% (2303 of 2303 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 1bd6fd254a..f5cedf902a 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2481,5 +2481,6 @@ "Restart": "Redémarrer", "Upgrade your Riot": "Mettre à niveau votre Riot", "A new version of Riot is available!": "Une nouvelle version de Riot est disponible !", - "New version available. Update now.": "Nouvelle version disponible. Faire la mise à niveau maintenant." + "New version available. Update now.": "Nouvelle version disponible. Faire la mise à niveau maintenant.", + "Emoji picker": "Sélecteur d’émojis" } From 677c7aac451766f386d1a6a075cd816dfb59bc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 3 Jun 2020 11:30:33 +0000 Subject: [PATCH 014/181] Translated using Weblate (Estonian) Currently translated at 57.8% (1304 of 2257 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index eccfec8ef5..0fdcca40d1 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1355,5 +1355,24 @@ "Your password has been reset.": "Sinu salasõna on muudetud.", "Dismiss read marker and jump to bottom": "Ära arvesta loetud sõnumite järjehoidjat ning mine kõige lõppu", "Jump to oldest unread message": "Mine vanima lugemata sõnumi juurde", - "Upload a file": "Lae fail üles" + "Upload a file": "Lae fail üles", + "Read Marker lifetime (ms)": "Lugemise markeri iga (ms)", + "Read Marker off-screen lifetime (ms)": "Lugemise markeri iga, kui Riot pole fookuses (ms)", + "Unignore": "Lõpeta eiramine", + "": "", + "Import E2E room keys": "Impordi E2E läbiva krüptimise võtmed jututubade jaoks", + "Cryptography": "Krüptimine", + "Session ID:": "Sessiooni tunnus:", + "Session key:": "Sessiooni võti:", + "Bulk options": "Masstoimingute seadistused", + "Accept all %(invitedRooms)s invites": "Võta vastu kõik %(invitedRooms)s kutsed", + "Reject all %(invitedRooms)s invites": "Lükka tagasi kõik %(invitedRooms)s kutsed", + "Key backup": "Võtmete varundus", + "Cross-signing": "Risttunnustamine", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Hoiatus: Jututoa versiooni uuendamine ei koli jututoa liikmeid automaatselt uude jututoa olekusse. Vanas jututoa versioonis saab olema viide uuele versioonile ning kõik liikmed peavad seda viidet klõpsama.", + "Uploaded sound": "Üleslaetud heli", + "Sounds": "Helid", + "Notification sound": "Teavitusheli", + "Reset": "Taasta algolek", + "Set a new custom sound": "Seadista uus kohandatud heli" } From 003e75968ac40e5ad77a15059a29c8062ff1ab01 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 3 Jun 2020 12:32:57 +0000 Subject: [PATCH 015/181] Translated using Weblate (Italian) Currently translated at 100.0% (2257 of 2257 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index e5c1b890d7..37c5159546 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2476,5 +2476,6 @@ "Restart": "Riavvia", "Upgrade your Riot": "Aggiorna Riot", "A new version of Riot is available!": "È disponibile una nuova versione di Riot!", - "New version available. Update now.": "Nuova versione disponibile. Aggiorna ora." + "New version available. Update now.": "Nuova versione disponibile. Aggiorna ora.", + "Emoji picker": "Selettore emoji" } From 520bb1e6fad4ca31db472257724df2ef895fb516 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Wed, 3 Jun 2020 14:40:22 +0000 Subject: [PATCH 016/181] Translated using Weblate (Esperanto) Currently translated at 100.0% (2257 of 2257 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 5252deba23..74cf3629bb 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2455,5 +2455,6 @@ "Restart": "Restartigi", "Upgrade your Riot": "Gradaltigi vian Rioton", "A new version of Riot is available!": "Nova versio de Riot estas disponebla!", - "New version available. Update now.": "Nova versio estas disponebla. Ĝisdatigu nun." + "New version available. Update now.": "Nova versio estas disponebla. Ĝisdatigu nun.", + "Emoji picker": "Elektilo de bildsignoj" } From ed2eca52b23d83c43e46bd94a9c51d8c1113a363 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 3 Jun 2020 18:05:40 -0500 Subject: [PATCH 017/181] Don't show FormatBar if composer is empty Signed-off-by: Aaron Raimist --- src/components/views/rooms/BasicMessageComposer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index ba1be6a125..82f61e0e1f 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -359,6 +359,8 @@ export default class BasicMessageEditor extends React.Component { } _onSelectionChange = () => { + const {isEmpty} = this.props.model; + this._refreshLastCaretIfNeeded(); const selection = document.getSelection(); if (this._hasTextSelected && selection.isCollapsed) { @@ -366,7 +368,7 @@ export default class BasicMessageEditor extends React.Component { if (this._formatBarRef) { this._formatBarRef.hide(); } - } else if (!selection.isCollapsed) { + } else if (!selection.isCollapsed && !isEmpty) { this._hasTextSelected = true; if (this._formatBarRef) { const selectionRect = selection.getRangeAt(0).getBoundingClientRect(); From c07b5ebe9a789eaca974cb79090f97474774d09f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 3 Jun 2020 15:07:12 -0600 Subject: [PATCH 018/181] Initial experiments with resizing of room lists --- res/css/views/rooms/_RoomList.scss | 8 ++ src/components/views/rooms/RoomList2.tsx | 76 ++++++++++-------- src/components/views/rooms/RoomSublist2.tsx | 13 ++-- src/stores/room-list/RoomListLayoutStore.ts | 85 +++++++++++++++++++++ src/stores/room-list/RoomListStore2.ts | 3 + 5 files changed, 145 insertions(+), 40 deletions(-) create mode 100644 src/stores/room-list/RoomListLayoutStore.ts diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index 50a9e7ee1f..7abf86cb0e 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -15,6 +15,14 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_RoomList2_resizer { + cursor: ns-resize; +} + +.mx_RoomList.mx_RoomList2 { + overflow-y: auto; +} + .mx_RoomList { /* take up remaining space below TopLeftMenu */ flex: 1; diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index e732e70edf..5e9f6ffb23 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -21,14 +21,13 @@ import { _t, _td } from "../../../languageHandler"; import { Layout } from '../../../resizer/distributors/roomsublist2'; import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex"; import { ResizeNotifier } from "../../../utils/ResizeNotifier"; -import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore2"; +import RoomListStore, { LISTS_UPDATE_EVENT, RoomListStore2 } from "../../../stores/room-list/RoomListStore2"; import { ITagMap } from "../../../stores/room-list/algorithms/models"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { Dispatcher } from "flux"; import dis from "../../../dispatcher/dispatcher"; import RoomSublist2 from "./RoomSublist2"; import { ActionPayload } from "../../../dispatcher/payloads"; -import { IFilterCondition } from "../../../stores/room-list/filters/IFilterCondition"; import { NameFilterCondition } from "../../../stores/room-list/filters/NameFilterCondition"; /******************************************************************* @@ -50,6 +49,7 @@ interface IProps { interface IState { sublists: ITagMap; + heights: Map; } const TAG_ORDER: TagID[] = [ @@ -133,13 +133,16 @@ export default class RoomList2 extends React.Component { private unfilteredLayout: Layout; private filteredLayout: Layout; private searchFilter: NameFilterCondition = new NameFilterCondition(); + private currentTagResize: TagID = null; constructor(props: IProps) { super(props); - this.state = {sublists: {}}; + this.state = { + sublists: {}, + heights: new Map(), + }; this.loadSublistSizes(); - this.prepareLayouts(); } public componentDidUpdate(prevProps: Readonly): void { @@ -158,9 +161,16 @@ export default class RoomList2 extends React.Component { } public componentDidMount(): void { - RoomListStore.instance.on(LISTS_UPDATE_EVENT, (store) => { - console.log("new lists", store.orderedLists); - this.setState({sublists: store.orderedLists}); + RoomListStore.instance.on(LISTS_UPDATE_EVENT, (store: RoomListStore2) => { + const newLists = store.orderedLists; + console.log("new lists", newLists); + + const heightMap = new Map(); + for (const tagId of Object.keys(newLists)) { + heightMap.set(tagId, store.layout.getPixelHeight(tagId)); + } + + this.setState({sublists: newLists, heights: heightMap}); }); } @@ -177,32 +187,24 @@ export default class RoomList2 extends React.Component { window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.sublistCollapseStates)); } - private prepareLayouts() { - // TODO: Change layout engine for FTUE support - this.unfilteredLayout = new Layout((tagId: string, height: number) => { - const sublist = this.sublistRefs[tagId]; - if (sublist) sublist.current.setHeight(height); + private onResizerMouseDown = (ev: React.MouseEvent) => { + const hr = ev.target as HTMLHRElement; + this.currentTagResize = hr.getAttribute("data-id"); + }; - // TODO: Check overflow (see old impl) + private onResizerMouseUp = (ev: React.MouseEvent) => { + this.currentTagResize = null; + }; - // Don't store a height for collapsed sublists - if (!this.sublistCollapseStates[tagId]) { - this.sublistSizes[tagId] = height; - this.saveSublistSizes(); - } - }, this.sublistSizes, this.sublistCollapseStates, { - allowWhitespace: false, - handleHeight: 1, - }); - - this.filteredLayout = new Layout((tagId: string, height: number) => { - const sublist = this.sublistRefs[tagId]; - if (sublist) sublist.current.setHeight(height); - }, null, null, { - allowWhitespace: false, - handleHeight: 0, - }); - } + private onMouseMove = (ev: React.MouseEvent) => { + ev.preventDefault(); + if (this.currentTagResize) { + const pixelHeight = this.state.heights.get(this.currentTagResize); + RoomListStore.instance.layout.setPixelHeight(this.currentTagResize, pixelHeight + ev.movementY); + this.state.heights.set(this.currentTagResize, RoomListStore.instance.layout.getPixelHeight(this.currentTagResize)); + this.forceUpdate(); + } + }; private renderSublists(): React.ReactElement[] { const components: React.ReactElement[] = []; @@ -235,6 +237,14 @@ export default class RoomList2 extends React.Component { onAddRoom={onAddRoomFn} addRoomLabel={aesthetics.addRoomLabel} isInvite={aesthetics.isInvite} + height={this.state.heights.get(orderedTagId)} + />); + components.push(
); } @@ -250,7 +260,9 @@ export default class RoomList2 extends React.Component { onFocus={this.props.onFocus} onBlur={this.props.onBlur} onKeyDown={onKeyDownHandler} - className="mx_RoomList" + onMouseUp={this.onResizerMouseUp} + onMouseMove={this.onMouseMove} + className="mx_RoomList mx_RoomList2" role="tree" aria-label={_t("Rooms")} // Firefox sometimes makes this element focusable due to diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index e2f489b959..2b5b131393 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -45,9 +45,9 @@ interface IProps { onAddRoom?: () => void; addRoomLabel: string; isInvite: boolean; + height: number; // pixels // TODO: Collapsed state - // TODO: Height // TODO: Group invites // TODO: Calls // TODO: forceExpand? @@ -61,10 +61,6 @@ interface IState { export default class RoomSublist2 extends React.Component { private headerButton = createRef(); - public setHeight(size: number) { - // TODO: Do a thing (maybe - height changes are different in FTUE) - } - private hasTiles(): boolean { return this.numTiles > 0; } @@ -205,9 +201,10 @@ export default class RoomSublist2 extends React.Component { // TODO: Lazy list rendering // TODO: Whatever scrolling magic needs to happen here content = ( - - {tiles} - + {tiles} ) } diff --git a/src/stores/room-list/RoomListLayoutStore.ts b/src/stores/room-list/RoomListLayoutStore.ts new file mode 100644 index 0000000000..cbd570b579 --- /dev/null +++ b/src/stores/room-list/RoomListLayoutStore.ts @@ -0,0 +1,85 @@ +/* +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. +*/ + +// TODO: Simplify the class load when we pick an approach for the list layout + +import { TagID } from "./models"; + +const TILE_HEIGHT_PX = 34; + +export class LayoutUnit { + constructor(public readonly multiplier: number) { + } + + public convert(val: number): number { + return Math.ceil(val * this.multiplier); + } + + public normalizePixels(pixels: number): number { + return this.convert(Math.ceil(pixels / this.multiplier)); + } + + public forNumTiles(n: number): number { + const unitsPerTile = TILE_HEIGHT_PX / this.multiplier; + return unitsPerTile * n; + } +} + +export const SMOOTH_RESIZE = new LayoutUnit(1); +export const CHUNKED_RESIZE = new LayoutUnit(TILE_HEIGHT_PX); + +export class RoomListLayoutStore { + public unit: LayoutUnit = SMOOTH_RESIZE; + public minTilesShown = 1; + + /** + * Minimum list height in pixels. + */ + public get minListHeight(): number { + return this.unit.forNumTiles(this.minTilesShown); + } + + private getStorageKey(tagId: TagID) { + return `mx_rlls_${tagId}_m_${this.unit.multiplier}`; + } + + public setPixelHeight(tagId: TagID, pixels: number): void { + localStorage.setItem(this.getStorageKey(tagId), JSON.stringify({pixels})); + } + + public getPixelHeight(tagId: TagID): number { + const stored = JSON.parse(localStorage.getItem(this.getStorageKey(tagId))); + let storedHeight = 0; + if (stored && stored.pixels) { + storedHeight = stored.pixels; + } + return this.unit.normalizePixels(Math.max(this.minListHeight, storedHeight)); + } + + // TODO: Remove helper functions for design iteration + + public beSmooth() { + this.unit = SMOOTH_RESIZE; + } + + public beChunked() { + this.unit = CHUNKED_RESIZE; + } + + public beDifferent(multiplier: number) { + this.unit = new LayoutUnit(multiplier); + } +} diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index af9970d3cc..84033b5cca 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -29,6 +29,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher"; import { readReceiptChangeIsFor } from "../../utils/read-receipts"; import { IFilterCondition } from "./filters/IFilterCondition"; import { TagWatcher } from "./TagWatcher"; +import { RoomListLayoutStore } from "./RoomListLayoutStore"; interface IState { tagsEnabled?: boolean; @@ -44,6 +45,8 @@ interface IState { export const LISTS_UPDATE_EVENT = "lists_update"; export class RoomListStore2 extends AsyncStore { + public readonly layout = new RoomListLayoutStore(); + private _matrixClient: MatrixClient; private initialListsGenerated = false; private enabled = false; From dbf996439bb5cd0e09835f427db97b00046e8d6a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 3 Jun 2020 21:16:53 -0600 Subject: [PATCH 019/181] Wedge t3chguy's resizer prototype into the sublist --- package.json | 1 + res/css/_components.scss | 1 + res/css/views/rooms/_RoomSublist2.scss | 17 +++++ src/components/views/rooms/RoomList2.tsx | 73 +++++------------- src/components/views/rooms/RoomSublist2.tsx | 37 +++++++-- src/stores/room-list/ListLayout.ts | 65 ++++++++++++++++ src/stores/room-list/RoomListLayoutStore.ts | 85 --------------------- src/stores/room-list/RoomListStore2.ts | 3 - yarn.lock | 20 ++++- 9 files changed, 152 insertions(+), 150 deletions(-) create mode 100644 res/css/views/rooms/_RoomSublist2.scss create mode 100644 src/stores/room-list/ListLayout.ts delete mode 100644 src/stores/room-list/RoomListLayoutStore.ts diff --git a/package.json b/package.json index 11906452e7..1029efaccd 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "react-beautiful-dnd": "^4.0.1", "react-dom": "^16.9.0", "react-focus-lock": "^2.2.1", + "react-resizable": "^1.10.1", "resize-observer-polyfill": "^1.5.0", "sanitize-html": "^1.18.4", "text-encoding-utf-8": "^1.0.1", diff --git a/res/css/_components.scss b/res/css/_components.scss index 5a7630a51f..b047519d99 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -179,6 +179,7 @@ @import "./views/rooms/_RoomList.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; +@import "./views/rooms/_RoomSublist2.scss"; @import "./views/rooms/_RoomTile.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss new file mode 100644 index 0000000000..bb760e7e6e --- /dev/null +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -0,0 +1,17 @@ +/* +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 "../../../../node_modules/react-resizable/css/styles.css"; diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 5e9f6ffb23..b6fe37589c 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -18,7 +18,6 @@ limitations under the License. import * as React from "react"; import { _t, _td } from "../../../languageHandler"; -import { Layout } from '../../../resizer/distributors/roomsublist2'; import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex"; import { ResizeNotifier } from "../../../utils/ResizeNotifier"; import RoomListStore, { LISTS_UPDATE_EVENT, RoomListStore2 } from "../../../stores/room-list/RoomListStore2"; @@ -29,6 +28,7 @@ import dis from "../../../dispatcher/dispatcher"; import RoomSublist2 from "./RoomSublist2"; import { ActionPayload } from "../../../dispatcher/payloads"; import { NameFilterCondition } from "../../../stores/room-list/filters/NameFilterCondition"; +import { ListLayout } from "../../../stores/room-list/ListLayout"; /******************************************************************* * CAUTION * @@ -49,7 +49,7 @@ interface IProps { interface IState { sublists: ITagMap; - heights: Map; + layouts: Map; } const TAG_ORDER: TagID[] = [ @@ -127,20 +127,16 @@ const TAG_AESTHETICS: { }; export default class RoomList2 extends React.Component { - private sublistRefs: { [tagId: string]: React.RefObject } = {}; private sublistSizes: { [tagId: string]: number } = {}; private sublistCollapseStates: { [tagId: string]: boolean } = {}; - private unfilteredLayout: Layout; - private filteredLayout: Layout; private searchFilter: NameFilterCondition = new NameFilterCondition(); - private currentTagResize: TagID = null; constructor(props: IProps) { super(props); this.state = { sublists: {}, - heights: new Map(), + layouts: new Map(), }; this.loadSublistSizes(); } @@ -165,12 +161,12 @@ export default class RoomList2 extends React.Component { const newLists = store.orderedLists; console.log("new lists", newLists); - const heightMap = new Map(); + const layoutMap = new Map(); for (const tagId of Object.keys(newLists)) { - heightMap.set(tagId, store.layout.getPixelHeight(tagId)); + layoutMap.set(tagId, new ListLayout(tagId)); } - this.setState({sublists: newLists, heights: heightMap}); + this.setState({sublists: newLists, layouts: layoutMap}); }); } @@ -182,30 +178,6 @@ export default class RoomList2 extends React.Component { if (collapsedJson) this.sublistCollapseStates = JSON.parse(collapsedJson); } - private saveSublistSizes() { - window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.sublistSizes)); - window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.sublistCollapseStates)); - } - - private onResizerMouseDown = (ev: React.MouseEvent) => { - const hr = ev.target as HTMLHRElement; - this.currentTagResize = hr.getAttribute("data-id"); - }; - - private onResizerMouseUp = (ev: React.MouseEvent) => { - this.currentTagResize = null; - }; - - private onMouseMove = (ev: React.MouseEvent) => { - ev.preventDefault(); - if (this.currentTagResize) { - const pixelHeight = this.state.heights.get(this.currentTagResize); - RoomListStore.instance.layout.setPixelHeight(this.currentTagResize, pixelHeight + ev.movementY); - this.state.heights.set(this.currentTagResize, RoomListStore.instance.layout.getPixelHeight(this.currentTagResize)); - this.forceUpdate(); - } - }; - private renderSublists(): React.ReactElement[] { const components: React.ReactElement[] = []; @@ -228,24 +200,19 @@ export default class RoomList2 extends React.Component { if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`); const onAddRoomFn = aesthetics.onAddRoom ? () => aesthetics.onAddRoom(dis) : null; - components.push(); - components.push(
); + components.push( + + ); } return components; @@ -260,8 +227,6 @@ export default class RoomList2 extends React.Component { onFocus={this.props.onFocus} onBlur={this.props.onBlur} onKeyDown={onKeyDownHandler} - onMouseUp={this.onResizerMouseUp} - onMouseMove={this.onMouseMove} className="mx_RoomList mx_RoomList2" role="tree" aria-label={_t("Rooms")} diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 2b5b131393..6705448764 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -20,7 +20,6 @@ import * as React from "react"; import { createRef } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import classNames from 'classnames'; -import IndicatorScrollbar from "../../structures/IndicatorScrollbar"; import * as RoomNotifs from '../../../RoomNotifs'; import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import { _t } from "../../../languageHandler"; @@ -28,6 +27,8 @@ import AccessibleButton from "../../views/elements/AccessibleButton"; import AccessibleTooltipButton from "../../views/elements/AccessibleTooltipButton"; import * as FormattingUtils from '../../../utils/FormattingUtils'; import RoomTile2 from "./RoomTile2"; +import { ResizableBox, ResizeCallbackData } from "react-resizable"; +import { ListLayout } from "../../../stores/room-list/ListLayout"; /******************************************************************* * CAUTION * @@ -45,7 +46,7 @@ interface IProps { onAddRoom?: () => void; addRoomLabel: string; isInvite: boolean; - height: number; // pixels + layout: ListLayout; // TODO: Collapsed state // TODO: Group invites @@ -183,6 +184,12 @@ export default class RoomSublist2 extends React.Component { ); } + private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => { + const tileDiff = e.movementY < 0 ? -1 : +1; + this.props.layout.visibleTiles += tileDiff; + this.forceUpdate(); // because the layout doesn't trigger a re-render + }; + public render(): React.ReactElement { // TODO: Proper rendering // TODO: Error boundary @@ -200,11 +207,29 @@ export default class RoomSublist2 extends React.Component { if (tiles.length > 0) { // TODO: Lazy list rendering // TODO: Whatever scrolling magic needs to happen here + const layout = this.props.layout; // to shorten calls + const minTilesPx = layout.tilesToPixels(Math.min(tiles.length, layout.minVisibleTiles)); + const maxTilesPx = layout.tilesToPixels(tiles.length); + const tilesPx = layout.tilesToPixels(Math.min(tiles.length, layout.visibleTiles)); + let handles = ['s']; + if (layout.visibleTiles >= tiles.length && tiles.length <= layout.minVisibleTiles) { + handles = []; // no handles, we're at a minimum + } + const visibleTiles = tiles.slice(0, layout.visibleTiles); content = ( - {tiles} + + {visibleTiles} + ) } diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts new file mode 100644 index 0000000000..af6abe3297 --- /dev/null +++ b/src/stores/room-list/ListLayout.ts @@ -0,0 +1,65 @@ +/* +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. +*/ + +const TILE_HEIGHT_PX = 34; + +interface ISerializedListLayout { + numTiles: number; +} + +export class ListLayout { + private _n = 0; + + constructor(public readonly tagId) { + const serialized = localStorage.getItem(this.key); + if (serialized) { + // We don't use the setters as they cause writes. + const parsed = JSON.parse(serialized); + this._n = parsed.numTiles; + } + } + + public get tileHeight(): number { + return TILE_HEIGHT_PX; + } + + private get key(): string { + return `mx_sublist_layout_${this.tagId}_boxed`; + } + + public get visibleTiles(): number { + return Math.max(this._n, this.minVisibleTiles); + } + + public set visibleTiles(v: number) { + this._n = v; + localStorage.setItem(this.key, JSON.stringify(this.serialize())); + } + + public get minVisibleTiles(): number { + return 3; + } + + public tilesToPixels(n: number): number { + return n * this.tileHeight; + } + + private serialize(): ISerializedListLayout { + return { + numTiles: this.visibleTiles, + }; + } +} diff --git a/src/stores/room-list/RoomListLayoutStore.ts b/src/stores/room-list/RoomListLayoutStore.ts deleted file mode 100644 index cbd570b579..0000000000 --- a/src/stores/room-list/RoomListLayoutStore.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* -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. -*/ - -// TODO: Simplify the class load when we pick an approach for the list layout - -import { TagID } from "./models"; - -const TILE_HEIGHT_PX = 34; - -export class LayoutUnit { - constructor(public readonly multiplier: number) { - } - - public convert(val: number): number { - return Math.ceil(val * this.multiplier); - } - - public normalizePixels(pixels: number): number { - return this.convert(Math.ceil(pixels / this.multiplier)); - } - - public forNumTiles(n: number): number { - const unitsPerTile = TILE_HEIGHT_PX / this.multiplier; - return unitsPerTile * n; - } -} - -export const SMOOTH_RESIZE = new LayoutUnit(1); -export const CHUNKED_RESIZE = new LayoutUnit(TILE_HEIGHT_PX); - -export class RoomListLayoutStore { - public unit: LayoutUnit = SMOOTH_RESIZE; - public minTilesShown = 1; - - /** - * Minimum list height in pixels. - */ - public get minListHeight(): number { - return this.unit.forNumTiles(this.minTilesShown); - } - - private getStorageKey(tagId: TagID) { - return `mx_rlls_${tagId}_m_${this.unit.multiplier}`; - } - - public setPixelHeight(tagId: TagID, pixels: number): void { - localStorage.setItem(this.getStorageKey(tagId), JSON.stringify({pixels})); - } - - public getPixelHeight(tagId: TagID): number { - const stored = JSON.parse(localStorage.getItem(this.getStorageKey(tagId))); - let storedHeight = 0; - if (stored && stored.pixels) { - storedHeight = stored.pixels; - } - return this.unit.normalizePixels(Math.max(this.minListHeight, storedHeight)); - } - - // TODO: Remove helper functions for design iteration - - public beSmooth() { - this.unit = SMOOTH_RESIZE; - } - - public beChunked() { - this.unit = CHUNKED_RESIZE; - } - - public beDifferent(multiplier: number) { - this.unit = new LayoutUnit(multiplier); - } -} diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 84033b5cca..af9970d3cc 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -29,7 +29,6 @@ import defaultDispatcher from "../../dispatcher/dispatcher"; import { readReceiptChangeIsFor } from "../../utils/read-receipts"; import { IFilterCondition } from "./filters/IFilterCondition"; import { TagWatcher } from "./TagWatcher"; -import { RoomListLayoutStore } from "./RoomListLayoutStore"; interface IState { tagsEnabled?: boolean; @@ -45,8 +44,6 @@ interface IState { export const LISTS_UPDATE_EVENT = "lists_update"; export class RoomListStore2 extends AsyncStore { - public readonly layout = new RoomListLayoutStore(); - private _matrixClient: MatrixClient; private initialListsGenerated = false; private enabled = false; diff --git a/yarn.lock b/yarn.lock index 56cf596fcf..ded8aa13f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2458,7 +2458,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.1.2: +classnames@^2.1.2, classnames@^2.2.5: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== @@ -6858,7 +6858,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.x, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -7062,6 +7062,14 @@ react-dom@^16.9.0: prop-types "^15.6.2" scheduler "^0.19.1" +react-draggable@^4.0.3: + version "4.4.2" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.2.tgz#f3cefecee25f467f865144cda0d066e5f05f94a0" + integrity sha512-zLQs4R4bnBCGnCVTZiD8hPsHtkiJxgMpGDlRESM+EHQo8ysXhKJ2GKdJ8UxxLJdRVceX1j19jy+hQS2wHislPQ== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + react-focus-lock@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.3.1.tgz#9d5d85899773609c7eefa4fc54fff6a0f5f2fc47" @@ -7106,6 +7114,14 @@ react-redux@^5.0.6: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" +react-resizable@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-1.10.1.tgz#f0c2cf1d83b3470b87676ce6d6b02bbe3f4d8cd4" + integrity sha512-Jd/bKOKx6+19NwC4/aMLRu/J9/krfxlDnElP41Oc+oLiUWs/zwV1S9yBfBZRnqAwQb6vQ/HRSk3bsSWGSgVbpw== + dependencies: + prop-types "15.x" + react-draggable "^4.0.3" + react-test-renderer@^16.0.0-0, react-test-renderer@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1" From be4f75bca920b0a9f45414aea9cc39d8701aba7a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 3 Jun 2020 21:52:05 -0600 Subject: [PATCH 020/181] Add a 'show more' button to room lists --- src/components/views/rooms/RoomSublist2.tsx | 44 ++++++++++++++++++--- src/i18n/strings/en_EN.json | 1 + 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 6705448764..a9852dd3d3 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -76,6 +76,17 @@ export default class RoomSublist2 extends React.Component { if (this.props.onAddRoom) this.props.onAddRoom(); }; + private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => { + const tileDiff = e.movementY < 0 ? -1 : +1; + this.props.layout.visibleTiles += tileDiff; + this.forceUpdate(); // because the layout doesn't trigger a re-render + }; + + private onShowAllClick = () => { + this.props.layout.visibleTiles = this.numTiles; + this.forceUpdate(); // because the layout doesn't trigger a re-render + }; + private renderTiles(): React.ReactElement[] { const tiles: React.ReactElement[] = []; @@ -184,12 +195,6 @@ export default class RoomSublist2 extends React.Component { ); } - private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => { - const tileDiff = e.movementY < 0 ? -1 : +1; - this.props.layout.visibleTiles += tileDiff; - this.forceUpdate(); // because the layout doesn't trigger a re-render - }; - public render(): React.ReactElement { // TODO: Proper rendering // TODO: Error boundary @@ -216,6 +221,33 @@ export default class RoomSublist2 extends React.Component { handles = []; // no handles, we're at a minimum } const visibleTiles = tiles.slice(0, layout.visibleTiles); + console.log({n: tiles.length, c: layout.visibleTiles}); + + // If we're hiding rooms, show a 'show more' button to the user. This button + // replaces the last visible tile, so will always show 2+ rooms. We do this + // because if it said "show 1 more room" we had might as well show that room + // instead. We also replace the last item so we don't have to adjust our math + // on pixel heights, etc. It's much easier to pretend the button is a tile. + if (tiles.length > layout.visibleTiles) { + // we have a cutoff condition - add the button to show all + + // we +1 to account for the room we're about to hide with our 'show more' button + const numMissing = (tiles.length - visibleTiles.length) + 1; + + // TODO: Copy TBD + // TODO: CSS TBD + // TODO: Show N more instead of infinity more? + // TODO: Safely use the same height of a tile, not hardcoded hacks + visibleTiles.splice(visibleTiles.length - 1, 1, ( +
+ {_t("Show %(n)s more rooms", {n: numMissing})} +
+ )); + } content = ( Date: Wed, 3 Jun 2020 21:53:58 -0600 Subject: [PATCH 021/181] Remove debugging --- src/components/views/rooms/RoomSublist2.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index a9852dd3d3..f2e5329ab0 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -221,7 +221,6 @@ export default class RoomSublist2 extends React.Component { handles = []; // no handles, we're at a minimum } const visibleTiles = tiles.slice(0, layout.visibleTiles); - console.log({n: tiles.length, c: layout.visibleTiles}); // If we're hiding rooms, show a 'show more' button to the user. This button // replaces the last visible tile, so will always show 2+ rooms. We do this From 39dcc9f23ca67873dabbe0831d1935cd97bc114b Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 4 Jun 2020 01:01:54 +0000 Subject: [PATCH 022/181] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2258 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 9022acca08..e4f9faa56b 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2481,5 +2481,6 @@ "Upgrade your Riot": "升級您的 Riot", "A new version of Riot is available!": "已有新版的 Riot!", "New version available. Update now.": "有可用的新版本。立刻更新。", - "Emoji picker": "顏文字挑選器" + "Emoji picker": "顏文字挑選器", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "您的伺服器管理員已在私人聊天室與直接訊息中預設停用端到端加密。" } From db6d60566bfcfb03a22215e19f7205fca31f1935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 4 Jun 2020 06:46:15 +0000 Subject: [PATCH 023/181] Translated using Weblate (French) Currently translated at 100.0% (2258 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index f5cedf902a..45345e5c7d 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2482,5 +2482,6 @@ "Upgrade your Riot": "Mettre à niveau votre Riot", "A new version of Riot is available!": "Une nouvelle version de Riot est disponible !", "New version available. Update now.": "Nouvelle version disponible. Faire la mise à niveau maintenant.", - "Emoji picker": "Sélecteur d’émojis" + "Emoji picker": "Sélecteur d’émojis", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "L’administrateur de votre serveur a désactivé le chiffrement de bout en bout par défaut dans les salons privés et les messages directs." } From 9c93e487481d710da5127f7b6e2b608da68e2c28 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 4 Jun 2020 07:33:11 +0000 Subject: [PATCH 024/181] Translated using Weblate (Italian) Currently translated at 100.0% (2258 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 37c5159546..6f97e61560 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2477,5 +2477,6 @@ "Upgrade your Riot": "Aggiorna Riot", "A new version of Riot is available!": "È disponibile una nuova versione di Riot!", "New version available. Update now.": "Nuova versione disponibile. Aggiorna ora.", - "Emoji picker": "Selettore emoji" + "Emoji picker": "Selettore emoji", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "L'amministratore del server ha disattivato la crittografia end-to-end in modo predefinito nelle stanze private e nei messaggi diretti." } From 82eb80f0c63e5291c25302aac9f9f54d3d457586 Mon Sep 17 00:00:00 2001 From: XoseM Date: Thu, 4 Jun 2020 12:57:18 +0000 Subject: [PATCH 025/181] Translated using Weblate (Galician) Currently translated at 52.7% (1189 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index d37abfb890..6ae8289889 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -1253,5 +1253,11 @@ "Add theme": "Engadir decorado", "Theme": "Decorado", "Language and region": "Idioma e rexión", - "Your theme": "O teu decorado" + "Your theme": "O teu decorado", + "Use the improved room list (in development - refresh to apply changes)": "Usar a lista mellorada de salas (en desenvolvemento - actualiza para aplicar)", + "Support adding custom themes": "Permitir engadir decorados personalizados", + "Use IRC layout": "Usar disposición IRC", + "Font size": "Tamaño da letra", + "Custom font size": "Tamaño letra personalizado", + "Enable Emoji suggestions while typing": "Activar suxestión de Emoji ao escribir" } From 47df8f2ac0e8834597ba5b7862c41dfb3fd882a8 Mon Sep 17 00:00:00 2001 From: XoseM Date: Thu, 4 Jun 2020 12:59:42 +0000 Subject: [PATCH 026/181] Translated using Weblate (Galician) Currently translated at 53.5% (1208 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 6ae8289889..ba67879d14 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -1259,5 +1259,24 @@ "Use IRC layout": "Usar disposición IRC", "Font size": "Tamaño da letra", "Custom font size": "Tamaño letra personalizado", - "Enable Emoji suggestions while typing": "Activar suxestión de Emoji ao escribir" + "Enable Emoji suggestions while typing": "Activar suxestión de Emoji ao escribir", + "Show a placeholder for removed messages": "Resaltar o lugar das mensaxes eliminadas", + "Show avatar changes": "Mostrar cambios de avatar", + "Show display name changes": "Mostrar cambios do nome mostrado", + "Show read receipts sent by other users": "Mostrar resgardo de lectura enviados por outras usuarias", + "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Mostrar recordatorio para activar Recuperación Segura de Mensaxes en salas cifradas", + "Show avatars in user and room mentions": "Mostrar avatares nas mencións de salas e usuarias", + "Enable big emoji in chat": "Activar Emojis grandes na conversa", + "Send typing notifications": "Enviar notificación de escritura", + "Show typing notifications": "Mostrar notificacións de escritura", + "Allow Peer-to-Peer for 1:1 calls": "Permitir Par-a-Par para chamadas 1:1", + "Never send encrypted messages to unverified sessions from this session": "Non enviar nunca desde esta sesión mensaxes cifradas a sesións non verificadas", + "Never send encrypted messages to unverified sessions in this room from this session": "Non enviar mensaxes cifradas desde esta sesión a sesións non verificadas nesta sala", + "Prompt before sending invites to potentially invalid matrix IDs": "Avisar antes de enviar convites a IDs de Matrix potencialmente incorrectos", + "Show developer tools": "Mostrar ferramentas de desenvolvemento", + "Order rooms by name": "Ordenar salas por nome", + "Show rooms with unread notifications first": "Mostrar primeiro as salas que teñen notificacións sen ler", + "Show shortcuts to recently viewed rooms above the room list": "Mostrar atallos a salas vistas recentemente enriba da lista de salas", + "Show hidden events in timeline": "Mostrar na cronoloxía eventos ocultos", + "Low bandwidth mode": "Modo de ancho de banda reducido" } From 83df79aab92f7d4e1c69580f848cb59328cc01a9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 09:19:03 -0600 Subject: [PATCH 027/181] Try variable resizing --- src/components/views/rooms/RoomSublist2.tsx | 5 +++-- src/stores/room-list/ListLayout.ts | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index f2e5329ab0..9c034ce749 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -77,7 +77,8 @@ export default class RoomSublist2 extends React.Component { }; private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => { - const tileDiff = e.movementY < 0 ? -1 : +1; + const direction = e.movementY < 0 ? -1 : +1; + const tileDiff = this.props.layout.pixelsToTiles(Math.abs(e.movementY)) * direction; this.props.layout.visibleTiles += tileDiff; this.forceUpdate(); // because the layout doesn't trigger a re-render }; @@ -254,7 +255,7 @@ export default class RoomSublist2 extends React.Component { axis="y" minConstraints={[-1, minTilesPx]} maxConstraints={[-1, maxTilesPx]} - draggableOpts={{grid: [-1, layout.tileHeight]}} + draggableOpts={{grid: [-1, 1]}} resizeHandles={handles} onResize={this.onResize} className="mx_RoomSublist2_resizeBox" diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index af6abe3297..fd57a03ca1 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -57,6 +57,10 @@ export class ListLayout { return n * this.tileHeight; } + public pixelsToTiles(px: number): number { + return px / this.tileHeight; + } + private serialize(): ISerializedListLayout { return { numTiles: this.visibleTiles, From f6504d67ba0a09379c1291a2000dac1dae9697aa Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 4 Jun 2020 16:23:28 +0100 Subject: [PATCH 028/181] Use 10px instead --- res/css/_common.scss | 2 +- res/css/_font-sizes.scss | 112 +++++++++++++-------------- src/settings/watchers/FontWatcher.ts | 4 +- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index c562c3e95f..77a8ff9f4a 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,7 +19,7 @@ limitations under the License. @import "./_font-sizes.scss"; :root { - font-size: 20px; + font-size: 10px; } html { diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index c4a223d66c..5b876ab11d 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -14,59 +14,59 @@ See the License for the specific language governing permissions and limitations under the License. */ -$font-1px: 0.05rem; -$font-1-5px: 0.075rem; -$font-2px: 0.1rem; -$font-3px: 0.15rem; -$font-4px: 0.2rem; -$font-5px: 0.25rem; -$font-6px: 0.3rem; -$font-7px: 0.35rem; -$font-8px: 0.4rem; -$font-9px: 0.45rem; -$font-10px: 0.5rem; -$font-10-4px: 0.52rem; -$font-11px: 0.55rem; -$font-12px: 0.6rem; -$font-13px: 0.65rem; -$font-14px: 0.7rem; -$font-15px: 0.75rem; -$font-16px: 0.8rem; -$font-17px: 0.85rem; -$font-18px: 0.9rem; -$font-19px: 0.95rem; -$font-20px: 1.0rem; -$font-21px: 1.05rem; -$font-22px: 1.1rem; -$font-23px: 1.15rem; -$font-24px: 1.2rem; -$font-25px: 1.25rem; -$font-26px: 1.3rem; -$font-27px: 1.35rem; -$font-28px: 1.4rem; -$font-29px: 1.45rem; -$font-30px: 1.5rem; -$font-31px: 1.55rem; -$font-32px: 1.6rem; -$font-33px: 1.65rem; -$font-34px: 1.7rem; -$font-35px: 1.75rem; -$font-36px: 1.8rem; -$font-37px: 1.85rem; -$font-38px: 1.9rem; -$font-39px: 1.95rem; -$font-40px: 2.0rem; -$font-41px: 2.05rem; -$font-42px: 2.1rem; -$font-43px: 2.15rem; -$font-44px: 2.2rem; -$font-45px: 2.25rem; -$font-46px: 2.3rem; -$font-47px: 2.35rem; -$font-48px: 2.4rem; -$font-49px: 2.45rem; -$font-50px: 2.5rem; -$font-51px: 2.55rem; -$font-52px: 2.6rem; -$font-88px: 4.4rem; -$font-400px: 20rem; +$font-1px: 0.1rem; +$font-1-5px: 0.15rem; +$font-2px: 0.2rem; +$font-3px: 0.3rem; +$font-4px: 0.4rem; +$font-5px: 0.5rem; +$font-6px: 0.6rem; +$font-7px: 0.7rem; +$font-8px: 0.8rem; +$font-9px: 0.9rem; +$font-10px: 1.0rem; +$font-10-4px: 1.04rem; +$font-11px: 1.1rem; +$font-12px: 1.2rem; +$font-13px: 1.3rem; +$font-14px: 1.4rem; +$font-15px: 1.5rem; +$font-16px: 1.6rem; +$font-17px: 1.7rem; +$font-18px: 1.8rem; +$font-19px: 1.9rem; +$font-20px: 2.0rem; +$font-21px: 2.1rem; +$font-22px: 2.2rem; +$font-23px: 2.3rem; +$font-24px: 2.4rem; +$font-25px: 2.5rem; +$font-26px: 2.6rem; +$font-27px: 2.7rem; +$font-28px: 2.8rem; +$font-29px: 2.9rem; +$font-30px: 3.0rem; +$font-31px: 3.1rem; +$font-32px: 3.2rem; +$font-33px: 3.3rem; +$font-34px: 3.4rem; +$font-35px: 3.5rem; +$font-36px: 3.6rem; +$font-37px: 3.7rem; +$font-38px: 3.8rem; +$font-39px: 3.9rem; +$font-40px: 4.0rem; +$font-41px: 4.1rem; +$font-42px: 4.2rem; +$font-43px: 4.3rem; +$font-44px: 4.4rem; +$font-45px: 4.5rem; +$font-46px: 4.6rem; +$font-47px: 4.7rem; +$font-48px: 4.8rem; +$font-49px: 4.9rem; +$font-50px: 5.0rem; +$font-51px: 5.1rem; +$font-52px: 5.2rem; +$font-88px: 8.8rem; +$font-400px: 40rem; diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index 08a809f869..5d7c601fa1 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -45,8 +45,8 @@ export class FontWatcher implements IWatcher { }; private setRootFontSize = (size) => { - // Externally we tell the user the font is size 15. Internally we use 20. - const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE) + 5; + // Externally we tell the user the font is size 15. Internally we use 10. + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE) - 5; if (fontSize !== size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); From a3fdd643d79ec3b2eb725380991b8d08d9db1c0c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 09:57:16 -0600 Subject: [PATCH 029/181] Add hacky math support --- src/components/views/rooms/RoomSublist2.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 9c034ce749..cc56f92769 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -221,14 +221,20 @@ export default class RoomSublist2 extends React.Component { if (layout.visibleTiles >= tiles.length && tiles.length <= layout.minVisibleTiles) { handles = []; // no handles, we're at a minimum } - const visibleTiles = tiles.slice(0, layout.visibleTiles); + + let nVisible = Math.floor(layout.visibleTiles); + if (localStorage.getItem("mx_rl_mathfn")) { + nVisible = Math[localStorage.getItem("mx_rl_mathfn")](layout.visibleTiles); + } + console.log({nVisible}) + const visibleTiles = tiles.slice(0, nVisible); // If we're hiding rooms, show a 'show more' button to the user. This button // replaces the last visible tile, so will always show 2+ rooms. We do this // because if it said "show 1 more room" we had might as well show that room // instead. We also replace the last item so we don't have to adjust our math // on pixel heights, etc. It's much easier to pretend the button is a tile. - if (tiles.length > layout.visibleTiles) { + if (tiles.length > nVisible) { // we have a cutoff condition - add the button to show all // we +1 to account for the room we're about to hide with our 'show more' button From 2fe56276f2bdf2f24fbe64149993710122e83549 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 10:07:41 -0600 Subject: [PATCH 030/181] css for vis --- res/css/views/rooms/_RoomSublist2.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index bb760e7e6e..abc3133fc1 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -15,3 +15,8 @@ limitations under the License. */ @import "../../../../node_modules/react-resizable/css/styles.css"; + +.mx_RoomList2 .mx_RoomSubList_labelContainer { + z-index: 12; + background-color: purple; +} From 2ec47ecc743593ef816b1b7e7f7e903428b065f3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 4 Jun 2020 17:50:56 +0100 Subject: [PATCH 031/181] Use different setting flag print expected values --- .../tabs/user/AppearanceUserSettingsTab.tsx | 12 +++++++++--- src/settings/Settings.js | 4 ++-- src/settings/watchers/FontWatcher.ts | 14 ++++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index bcd87b290a..9b77493c4f 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -62,7 +62,7 @@ export default class AppearanceUserSettingsTab extends React.Component { this.setState({fontSize: size.toString()}); - SettingsStore.setValue("fontSize", null, SettingLevel.DEVICE, size); + SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, size - FontWatcher.SIZE_DIFF); }; private onValidateFontSize = async ({value}: Pick): Promise => { @@ -151,7 +151,13 @@ export default class AppearanceUserSettingsTab extends React.Component { - // Externally we tell the user the font is size 15. Internally we use 10. - const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE) - 5; + console.log({size}) + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); if (fontSize !== size) { - SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); + SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, fontSize); } (document.querySelector(":root")).style.fontSize = toPx(fontSize); }; From bba6767608cea9444d927b02195794f48111c3f5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 4 Jun 2020 18:00:22 +0100 Subject: [PATCH 032/181] Update displayed min and max --- .../views/settings/tabs/user/AppearanceUserSettingsTab.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 9b77493c4f..2b58e0e28e 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -137,8 +137,8 @@ export default class AppearanceUserSettingsTab extends React.Component): Promise => { const parsedSize = parseFloat(value); - const min = FontWatcher.MIN_SIZE; - const max = FontWatcher.MAX_SIZE; + const min = FontWatcher.MIN_SIZE + FontWatcher.SIZE_DIFF; + const max = FontWatcher.MAX_SIZE + FontWatcher.SIZE_DIFF; if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; From f3011f00f78a3de7a34868fc506b462472154b7c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Thu, 4 Jun 2020 19:43:35 +0100 Subject: [PATCH 033/181] Remove debug --- src/settings/watchers/FontWatcher.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index 1eb0af9f3e..05c707a57b 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -47,7 +47,6 @@ export class FontWatcher implements IWatcher { }; private setRootFontSize = (size) => { - console.log({size}) const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); if (fontSize !== size) { From 434f6ea4d470ac6d1de2cd16c7663ebc8030d97c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 16:36:14 -0600 Subject: [PATCH 034/181] Remove legacy sublist sizing --- src/components/views/rooms/RoomList2.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index b6fe37589c..15aa880109 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -127,8 +127,6 @@ const TAG_AESTHETICS: { }; export default class RoomList2 extends React.Component { - private sublistSizes: { [tagId: string]: number } = {}; - private sublistCollapseStates: { [tagId: string]: boolean } = {}; private searchFilter: NameFilterCondition = new NameFilterCondition(); constructor(props: IProps) { @@ -138,7 +136,6 @@ export default class RoomList2 extends React.Component { sublists: {}, layouts: new Map(), }; - this.loadSublistSizes(); } public componentDidUpdate(prevProps: Readonly): void { @@ -170,14 +167,6 @@ export default class RoomList2 extends React.Component { }); } - private loadSublistSizes() { - const sizesJson = window.localStorage.getItem("mx_roomlist_sizes"); - if (sizesJson) this.sublistSizes = JSON.parse(sizesJson); - - const collapsedJson = window.localStorage.getItem("mx_roomlist_collapsed"); - if (collapsedJson) this.sublistCollapseStates = JSON.parse(collapsedJson); - } - private renderSublists(): React.ReactElement[] { const components: React.ReactElement[] = []; From 4c1bc50649afcf31f403b7b887ecbdc95ba31772 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 16:34:04 -0600 Subject: [PATCH 035/181] Initial styling for new room list This is a work in progress, but covers the coarse areas. This uses all-new classes to better describe what everything is, and to reduce the number of selectors we keep track of. This is primarily layout for the list and not actually the final structure. For example, some buttons are missing and other areas are not styled correctly - the idea in this commit was to get things roughly in the right place and work on it. --- res/css/_components.scss | 3 + res/css/structures/_LeftPanel.scss | 8 -- res/css/structures/_LeftPanel2.scss | 91 +++++++++++++++++++++ res/css/structures/_MatrixChat.scss | 2 +- res/css/views/rooms/_RoomList2.scss | 23 ++++++ res/css/views/rooms/_RoomSublist2.scss | 2 +- res/css/views/rooms/_RoomTile2.scss | 18 ++++ src/components/structures/LeftPanel2.tsx | 63 +++++++++----- src/components/views/rooms/RoomList2.tsx | 2 +- src/components/views/rooms/RoomSublist2.tsx | 21 +++-- src/components/views/rooms/RoomTile2.tsx | 32 ++++---- 11 files changed, 205 insertions(+), 60 deletions(-) create mode 100644 res/css/structures/_LeftPanel2.scss create mode 100644 res/css/views/rooms/_RoomList2.scss create mode 100644 res/css/views/rooms/_RoomTile2.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index b047519d99..62bec5ad62 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -12,6 +12,7 @@ @import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; +@import "./structures/_LeftPanel2.scss"; @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; @@ -177,10 +178,12 @@ @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; +@import "./views/rooms/_RoomList2.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; @import "./views/rooms/_RoomSublist2.scss"; @import "./views/rooms/_RoomTile.scss"; +@import "./views/rooms/_RoomTile2.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SendMessageComposer.scss"; diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 899824bc57..35d9f0e7da 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -23,14 +23,6 @@ limitations under the License. flex: 0 0 auto; } -// TODO: Remove temporary indicator of new room list implementation. -// This border is meant to visually distinguish between the two components when the -// user has turned on the new room list implementation, at least until the designs -// themselves give it away. -.mx_LeftPanel2 .mx_LeftPanel { - border-left: 5px #e26dff solid; -} - .mx_LeftPanel_container.collapsed { min-width: unset; /* Collapsed LeftPanel 50px */ diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss new file mode 100644 index 0000000000..52ee4f16ac --- /dev/null +++ b/res/css/structures/_LeftPanel2.scss @@ -0,0 +1,91 @@ +/* +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. +*/ + +$tagPanelWidth: 70px; +$roomListMinimizedWidth: 50px; + +.mx_LeftPanel2 { + background-color: $header-panel-bg-color; + min-width: 260px; + max-width: 50%; + + // Create a row-based flexbox for the TagPanel and the room list + display: flex; + + .mx_LeftPanel2_tagPanelContainer { + flex-grow: 0; + flex-shrink: 0; + flex-basis: $tagPanelWidth; + height: 100%; + + // Create another flexbox so the TagPanel fills the container + display: flex; + + // TagPanel handles its own CSS + } + + // Note: The 'room list' in this context is actually everything that isn't the tag + // panel, such as the menu options, breadcrumbs, filtering, etc + .mx_LeftPanel2_roomListContainer { + width: calc(100% - $tagPanelWidth); + + // Create another flexbox (this time a column) for the room list components + display: flex; + flex-direction: column; + + .mx_LeftPanel2_userHeader { + padding: 14px 12px 20px; // 14px top, 12px sides, 20px bottom + + // Create another flexbox column for the rows to stack within + display: flex; + flex-direction: column; + + // There's 2 rows when breadcrumbs are present: the top bit and the breadcrumbs + .mx_LeftPanel2_headerRow { + // Create yet another flexbox, this time within the row, to ensure items stay + // aligned correctly. This is also a row-based flexbox. + display: flex; + align-items: center; + } + + .mx_LeftPanel2_userAvatarContainer { + position: relative; // to make default avatars work + margin-right: 8px; + } + + .mx_LeftPanel2_userName { + font-weight: 600; + font-size: $font-15px; + line-height: $font-20px; + } + + .mx_LeftPanel2_breadcrumbsContainer { + // TODO: Improve CSS for breadcrumbs (currently shoved into the view rather than placed) + width: 100%; + overflow: hidden; + } + } + + .mx_LeftPanel2_filterContainer { + // TODO: Improve CSS for filtering and its input + } + + .mx_LeftPanel2_actualRoomListContainer { + flex-grow: 1; // fill the available space + overflow-y: auto; + } + } +} diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 05c703ab6d..08ed9e5559 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -66,7 +66,7 @@ limitations under the License. } /* not the left panel, and not the resize handle, so the roomview/groupview/... */ -.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_ResizeHandle) { +.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_LeftPanel2):not(.mx_ResizeHandle) { background-color: $primary-bg-color; flex: 1 1 0; diff --git a/res/css/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss new file mode 100644 index 0000000000..add7214468 --- /dev/null +++ b/res/css/views/rooms/_RoomList2.scss @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomList2 { + // Create a column-based flexbox for the sublists. That's pretty much all we have to + // worry about in this stylesheet. + display: flex; + flex-direction: column; + flex-wrap: wrap; +} diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index abc3133fc1..55b16843b3 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -16,7 +16,7 @@ limitations under the License. @import "../../../../node_modules/react-resizable/css/styles.css"; -.mx_RoomList2 .mx_RoomSubList_labelContainer { +.mx_RoomList2 .mx_RoomSubList2_labelContainer { z-index: 12; background-color: purple; } diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss new file mode 100644 index 0000000000..6577f1ce25 --- /dev/null +++ b/res/css/views/rooms/_RoomTile2.scss @@ -0,0 +1,18 @@ +/* +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_RoomTile2 { +} diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index c9a4948539..bf0e72beeb 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -24,6 +24,9 @@ import SearchBox from "./SearchBox"; import RoomList2 from "../views/rooms/RoomList2"; import TopLeftMenuButton from "./TopLeftMenuButton"; import { Action } from "../../dispatcher/actions"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; +import BaseAvatar from '../views/avatars/BaseAvatar'; +import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; /******************************************************************* * CAUTION * @@ -82,24 +85,44 @@ export default class LeftPanel2 extends React.Component { } } + private renderHeader(): React.ReactNode { + // TODO: Use real profile info + // TODO: Presence + // TODO: Breadcrumbs toggle + // TODO: Menu button + const avatarSize = 32; + return ( +
+
+ + + + Irene +
+
+ +
+
+ ); + } + public render(): React.ReactNode { const tagPanel = ( -
+
); - const exploreButton = ( -
- dis.dispatch({action: 'view_room_directory'})}> - {_t("Explore")} - -
- ); - const searchBox = ( { // TODO: Conference handling / calls const containerClasses = classNames({ - "mx_LeftPanel_container": true, - "mx_fadable": true, - "collapsed": false, // TODO: Collapsed support - "mx_LeftPanel_container_hasTagPanel": true, // TODO: TagPanel support - "mx_fadable_faded": false, - "mx_LeftPanel2": true, // TODO: Remove flag when RoomList2 ships (used as an indicator) + "mx_LeftPanel2": true, }); return (
{tagPanel} -
); diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 15aa880109..af366e9685 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -216,7 +216,7 @@ export default class RoomList2 extends React.Component { onFocus={this.props.onFocus} onBlur={this.props.onBlur} onKeyDown={onKeyDownHandler} - className="mx_RoomList mx_RoomList2" + className="mx_RoomList2" role="tree" aria-label={_t("Rooms")} // Firefox sometimes makes this element focusable due to diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index cc56f92769..e7682a9bb4 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -113,9 +113,9 @@ export default class RoomSublist2 extends React.Component { let chevron = null; if (this.hasTiles()) { const chevronClasses = classNames({ - 'mx_RoomSubList_chevron': true, - 'mx_RoomSubList_chevronRight': false, // isCollapsed - 'mx_RoomSubList_chevronDown': true, // !isCollapsed + 'mx_RoomSublist2_chevron': true, + 'mx_RoomSublist2_chevronRight': false, // isCollapsed + 'mx_RoomSublist2_chevronDown': true, // !isCollapsed }); chevron = (
); } @@ -130,8 +130,8 @@ export default class RoomSublist2 extends React.Component { let badge; if (true) { // !isCollapsed const badgeClasses = classNames({ - 'mx_RoomSubList_badge': true, - 'mx_RoomSubList_badgeHighlight': notifHighlight, + 'mx_RoomSublist2_badge': true, + 'mx_RoomSublist2_badgeHighlight': notifHighlight, }); // Wrap the contents in a div and apply styles to the child div so that the browser default outline works if (notifCount > 0) { @@ -168,7 +168,7 @@ export default class RoomSublist2 extends React.Component { ); @@ -176,11 +176,11 @@ export default class RoomSublist2 extends React.Component { // TODO: a11y (see old component) return ( -
+
@@ -204,9 +204,8 @@ export default class RoomSublist2 extends React.Component { const classes = classNames({ // TODO: Proper collapse support - 'mx_RoomSubList': true, - 'mx_RoomSubList_hidden': false, // len && isCollapsed - 'mx_RoomSubList_nonEmpty': this.hasTiles(), // len && !isCollapsed + 'mx_RoomSublist2': true, + 'mx_RoomSublist2_collapsed': false, // len && isCollapsed }); let content = null; diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index c95cd108dc..40c7bcbc12 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -195,28 +195,28 @@ export default class RoomTile2 extends React.Component { const hasBadge = this.state.notificationState.color > NotificationColor.Bold; const isUnread = this.state.notificationState.color > NotificationColor.None; const classes = classNames({ - 'mx_RoomTile': true, + 'mx_RoomTile2': true, // 'mx_RoomTile_selected': this.state.selected, - 'mx_RoomTile_unread': isUnread, - 'mx_RoomTile_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey, - 'mx_RoomTile_highlight': this.state.notificationState.color >= NotificationColor.Red, - 'mx_RoomTile_invited': this.roomIsInvite, + 'mx_RoomTile2_unread': isUnread, + 'mx_RoomTile2_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey, + 'mx_RoomTile2_highlight': this.state.notificationState.color >= NotificationColor.Red, + 'mx_RoomTile2_invited': this.roomIsInvite, // 'mx_RoomTile_menuDisplayed': isMenuDisplayed, - 'mx_RoomTile_noBadges': !hasBadge, + 'mx_RoomTile2_noBadges': !hasBadge, // 'mx_RoomTile_transparent': this.props.transparent, // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed, }); const avatarClasses = classNames({ - 'mx_RoomTile_avatar': true, + 'mx_RoomTile2_avatar': true, }); let badge; if (hasBadge) { const badgeClasses = classNames({ - 'mx_RoomTile_badge': true, - 'mx_RoomTile_badgeButton': false, // this.state.badgeHover || isMenuDisplayed + 'mx_RoomTile2_badge': true, + 'mx_RoomTile2_badgeButton': false, // this.state.badgeHover || isMenuDisplayed }); badge =
{this.state.notificationState.symbol}
; } @@ -227,16 +227,16 @@ export default class RoomTile2 extends React.Component { name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon const nameClasses = classNames({ - 'mx_RoomTile_name': true, - 'mx_RoomTile_invite': this.roomIsInvite, - 'mx_RoomTile_badgeShown': hasBadge, + 'mx_RoomTile2_name': true, + 'mx_RoomTile2_invite': this.roomIsInvite, + 'mx_RoomTile2_badgeShown': hasBadge, }); // TODO: Support collapsed state properly let tooltip = null; if (false) { // isCollapsed if (this.state.hover) { - tooltip = + tooltip = } } @@ -255,12 +255,12 @@ export default class RoomTile2 extends React.Component { role="treeitem" >
-
+
-
-
+
+
{name}
From 0c15b2bdb6c332713b8e95cb907c6b50adeaeeda Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 21:21:04 -0600 Subject: [PATCH 036/181] Simple structuring of the room list itself This covers the larger parts of the design, but doesn't deal with the nuances of hover states, badge sizing, etc. --- res/css/views/rooms/_RoomSublist2.scss | 26 +++- res/css/views/rooms/_RoomTile2.scss | 76 +++++++++++ res/themes/light/css/_light.scss | 5 + src/components/views/rooms/RoomList2.tsx | 3 +- src/components/views/rooms/RoomSublist2.tsx | 135 ++++++++++---------- src/components/views/rooms/RoomTile2.tsx | 65 ++++------ src/i18n/strings/en_EN.json | 9 +- src/stores/room-list/ListLayout.ts | 2 +- 8 files changed, 205 insertions(+), 116 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 55b16843b3..4d8c3fffff 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -16,7 +16,27 @@ limitations under the License. @import "../../../../node_modules/react-resizable/css/styles.css"; -.mx_RoomList2 .mx_RoomSubList2_labelContainer { - z-index: 12; - background-color: purple; +.mx_RoomSublist2 { + // The sublist is a column of rows, essentially + display: flex; + flex-direction: column; + + margin-left: 8px; + margin-top: 12px; + margin-bottom: 12px; + + .mx_RoomSublist2_headerContainer { + text-transform: uppercase; + opacity: 0.5; + line-height: $font-16px; + font-size: $font-12px; + padding-bottom: 8px; + } + + .mx_RoomSublist2_resizeBox { + // Create another flexbox column for the tiles + display: flex; + flex-direction: column; + overflow: hidden; + } } diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index 6577f1ce25..bb27942f81 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -14,5 +14,81 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Note: the room tile expects to be in a flexbox column container .mx_RoomTile2 { + width: 100%; + padding-bottom: 12px; + + // The tile is also a flexbox row itself + display: flex; + flex-wrap: wrap; + + .mx_RoomTile2_avatarContainer { + margin-right: 8px; + } + + .mx_RoomTile2_nameContainer { + // Create a new column layout flexbox for the name parts + display: flex; + flex-direction: column; + justify-content: center; + + .mx_RoomTile2_name, + .mx_RoomTile2_messagePreview { + margin: 0 2px; + } + + // TODO: Ellipsis on the name and preview + + .mx_RoomTile2_name { + font-weight: 600; + font-size: $font-14px; + line-height: $font-19px; + } + + .mx_RoomTile2_messagePreview { + font-size: $font-13px; + line-height: $font-18px; + color: $roomtile2-preview-color; + } + } + + .mx_RoomTile2_badgeContainer { + flex-grow: 1; + + // Create another flexbox row because it's super easy to position the badge at + // the end this way. + display: flex; + align-items: center; + justify-content: flex-end; + + .mx_RoomTile2_badge { + background-color: $roomtile2-badge-color; + + &:not(.mx_RoomTile2_badgeEmpty) { + border-radius: 16px; + font-size: $font-10px; + line-height: $font-14px; + text-align: center; + font-weight: bold; + margin-right: 14px; + color: #fff; // TODO: Variable + + // TODO: Confirm padding on counted badges + padding: 2px 5px; + } + + &.mx_RoomTile2_badgeEmpty { + width: 6px; + height: 6px; + border-radius: 6px; + margin-right: 18px; + } + + &.mx_RoomTile2_badgeHighlight { + // TODO: Use a more specific variable + background-color: $warning-color; + } + } + } } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 78fe2a74c5..683c02528d 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -172,6 +172,11 @@ $header-divider-color: #91A1C0; // ******************** +// TODO: Update variables for new room list +// TODO: Dark theme +$roomtile2-preview-color: #9e9e9e; +$roomtile2-badge-color: #61708b; + $roomtile-name-color: #61708b; $roomtile-badge-fg-color: $accent-fg-color; $roomtile-selected-color: #212121; diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index af366e9685..ce1956f68d 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -96,7 +96,7 @@ const TAG_AESTHETICS: { defaultHidden: false, }, [DefaultTagID.DM]: { - sectionLabel: _td("Direct Messages"), + sectionLabel: _td("People"), isInvite: false, defaultHidden: false, addRoomLabel: _td("Start chat"), @@ -200,6 +200,7 @@ export default class RoomList2 extends React.Component { addRoomLabel={aesthetics.addRoomLabel} isInvite={aesthetics.isInvite} layout={this.state.layouts.get(orderedTagId)} + showMessagePreviews={orderedTagId === DefaultTagID.DM} /> ); } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index e7682a9bb4..84bcb1ea4d 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -20,15 +20,13 @@ import * as React from "react"; import { createRef } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import classNames from 'classnames'; -import * as RoomNotifs from '../../../RoomNotifs'; import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../../views/elements/AccessibleButton"; -import AccessibleTooltipButton from "../../views/elements/AccessibleTooltipButton"; -import * as FormattingUtils from '../../../utils/FormattingUtils'; import RoomTile2 from "./RoomTile2"; import { ResizableBox, ResizeCallbackData } from "react-resizable"; import { ListLayout } from "../../../stores/room-list/ListLayout"; +import { DefaultTagID, TagID } from "../../../stores/room-list/models"; /******************************************************************* * CAUTION * @@ -43,6 +41,7 @@ interface IProps { rooms?: Room[]; startAsHidden: boolean; label: string; + showMessagePreviews: boolean; onAddRoom?: () => void; addRoomLabel: string; isInvite: boolean; @@ -93,7 +92,13 @@ export default class RoomSublist2 extends React.Component { if (this.props.rooms) { for (const room of this.props.rooms) { - tiles.push(); + tiles.push( + + ); } } @@ -101,25 +106,16 @@ export default class RoomSublist2 extends React.Component { } private renderHeader(): React.ReactElement { - const notifications = !this.props.isInvite - ? RoomNotifs.aggregateNotificationCount(this.props.rooms) - : {count: 0, highlight: true}; - const notifCount = notifications.count; - const notifHighlight = notifications.highlight; + // TODO: Handle badge count + // const notifications = !this.props.isInvite + // ? RoomNotifs.aggregateNotificationCount(this.props.rooms) + // : {count: 0, highlight: true}; + // const notifCount = notifications.count; + // const notifHighlight = notifications.highlight; // TODO: Title on collapsed // TODO: Incoming call box - let chevron = null; - if (this.hasTiles()) { - const chevronClasses = classNames({ - 'mx_RoomSublist2_chevron': true, - 'mx_RoomSublist2_chevronRight': false, // isCollapsed - 'mx_RoomSublist2_chevronDown': true, // !isCollapsed - }); - chevron = (
); - } - return ( {({onFocus, isActive, ref}) => { @@ -127,52 +123,55 @@ export default class RoomSublist2 extends React.Component { const tabIndex = isActive ? 0 : -1; // TODO: Collapsed state - let badge; - if (true) { // !isCollapsed - const badgeClasses = classNames({ - 'mx_RoomSublist2_badge': true, - 'mx_RoomSublist2_badgeHighlight': notifHighlight, - }); - // Wrap the contents in a div and apply styles to the child div so that the browser default outline works - if (notifCount > 0) { - badge = ( - -
- {FormattingUtils.formatCount(notifCount)} -
-
- ); - } else if (this.props.isInvite && this.hasTiles()) { - // Render the `!` badge for invites - badge = ( - -
- {FormattingUtils.formatCount(this.numTiles)} -
-
- ); - } - } + // TODO: Handle badge count + // let badge; + // if (true) { // !isCollapsed + // const showCount = localStorage.getItem("mx_rls_count") || notifHighlight; + // const badgeClasses = classNames({ + // 'mx_RoomSublist2_badge': true, + // 'mx_RoomSublist2_badgeHighlight': notifHighlight, + // 'mx_RoomSublist2_badgeEmpty': !showCount, + // }); + // // Wrap the contents in a div and apply styles to the child div so that the browser default outline works + // if (notifCount > 0) { + // const count =
{FormattingUtils.formatCount(notifCount)}
; + // badge = ( + // + // {showCount ? count : null} + // + // ); + // } else if (this.props.isInvite && this.hasTiles()) { + // // Render the `!` badge for invites + // badge = ( + // + //
+ // {FormattingUtils.formatCount(this.numTiles)} + //
+ //
+ // ); + // } + // } - let addRoomButton = null; - if (!!this.props.onAddRoom) { - addRoomButton = ( - - ); - } + // TODO: Aux button + // let addRoomButton = null; + // if (!!this.props.onAddRoom) { + // addRoomButton = ( + // + // ); + // } // TODO: a11y (see old component) return ( @@ -184,11 +183,8 @@ export default class RoomSublist2 extends React.Component { role="treeitem" aria-level="1" > - {chevron} {this.props.label} - {badge} - {addRoomButton}
); }} @@ -243,13 +239,14 @@ export default class RoomSublist2 extends React.Component { // TODO: CSS TBD // TODO: Show N more instead of infinity more? // TODO: Safely use the same height of a tile, not hardcoded hacks + const moreTileHeightPx = `${layout.tileHeight}px`; visibleTiles.splice(visibleTiles.length - 1, 1, (
- {_t("Show %(n)s more rooms", {n: numMissing})} + {_t("Show %(n)s more", {n: numMissing})}
)); } diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 40c7bcbc12..8a51327ae2 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -51,6 +51,7 @@ enum NotificationColor { interface IProps { room: Room; + showMessagePreview: boolean; // TODO: Allow falsifying counts (for invites and stuff) // TODO: Transparency? Was this ever used? @@ -192,33 +193,22 @@ export default class RoomTile2 extends React.Component { // TODO: a11y proper // TODO: Render more than bare minimum - const hasBadge = this.state.notificationState.color > NotificationColor.Bold; - const isUnread = this.state.notificationState.color > NotificationColor.None; const classes = classNames({ 'mx_RoomTile2': true, - // 'mx_RoomTile_selected': this.state.selected, - 'mx_RoomTile2_unread': isUnread, - 'mx_RoomTile2_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey, - 'mx_RoomTile2_highlight': this.state.notificationState.color >= NotificationColor.Red, - 'mx_RoomTile2_invited': this.roomIsInvite, - // 'mx_RoomTile_menuDisplayed': isMenuDisplayed, - 'mx_RoomTile2_noBadges': !hasBadge, - // 'mx_RoomTile_transparent': this.props.transparent, - // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed, }); - const avatarClasses = classNames({ - 'mx_RoomTile2_avatar': true, - }); - - let badge; + const hasBadge = this.state.notificationState.color > NotificationColor.Bold; if (hasBadge) { + const hasNotif = this.state.notificationState.color >= NotificationColor.Red; + const isEmptyBadge = !localStorage.getItem("mx_rl_rt_badgeCount"); const badgeClasses = classNames({ 'mx_RoomTile2_badge': true, - 'mx_RoomTile2_badgeButton': false, // this.state.badgeHover || isMenuDisplayed + 'mx_RoomTile2_badgeHighlight': hasNotif, + 'mx_RoomTile2_badgeEmpty': isEmptyBadge, }); - badge =
{this.state.notificationState.symbol}
; + const symbol = this.state.notificationState.symbol; + badge =
{isEmptyBadge ? null : symbol}
; } // TODO: the original RoomTile uses state for the room name. Do we need to? @@ -226,20 +216,21 @@ export default class RoomTile2 extends React.Component { if (typeof name !== 'string') name = ''; name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon - const nameClasses = classNames({ - 'mx_RoomTile2_name': true, - 'mx_RoomTile2_invite': this.roomIsInvite, - 'mx_RoomTile2_badgeShown': hasBadge, - }); - // TODO: Support collapsed state properly - let tooltip = null; - if (false) { // isCollapsed - if (this.state.hover) { - tooltip = - } + // TODO: Tooltip? + + let messagePreview = null; + if (this.props.showMessagePreview) { + // TODO: Actually get the real message preview from state + messagePreview =
I just ate a pie.
; } + const nameClasses = classNames({ + "mx_RoomTile2_name": true, + "mx_RoomTile2_nameWithPreview": !!messagePreview, + }); + + const avatarSize = 32; return ( @@ -254,20 +245,18 @@ export default class RoomTile2 extends React.Component { onClick={this.onTileClick} role="treeitem" > -
-
- -
+
+
-
-
- {name} -
+
+ {name}
+ {messagePreview} +
+
{badge}
- {tooltip} } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 884192b22a..cf6dc2431a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1090,6 +1090,7 @@ "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", + "People": "People", "This room": "This room", "Joining room …": "Joining room …", "Loading …": "Loading …", @@ -1133,10 +1134,7 @@ "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", "Not now": "Not now", "Don't ask me again": "Don't ask me again", - "Jump to first unread room.": "Jump to first unread room.", - "Jump to first invite.": "Jump to first invite.", - "Add room": "Add room", - "Show %(n)s more rooms": "Show %(n)s more rooms", + "Show %(n)s more": "Show %(n)s more", "Options": "Options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", "%(count)s unread messages including mentions.|one": "1 unread mention.", @@ -2017,6 +2015,9 @@ "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", "Active call": "Active call", "There's no one else here! Would you like to invite others or stop warning about the empty room?": "There's no one else here! Would you like to invite others or stop warning about the empty room?", + "Jump to first unread room.": "Jump to first unread room.", + "Jump to first invite.": "Jump to first invite.", + "Add room": "Add room", "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", "Search failed": "Search failed", diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index fd57a03ca1..ee51230a61 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -const TILE_HEIGHT_PX = 34; +const TILE_HEIGHT_PX = 44; interface ISerializedListLayout { numTiles: number; From f347019cf86bf8243fb643c949fa804ce6c6a0c4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 21:37:10 -0600 Subject: [PATCH 037/181] Remove obviously questionable color choices --- res/css/views/rooms/_RoomSublist2.scss | 1 - src/components/views/rooms/RoomSublist2.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index abc3133fc1..1dfaf3fb1a 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -18,5 +18,4 @@ limitations under the License. .mx_RoomList2 .mx_RoomSubList_labelContainer { z-index: 12; - background-color: purple; } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index cc56f92769..3aa1ea3566 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -247,7 +247,7 @@ export default class RoomSublist2 extends React.Component { visibleTiles.splice(visibleTiles.length - 1, 1, (
{_t("Show %(n)s more rooms", {n: numMissing})} From 0694637b06af0288b268deec4e271b4026395640 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 21:38:06 -0600 Subject: [PATCH 038/181] Remove debug --- src/components/views/rooms/RoomSublist2.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 3aa1ea3566..a64f4bf6ba 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -226,7 +226,6 @@ export default class RoomSublist2 extends React.Component { if (localStorage.getItem("mx_rl_mathfn")) { nVisible = Math[localStorage.getItem("mx_rl_mathfn")](layout.visibleTiles); } - console.log({nVisible}) const visibleTiles = tiles.slice(0, nVisible); // If we're hiding rooms, show a 'show more' button to the user. This button From e90e70bd77f9de1ad408a8f714c60b2ccec62d4c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 21:43:33 -0600 Subject: [PATCH 039/181] Misc cleanup --- res/css/views/rooms/_RoomList.scss | 4 ---- res/css/views/rooms/_RoomSublist2.scss | 2 +- src/components/views/rooms/RoomSublist2.tsx | 7 ++----- src/i18n/strings/en_EN.json | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index 7abf86cb0e..c23c19699d 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -15,10 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RoomList2_resizer { - cursor: ns-resize; -} - .mx_RoomList.mx_RoomList2 { overflow-y: auto; } diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 1dfaf3fb1a..9ab1785566 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -17,5 +17,5 @@ limitations under the License. @import "../../../../node_modules/react-resizable/css/styles.css"; .mx_RoomList2 .mx_RoomSubList_labelContainer { - z-index: 12; + z-index: 12; } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index a64f4bf6ba..548166815f 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -239,17 +239,15 @@ export default class RoomSublist2 extends React.Component { // we +1 to account for the room we're about to hide with our 'show more' button const numMissing = (tiles.length - visibleTiles.length) + 1; - // TODO: Copy TBD // TODO: CSS TBD - // TODO: Show N more instead of infinity more? - // TODO: Safely use the same height of a tile, not hardcoded hacks + // TODO: Make this an actual tile visibleTiles.splice(visibleTiles.length - 1, 1, (
- {_t("Show %(n)s more rooms", {n: numMissing})} + {_t("Show %(n)s more", {n: numMissing})}
)); } @@ -260,7 +258,6 @@ export default class RoomSublist2 extends React.Component { axis="y" minConstraints={[-1, minTilesPx]} maxConstraints={[-1, maxTilesPx]} - draggableOpts={{grid: [-1, 1]}} resizeHandles={handles} onResize={this.onResize} className="mx_RoomSublist2_resizeBox" diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 884192b22a..0aa4c3779e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1136,7 +1136,7 @@ "Jump to first unread room.": "Jump to first unread room.", "Jump to first invite.": "Jump to first invite.", "Add room": "Add room", - "Show %(n)s more rooms": "Show %(n)s more rooms", + "Show %(n)s more": "Show %(n)s more", "Options": "Options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", "%(count)s unread messages including mentions.|one": "1 unread mention.", From 1f11298aa327d15067ea0b14d10ecd6d50c14d3e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 4 Jun 2020 21:45:32 -0600 Subject: [PATCH 040/181] Annotate hacky math --- src/components/views/rooms/RoomSublist2.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 548166815f..e20a36d6ce 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -222,6 +222,7 @@ export default class RoomSublist2 extends React.Component { handles = []; // no handles, we're at a minimum } + // TODO: Remove Math hacks let nVisible = Math.floor(layout.visibleTiles); if (localStorage.getItem("mx_rl_mathfn")) { nVisible = Math[localStorage.getItem("mx_rl_mathfn")](layout.visibleTiles); From 0a1080de5a49c8bf1b390a2eb004e4746207e5b9 Mon Sep 17 00:00:00 2001 From: MamasLT Date: Thu, 4 Jun 2020 21:20:26 +0000 Subject: [PATCH 041/181] Translated using Weblate (Lithuanian) Currently translated at 67.4% (1522 of 2258 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/lt/ --- src/i18n/strings/lt.json | 43 ++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json index d885c6fd7d..08f7c9f33d 100644 --- a/src/i18n/strings/lt.json +++ b/src/i18n/strings/lt.json @@ -1254,12 +1254,12 @@ "Report Content": "Pranešti", "Nice, strong password!": "Puiku, stiprus slaptažodis!", "Old cryptography data detected": "Aptikti seni kriptografijos duomenys", - "Verify this login": "Patvirtinti šį prisijungimą", + "Verify this login": "Patvirtinkite šį prisijungimą", "Registration has been disabled on this homeserver.": "Registracija šiame serveryje išjungta.", "You can now close this window or log in to your new account.": "Jūs galite uždaryti šį langą arba prisijungti į savo naują paskyrą.", - "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Patvirtinkite savo tapatybę verifikuodami šį prisijungimą viename iš kitų jūsų seansų, suteikdami jam prieigą prie šifruotų žinučių.", + "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Identifikuokite save patvirtindami šį prisijungimą viename iš kitų jūsų seansų ir suteikdami jam prieigą prie šifruotų žinučių.", "This requires the latest Riot on your other devices:": "Tam reikia naujausios Riot versijos kituose jūsų įrenginiuose:", - "or another cross-signing capable Matrix client": "arba kitą kryžminį pasirašymą palaikantį Matrix klientą", + "or another cross-signing capable Matrix client": "arba kito kryžminį pasirašymą palaikančio Matrix kliento", "Use Recovery Passphrase or Key": "Naudoti atgavimo slaptafrazę arba raktą", "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Atnaujinkite šį seansą, kad jam būtų leista patvirtinti kitus seansus, suteikiant jiems prieigą prie šifruotų žinučių ir juos pažymint kaip patikimus kitiems vartotojams.", "Use Single Sign On to continue": "Norėdami tęsti naudokite Vieno Prisijungimo sistemą", @@ -1426,7 +1426,7 @@ "Enable desktop notifications for this session": "Įjungti darbalaukio pranešimus šiam seansui", "Enable audible notifications for this session": "Įjungti garsinius pranešimus šiam seansui", "wait and try again later": "palaukite ir bandykite vėliau dar kartą", - "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Jei jūs nenorite naudoti radimui ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, žemiau įveskite kitą tapatybės serverį.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Jei jūs nenorite naudoti serverio radimui ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, žemiau įveskite kitą tapatybės serverį.", "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Tapatybės serverio naudojimas yra pasirinktinis. Jei jūs pasirinksite jo nenaudoti, jūs nebūsite randamas kitų vartotojų ir neturėsite galimybės pakviesti kitų nurodydamas el. paštą ar telefoną.", "Do not use an identity server": "Nenaudoti tapatybės serverio", "Cross-signing": "Kryžminis pasirašymas", @@ -1462,7 +1462,7 @@ "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.": "Pasiekite savo saugių žinučių istoriją ir kryžminio pasirašymo tapatybę, naudojamą kitų seansų patvirtinimui, įvesdami savo atgavimo raktą.", "Session verified": "Seansas patvirtintas", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Neįmanoma prisijungti prie serverio per HTTP, kai naršyklės juostoje yra HTTPS URL. Naudokite HTTPS arba įjunkite nesaugias rašmenas.", - "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Jūsų naujas seansas dabar yra patvirtintas. Jis turi prieigą prie jūsų šifruotų žinučių ir kiti vartotojai matys jį kaip patikimą.", + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Jūsų naujas seansas buvo patvirtintas. Jis turi prieigą prie jūsų šifruotų žinučių ir kiti vartotojai matys jį kaip patikimą.", "Your new session is now verified. Other users will see it as trusted.": "Jūsų naujas seansas dabar yra patvirtintas. Kiti vartotojai matys jį kaip patikimą.", "NOT verified": "Nepatvirtinta", "verified": "patvirtinta", @@ -1588,5 +1588,36 @@ "Click the button below to confirm setting up encryption.": "Paspauskite mygtuką žemiau, kad patvirtintumėte šifravimo nustatymą.", "Restore your key backup to upgrade your encryption": "Atstatykite savo raktų atsarginę kopiją, kad atnaujintumėte šifravimą", "Upgrade your encryption": "Atnaujinkite savo šifravimą", - "Failed to set topic": "Nepavyko nustatyti temos" + "Failed to set topic": "Nepavyko nustatyti temos", + "Show typing notifications": "Rodyti spausdinimo pranešimus", + "Show hidden events in timeline": "Rodyti paslėptus įvykius laiko juostoje", + "Session backup key:": "Seanso atsarginės kopijos raktas:", + "Unable to load key backup status": "Nepavyko įkelti rakto atsarginės kopijos būklės", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Prieš atsijungdami susiekite šį seansą prie rakto atsarginės kopijos, kad išvengtumėte praradimo raktų, kurie gali būti tik šiame seanse.", + "Connect this session to Key Backup": "Susieti šį seansą su rakto atsargine kopija", + "Backup key stored: ": "Atsarginė rakto kopija saugoma: ", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Tam, kad galėtumėte rasti ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, jūs šiuo metu naudojate tapatybės serverį. Jį pakeisti galite žemiau.", + "Identity Server": "Tapatybės serveris", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Šiuo metu jūs nenaudojate tapatybės serverio. Tam, kad galėtumėte rasti ir tam, kad būtumėte randamas esamų, jums žinomų kontaktų, pridėkite jį žemiau.", + "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Atsijungimas nuo tapatybės serverio reikš, kad jūs nebebūsite randamas kitų vartotojų ir jūs nebegalėsite pakviesti kitų, naudodami jų el. paštą arba telefoną.", + "Appearance": "Išvaizda", + "Deactivate account": "Deaktyvuoti paskyrą", + "Identity Server is": "Tapatybės serveris yra", + "Timeline": "Laiko juosta", + "Key backup": "Atsarginė rakto kopija", + "Where you’re logged in": "Kur esate prisijungę", + "A session's public name is visible to people you communicate with": "Seanso viešas vardas yra matomas žmonių su kuriais jūs bendraujate", + "Try scrolling up in the timeline to see if there are any earlier ones.": "Pabandykite slinkti aukštyn laiko juostoje, kad sužinotumėte, ar yra ankstesnių.", + "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.": "Tai visam laikui padarys jūsų paskyrą nebetinkama naudoti. Jūs nebegalėsite prisijungti ir niekas nebegalės iš naujo užregistruoti to pačio vartotojo ID. Jūsų paskyra išeis iš visų kambarių, kuriuose ji dalyvauja ir pašalins jūsų paskyros detales iš jūsų tapatybės serverio. Šis veiksmas neatšaukiamas.", + "Unable to validate homeserver/identity server": "Neįmanoma patvirtinti serverio/tapatybės serverio", + "No identity server is configured so you cannot add an email address in order to reset your password in the future.": "Nėra sukonfigūruota jokio tapatybės serverio, tad jūs negalite pridėti el. pašto adreso, tam, kad galėtumėte iš naujo nustatyti savo slaptažodį ateityje.", + "Enter your custom identity server URL What does this mean?": "Įveskite savo pasirinktinio tapatybės serverio URL Ką tai reiškia?", + "Identity Server URL": "Tapatybės serverio URL", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Bandyta įkelti konkrečią vietą šio kambario laiko juostoje, bet jūs neturite leidimo peržiūrėti tos žinutės.", + "Failed to load timeline position": "Nepavyko įkelti laiko juostos pozicijos", + "Your Matrix account on %(serverName)s": "Jūsų Matrix paskyra %(serverName)s serveryje", + "Your Matrix account on ": "Jūsų Matrix paskyra serveryje", + "No identity server is configured: add one in server settings to reset your password.": "Nesukonfigūruotas joks tapatybės serveris: pridėkite jį serverio nustatymuose, kad iš naujo nustatytumėte slaptažodį.", + "Identity server URL does not appear to be a valid identity server": "Tapatybės serverio URL neatrodo kaip tinkamas tapatybės serveris", + "Scroll up/down in the timeline": "Slinkite aukštyn/žemyn laiko juostoje" } From 211ad66fea90e3c488a890fb9c7c71e3d24cf1ad Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 08:39:38 -0600 Subject: [PATCH 042/181] Fix i18n --- src/i18n/strings/en_EN.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1f70edc5d1..cf6dc2431a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1134,9 +1134,6 @@ "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", "Not now": "Not now", "Don't ask me again": "Don't ask me again", - "Jump to first unread room.": "Jump to first unread room.", - "Jump to first invite.": "Jump to first invite.", - "Add room": "Add room", "Show %(n)s more": "Show %(n)s more", "Options": "Options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", From 6752c2832ee8a21e95ce105888b2257ec5bfce2a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 08:40:32 -0600 Subject: [PATCH 043/181] Add missing var --- src/components/views/rooms/RoomSublist2.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 15a0ff801b..1a58b67377 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -237,6 +237,7 @@ export default class RoomSublist2 extends React.Component { // TODO: CSS TBD // TODO: Make this an actual tile + const moreTileHeightPx = layout.tileHeight; visibleTiles.splice(visibleTiles.length - 1, 1, (
Date: Fri, 5 Jun 2020 08:48:23 -0600 Subject: [PATCH 044/181] Give the show more button some real CSS This is still somewhat placeholder. --- res/css/views/rooms/_RoomSublist2.scss | 9 +++++++++ src/components/views/rooms/RoomSublist2.tsx | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 4d8c3fffff..da1c23b664 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -38,5 +38,14 @@ limitations under the License. display: flex; flex-direction: column; overflow: hidden; + + .mx_RoomSublist2_showMoreButton { + height: 44px; // 1 room tile high + cursor: pointer; + + // We create a flexbox to cheat at alignment + display: flex; + align-items: center; + } } } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 1a58b67377..90231596fe 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -237,11 +237,10 @@ export default class RoomSublist2 extends React.Component { // TODO: CSS TBD // TODO: Make this an actual tile - const moreTileHeightPx = layout.tileHeight; visibleTiles.splice(visibleTiles.length - 1, 1, (
{_t("Show %(n)s more", {n: numMissing})} From 51038b0c02d934ae14a94fddeb9d889acdc8d6d0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 11:03:18 -0600 Subject: [PATCH 045/181] Replace math hacks with temporary placeholder --- src/components/views/rooms/RoomSublist2.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index e20a36d6ce..dd0d591c53 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -222,11 +222,8 @@ export default class RoomSublist2 extends React.Component { handles = []; // no handles, we're at a minimum } - // TODO: Remove Math hacks - let nVisible = Math.floor(layout.visibleTiles); - if (localStorage.getItem("mx_rl_mathfn")) { - nVisible = Math[localStorage.getItem("mx_rl_mathfn")](layout.visibleTiles); - } + // TODO: This might need adjustment, however for now it is fine as a round. + const nVisible = Math.round(layout.visibleTiles); const visibleTiles = tiles.slice(0, nVisible); // If we're hiding rooms, show a 'show more' button to the user. This button @@ -242,6 +239,7 @@ export default class RoomSublist2 extends React.Component { // TODO: CSS TBD // TODO: Make this an actual tile + // TODO: This is likely to pop out of the list, consider that. visibleTiles.splice(visibleTiles.length - 1, 1, (
Date: Fri, 5 Jun 2020 13:14:44 -0600 Subject: [PATCH 046/181] Minor clarity --- src/components/views/rooms/RoomSublist2.tsx | 1 + src/stores/room-list/ListLayout.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index dd0d591c53..d3bb19729d 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -235,6 +235,7 @@ export default class RoomSublist2 extends React.Component { // we have a cutoff condition - add the button to show all // we +1 to account for the room we're about to hide with our 'show more' button + // this results in the button always being 1+, and not needing an i18n `count`. const numMissing = (tiles.length - visibleTiles.length) + 1; // TODO: CSS TBD diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index fd57a03ca1..b41d56be3e 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { TagID } from "./models"; + const TILE_HEIGHT_PX = 34; interface ISerializedListLayout { @@ -23,7 +25,7 @@ interface ISerializedListLayout { export class ListLayout { private _n = 0; - constructor(public readonly tagId) { + constructor(public readonly tagId: TagID) { const serialized = localStorage.getItem(this.key); if (serialized) { // We don't use the setters as they cause writes. From 1d8833e9f83a50e9223b04c59b8e216b86c56f04 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 14:08:20 -0600 Subject: [PATCH 047/181] Selected state, cleanup, and profile display --- res/css/structures/_LeftPanel2.scss | 3 +++ res/css/views/rooms/_RoomList2.scss | 2 ++ res/css/views/rooms/_RoomSublist2.scss | 2 ++ res/css/views/rooms/_RoomTile2.scss | 13 +++++++++++-- res/themes/light/css/_light.scss | 1 + src/ActiveRoomObserver.js | 14 +++++++++----- src/components/structures/LeftPanel2.tsx | 17 +++++++++++++---- src/components/views/rooms/RoomTile2.tsx | 11 ++++++++++- 8 files changed, 51 insertions(+), 12 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 52ee4f16ac..822a5ac399 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -14,6 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO: Rename to mx_LeftPanel during replacement of old component + +// TODO: Put these variables in the right place, or namespace them. $tagPanelWidth: 70px; $roomListMinimizedWidth: 50px; diff --git a/res/css/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss index add7214468..89760958f9 100644 --- a/res/css/views/rooms/_RoomList2.scss +++ b/res/css/views/rooms/_RoomList2.scss @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO: Rename to mx_RoomList during replacement of old component + .mx_RoomList2 { // Create a column-based flexbox for the sublists. That's pretty much all we have to // worry about in this stylesheet. diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index da1c23b664..3b3eccfd60 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO: Rename to mx_RoomSublist during replacement of old component + @import "../../../../node_modules/react-resizable/css/styles.css"; .mx_RoomSublist2 { diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index bb27942f81..3151bb8716 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -14,15 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO: Rename to mx_RoomTile during replacement of old component + // Note: the room tile expects to be in a flexbox column container .mx_RoomTile2 { - width: 100%; - padding-bottom: 12px; + width: calc(100% - 11px); // 8px for padding (4px on either side), 3px for margin + margin-bottom: 4px; + margin-right: 3px; + padding: 4px; // The tile is also a flexbox row itself display: flex; flex-wrap: wrap; + &.mx_RoomTile2_selected { + background-color: $roomtile2-selected-bg-color; + border-radius: 32px; + } + .mx_RoomTile2_avatarContainer { margin-right: 8px; } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 683c02528d..5aeb125774 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -176,6 +176,7 @@ $header-divider-color: #91A1C0; // TODO: Dark theme $roomtile2-preview-color: #9e9e9e; $roomtile2-badge-color: #61708b; +$roomtile2-selected-bg-color: #FFF; $roomtile-name-color: #61708b; $roomtile-badge-fg-color: $accent-fg-color; diff --git a/src/ActiveRoomObserver.js b/src/ActiveRoomObserver.js index d6fbb460b5..b7695d401d 100644 --- a/src/ActiveRoomObserver.js +++ b/src/ActiveRoomObserver.js @@ -27,7 +27,7 @@ import RoomViewStore from './stores/RoomViewStore'; */ class ActiveRoomObserver { constructor() { - this._listeners = {}; + this._listeners = {}; // key=roomId, value=function(isActive:boolean) this._activeRoomId = RoomViewStore.getRoomId(); // TODO: We could self-destruct when the last listener goes away, or at least @@ -35,6 +35,10 @@ class ActiveRoomObserver { this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this)); } + get activeRoomId(): string { + return this._activeRoomId; + } + addListener(roomId, listener) { if (!this._listeners[roomId]) this._listeners[roomId] = []; this._listeners[roomId].push(listener); @@ -51,23 +55,23 @@ class ActiveRoomObserver { } } - _emit(roomId) { + _emit(roomId, isActive: boolean) { if (!this._listeners[roomId]) return; for (const l of this._listeners[roomId]) { - l.call(); + l.call(null, isActive); } } _onRoomViewStoreUpdate() { // emit for the old room ID - if (this._activeRoomId) this._emit(this._activeRoomId); + if (this._activeRoomId) this._emit(this._activeRoomId, false); // update our cache this._activeRoomId = RoomViewStore.getRoomId(); // and emit for the new one - if (this._activeRoomId) this._emit(this._activeRoomId); + if (this._activeRoomId) this._emit(this._activeRoomId, true); } } diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index bf0e72beeb..00419465eb 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -86,26 +86,35 @@ export default class LeftPanel2 extends React.Component { } private renderHeader(): React.ReactNode { - // TODO: Use real profile info + // TODO: Update when profile info changes // TODO: Presence // TODO: Breadcrumbs toggle // TODO: Menu button const avatarSize = 32; + // TODO: Don't do this profile lookup in render() + const client = MatrixClientPeg.get(); + let displayName = client.getUserId(); + let avatarUrl: string = null; + const myUser = client.getUser(client.getUserId()); + if (myUser) { + displayName = myUser.rawDisplayName; + avatarUrl = myUser.avatarUrl; + } return (
- Irene + {displayName}
diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 8a51327ae2..09d7b46ba5 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -23,7 +23,6 @@ import classNames from "classnames"; import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomAvatar from "../../views/avatars/RoomAvatar"; -import Tooltip from "../../views/elements/Tooltip"; import dis from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; import * as RoomNotifs from '../../../RoomNotifs'; @@ -32,6 +31,7 @@ import * as Unread from '../../../Unread'; import * as FormattingUtils from "../../../utils/FormattingUtils"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import ActiveRoomObserver from "../../../ActiveRoomObserver"; /******************************************************************* * CAUTION * @@ -66,6 +66,7 @@ interface INotificationState { interface IState { hover: boolean; notificationState: INotificationState; + selected: boolean; } export default class RoomTile2 extends React.Component { @@ -88,12 +89,14 @@ export default class RoomTile2 extends React.Component { this.state = { hover: false, notificationState: this.getNotificationState(), + selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId, }; this.props.room.on("Room.receipt", this.handleRoomEventUpdate); this.props.room.on("Room.timeline", this.handleRoomEventUpdate); this.props.room.on("Room.redaction", this.handleRoomEventUpdate); MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate); + ActiveRoomObserver.addListener(this.props.room.roomId, this.onActiveRoomUpdate); } public componentWillUnmount() { @@ -101,6 +104,7 @@ export default class RoomTile2 extends React.Component { this.props.room.removeListener("Room.receipt", this.handleRoomEventUpdate); this.props.room.removeListener("Room.timeline", this.handleRoomEventUpdate); this.props.room.removeListener("Room.redaction", this.handleRoomEventUpdate); + ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate); } if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate); @@ -187,6 +191,10 @@ export default class RoomTile2 extends React.Component { }); }; + private onActiveRoomUpdate = (isActive: boolean) => { + this.setState({selected: isActive}); + }; + public render(): React.ReactElement { // TODO: Collapsed state // TODO: Invites @@ -195,6 +203,7 @@ export default class RoomTile2 extends React.Component { const classes = classNames({ 'mx_RoomTile2': true, + 'mx_RoomTile2_selected': this.state.selected, }); let badge; From 829bf3c774498bacad2aadbaecc582821457ccab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 14:11:04 -0600 Subject: [PATCH 048/181] Add another TODO comment --- res/css/views/rooms/_RoomSublist2.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 3b3eccfd60..e6e5af3b48 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -16,6 +16,8 @@ limitations under the License. // TODO: Rename to mx_RoomSublist during replacement of old component +// TODO: Just use the 3 selectors we need from this instead of importing it. +// We're going to end up with heavy modifications anyways. @import "../../../../node_modules/react-resizable/css/styles.css"; .mx_RoomSublist2 { From 2806c8c18ba4f8bf864b981b54011c5211c333ab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 14:13:28 -0600 Subject: [PATCH 049/181] Fix temporary class --- src/components/structures/LeftPanel2.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 00419465eb..c66c0a6799 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -131,7 +131,7 @@ export default class LeftPanel2 extends React.Component { ); const searchBox = ( Date: Fri, 5 Jun 2020 23:19:00 +0100 Subject: [PATCH 050/181] Allow searching the emoji picker using other emoji Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 4 ++-- src/emoji.ts | 4 ++-- yarn.lock | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index eae83d3878..93d59a4fa6 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "create-react-class": "^15.6.0", "diff-dom": "^4.1.3", "diff-match-patch": "^1.0.4", - "emojibase-data": "^4.0.2", - "emojibase-regex": "^3.0.0", + "emojibase-data": "^5.0.1", + "emojibase-regex": "^4.0.1", "escape-html": "^1.0.3", "file-saver": "^1.3.3", "filesize": "3.5.6", diff --git a/src/emoji.ts b/src/emoji.ts index c0a755145a..753b808f4a 100644 --- a/src/emoji.ts +++ b/src/emoji.ts @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// @ts-ignore - import * as EMOJIBASE actually breaks this import EMOJIBASE from 'emojibase-data/en/compact.json'; export interface IEmoji { @@ -70,7 +69,8 @@ EMOJIBASE.forEach((emoji: IEmojiWithFilterString) => { DATA_BY_CATEGORY[categoryId].push(emoji); } // This is used as the string to match the query against when filtering emojis - emoji.filterString = `${emoji.annotation}\n${emoji.shortcodes.join('\n')}}\n${emoji.emoticon || ''}`.toLowerCase(); + emoji.filterString = `${emoji.annotation}\n${emoji.shortcodes.join('\n')}}\n${emoji.emoticon || ''}\n` + + `${emoji.unicode.split("\u200D").join("\n")}`.toLowerCase(); // Add mapping from unicode to Emoji object // The 'unicode' field that we use in emojibase has either diff --git a/yarn.lock b/yarn.lock index 393b89ba26..7e444a0e0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3155,15 +3155,15 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojibase-data@^4.0.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/emojibase-data/-/emojibase-data-4.2.1.tgz#3d1f0c69ddbb2ca7b7014f5e34654190802a40df" - integrity sha512-O0vxoPMgVkRq/uII/gdAjz9RwNv6ClJrd3J9QCCRC4btZRmeut/qohC/Fi+NNXUcjY08RWNTvxSnq/vij8hvrw== +emojibase-data@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/emojibase-data/-/emojibase-data-5.0.1.tgz#ce6fe36b4affd3578e0be8779211018a2fdae960" + integrity sha512-rYWlogJ2q5P78U8Xx1vhsXHcYKu1wFnr7+o6z9QHssZ1SsJLTCkJINZIPHRFWuDreAUK457TkqHpdOXElu0fzA== -emojibase-regex@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/emojibase-regex/-/emojibase-regex-3.2.1.tgz#122935958c9a49c96bb29ac69ccbbac0b2e7022d" - integrity sha512-VAX2Rc2U/alu5q6P2cET2alzC63o1Uarm6Ea/b3ab+KOzxZT4JKmB0tCU1sTZvfNKa16KMLCK2k7hJBHJq4vWQ== +emojibase-regex@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/emojibase-regex/-/emojibase-regex-4.0.1.tgz#a2cd4bbb42825422da9ec72f15e970bc2c90b46a" + integrity sha512-S42UHkFfz15i4NNz+wi9iMKFo+B6Kalc6PJLpYX0BUANViXw4vSyYZMFdBGRLduSabWHuEcTLZl9xOa2YP3eJw== emojis-list@^2.0.0: version "2.1.0" From 0c32daa162e7f3b273f7b09efc1ce3b230398c21 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 5 Jun 2020 23:34:04 +0100 Subject: [PATCH 051/181] label ZWJ as such Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/emoji.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/emoji.ts b/src/emoji.ts index 753b808f4a..53625f3026 100644 --- a/src/emoji.ts +++ b/src/emoji.ts @@ -62,6 +62,8 @@ export const DATA_BY_CATEGORY = { "flags": [], }; +const ZERO_WIDTH_JOINER = "\u200D"; + // Store various mappings from unicode/emoticon/shortcode to the Emoji objects EMOJIBASE.forEach((emoji: IEmojiWithFilterString) => { const categoryId = EMOJIBASE_GROUP_ID_TO_CATEGORY[emoji.group]; @@ -70,7 +72,7 @@ EMOJIBASE.forEach((emoji: IEmojiWithFilterString) => { } // This is used as the string to match the query against when filtering emojis emoji.filterString = `${emoji.annotation}\n${emoji.shortcodes.join('\n')}}\n${emoji.emoticon || ''}\n` + - `${emoji.unicode.split("\u200D").join("\n")}`.toLowerCase(); + `${emoji.unicode.split(ZERO_WIDTH_JOINER).join("\n")}`.toLowerCase(); // Add mapping from unicode to Emoji object // The 'unicode' field that we use in emojibase has either From 6548748d7c3663cc0e3cb4df65bb88f07e208cba Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 18:44:05 -0600 Subject: [PATCH 052/181] Introduce sticky rooms to the new room list Originally this was intended to be done only in the importance algorithm, however it is clear that all algorithms will need to deal with this. As such, it has been put into the base class to deal with as we may override it in the future. This commit should be self-documenting enough to describe what is going on, though the major highlight is that the handling of the sticky room is done by lying to the underlying algorithm. This has not been optimized for performance yet. For https://github.com/vector-im/riot-web/issues/13635 --- src/stores/room-list/README.md | 38 ++--- src/stores/room-list/RoomListStore2.ts | 20 +++ .../algorithms/list-ordering/Algorithm.ts | 136 +++++++++++++++++- .../list-ordering/ImportanceAlgorithm.ts | 26 ++-- .../list-ordering/NaturalAlgorithm.ts | 8 +- src/stores/room-list/models.ts | 1 + 6 files changed, 193 insertions(+), 36 deletions(-) diff --git a/src/stores/room-list/README.md b/src/stores/room-list/README.md index f4a56130ca..ba34691d34 100644 --- a/src/stores/room-list/README.md +++ b/src/stores/room-list/README.md @@ -74,29 +74,29 @@ gets applied to each category in a sub-sub-list fashion. This should result in t being sorted alphabetically amongst each other as well as the grey rooms sorted amongst each other, but collectively the tag will be sorted into categories with red being at the top. - +### Sticky rooms -The algorithm also has a concept of a 'sticky' room which is the room the user is currently viewing. -The sticky room will remain in position on the room list regardless of other factors going on as typically -clicking on a room will cause it to change categories into 'idle'. This is done by preserving N rooms -above the selected room at all times, where N is the number of rooms above the selected rooms when it was -selected. +When the user visits a room, that room becomes 'sticky' in the list, regardless of ordering algorithm. +From a code perspective, the underlying algorithm is not aware of a sticky room and instead the base class +manages which room is sticky. This is to ensure that all algorithms handle it the same. -For example, if the user has 3 red rooms and selects the middle room, they will always see exactly one -room above their selection at all times. If they receive another notification, and the tag ordering is -specified as Recent, they'll see the new notification go to the top position, and the one that was previously -there fall behind the sticky room. +The sticky flag is simply to say it will not move higher or lower down the list while it is active. For +example, if using the importance algorithm, the room would naturally become idle once viewed and thus +would normally fly down the list out of sight. The sticky room concept instead holds it in place, never +letting it fly down until the user moves to another room. -The sticky room's category is technically 'idle' while being viewed and is explicitly pulled out of the -tag sorting algorithm's input as it must maintain its position in the list. When the user moves to another -room, the previous sticky room gets recalculated to determine which category it needs to be in as the user -could have been scrolled up while new messages were received. +Only one room can be sticky at a time. Room updates around the sticky room will still hold the sticky +room in place. The best example of this is the importance algorithm: if the user has 3 red rooms and +selects the middle room, they will see exactly one room above their selection at all times. If they +receive another notification which causes the room to move into the topmost position, the room that was +above the sticky room will move underneath to allow for the new room to take the top slot, maintaining +the sticky room's position. -Further, the sticky room is not aware of category boundaries and thus the user can see a shift in what -kinds of rooms move around their selection. An example would be the user having 4 red rooms, the user -selecting the third room (leaving 2 above it), and then having the rooms above it read on another device. -This would result in 1 red room and 1 other kind of room above the sticky room as it will try to maintain -2 rooms above the sticky room. +Though only applicable to the importance algorithm, the sticky room is not aware of category boundaries +and thus the user can see a shift in what kinds of rooms move around their selection. An example would +be the user having 4 red rooms, the user selecting the third room (leaving 2 above it), and then having +the rooms above it read on another device. This would result in 1 red room and 1 other kind of room +above the sticky room as it will try to maintain 2 rooms above the sticky room. An exception for the sticky room placement is when there's suddenly not enough rooms to maintain the placement exactly. This typically happens if the user selects a room and leaves enough rooms where it cannot maintain diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index af9970d3cc..7f7d2da0f6 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -29,6 +29,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher"; import { readReceiptChangeIsFor } from "../../utils/read-receipts"; import { IFilterCondition } from "./filters/IFilterCondition"; import { TagWatcher } from "./TagWatcher"; +import RoomViewStore from "../RoomViewStore"; interface IState { tagsEnabled?: boolean; @@ -62,6 +63,7 @@ export class RoomListStore2 extends AsyncStore { this.checkEnabled(); for (const settingName of this.watchedSettings) SettingsStore.monitorSetting(settingName, null); + RoomViewStore.addListener(this.onRVSUpdate); } public get orderedLists(): ITagMap { @@ -93,6 +95,23 @@ export class RoomListStore2 extends AsyncStore { this.setAlgorithmClass(); } + private onRVSUpdate = () => { + if (!this.enabled) return; // TODO: Remove enabled flag when RoomListStore2 takes over + if (!this.matrixClient) return; // We assume there won't be RVS updates without a client + + const activeRoomId = RoomViewStore.getRoomId(); + if (!activeRoomId && this.algorithm.stickyRoom) { + this.algorithm.stickyRoom = null; + } else if (activeRoomId) { + const activeRoom = this.matrixClient.getRoom(activeRoomId); + if (!activeRoom) throw new Error(`${activeRoomId} is current in RVS but missing from client`); + if (activeRoom !== this.algorithm.stickyRoom) { + console.log(`Changing sticky room to ${activeRoomId}`); + this.algorithm.stickyRoom = activeRoom; + } + } + }; + protected async onDispatch(payload: ActionPayload) { if (payload.action === 'MatrixActions.sync') { // Filter out anything that isn't the first PREPARED sync. @@ -110,6 +129,7 @@ export class RoomListStore2 extends AsyncStore { console.log("Regenerating room lists: Startup"); await this.readAndCacheSettingsFromStore(); await this.regenerateAllLists(); + this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed } // TODO: Remove this once the RoomListStore becomes default diff --git a/src/stores/room-list/algorithms/list-ordering/Algorithm.ts b/src/stores/room-list/algorithms/list-ordering/Algorithm.ts index 3921bb6221..e8058a2964 100644 --- a/src/stores/room-list/algorithms/list-ordering/Algorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/Algorithm.ts @@ -22,6 +22,7 @@ import { ITagMap, ITagSortingMap } from "../models"; import DMRoomMap from "../../../../utils/DMRoomMap"; import { FILTER_CHANGED, IFilterCondition } from "../../filters/IFilterCondition"; import { EventEmitter } from "events"; +import { UPDATE_EVENT } from "../../../AsyncStore"; // TODO: Add locking support to avoid concurrent writes? @@ -30,6 +31,12 @@ import { EventEmitter } from "events"; */ export const LIST_UPDATED_EVENT = "list_updated_event"; +interface IStickyRoom { + room: Room; + position: number; + tag: TagID; +} + /** * Represents a list ordering algorithm. This class will take care of tag * management (which rooms go in which tags) and ask the implementation to @@ -37,7 +44,9 @@ export const LIST_UPDATED_EVENT = "list_updated_event"; */ export abstract class Algorithm extends EventEmitter { private _cachedRooms: ITagMap = {}; + private _cachedStickyRooms: ITagMap = {}; // a clone of the _cachedRooms, with the sticky room private filteredRooms: ITagMap = {}; + private _stickyRoom: IStickyRoom = null; protected sortAlgorithms: ITagSortingMap; protected rooms: Room[] = []; @@ -51,6 +60,73 @@ export abstract class Algorithm extends EventEmitter { super(); } + public get stickyRoom(): Room { + return this._stickyRoom ? this._stickyRoom.room : null; + } + + public set stickyRoom(val: Room) { + // We wrap this in a closure because we can't use async setters. + // We need async so we can wait for handleRoomUpdate() to do its thing, otherwise + // we risk duplicating rooms. + (async () => { + // It's possible to have no selected room. In that case, clear the sticky room + if (!val) { + if (this._stickyRoom) { + // Lie to the algorithm and re-add the room to the algorithm + await this.handleRoomUpdate(this._stickyRoom.room, RoomUpdateCause.NewRoom); + } + this._stickyRoom = null; + return; + } + + // When we do have a room though, we expect to be able to find it + const tag = this.roomIdsToTags[val.roomId][0]; + if (!tag) throw new Error(`${val.roomId} does not belong to a tag and cannot be sticky`); + let position = this.cachedRooms[tag].indexOf(val); + if (position < 0) throw new Error(`${val.roomId} does not appear to be known and cannot be sticky`); + + // 🐉 Here be dragons. + // Before we can go through with lying to the underlying algorithm about a room + // we need to ensure that when we do we're ready for the innevitable sticky room + // update we'll receive. To prepare for that, we first remove the sticky room and + // recalculate the state ourselves so that when the underlying algorithm calls for + // the same thing it no-ops. After we're done calling the algorithm, we'll issue + // a new update for ourselves. + const lastStickyRoom = this._stickyRoom; + console.log(`Last sticky room:`, lastStickyRoom); + this._stickyRoom = null; + this.recalculateStickyRoom(); + + // When we do have the room, re-add the old room (if needed) to the algorithm + // and remove the sticky room from the algorithm. This is so the underlying + // algorithm doesn't try and confuse itself with the sticky room concept. + if (lastStickyRoom) { + // Lie to the algorithm and re-add the room to the algorithm + await this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom); + } + // Lie to the algorithm and remove the room from it's field of view + await this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); + + // Now that we're done lying to the algorithm, we need to update our position + // marker only if the user is moving further down the same list. If they're switching + // lists, or moving upwards, the position marker will splice in just fine but if + // they went downwards in the same list we'll be off by 1 due to the shifting rooms. + if (lastStickyRoom && lastStickyRoom.tag === tag && lastStickyRoom.position <= position) { + position++; + } + + this._stickyRoom = { + room: val, + position: position, + tag: tag, + }; + this.recalculateStickyRoom(); + + // Finally, trigger an update + this.emit(LIST_UPDATED_EVENT); + })(); + } + protected get hasFilters(): boolean { return this.allowedByFilter.size > 0; } @@ -58,9 +134,14 @@ export abstract class Algorithm extends EventEmitter { protected set cachedRooms(val: ITagMap) { this._cachedRooms = val; this.recalculateFilteredRooms(); + this.recalculateStickyRoom(); } protected get cachedRooms(): ITagMap { + // 🐉 Here be dragons. + // Note: this is used by the underlying algorithm classes, so don't make it return + // the sticky room cache. If it ends up returning the sticky room cache, we end up + // corrupting our caches and confusing them. return this._cachedRooms; } @@ -154,6 +235,59 @@ export abstract class Algorithm extends EventEmitter { console.log(`[DEBUG] ${filteredRooms.length}/${rooms.length} rooms filtered into ${tagId}`); } + /** + * Recalculate the sticky room position. If this is being called in relation to + * a specific tag being updated, it should be given to this function to optimize + * the call. + * @param updatedTag The tag that was updated, if possible. + */ + protected recalculateStickyRoom(updatedTag: TagID = null): void { + // 🐉 Here be dragons. + // This function does far too much for what it should, and is called by many places. + // Not only is this responsible for ensuring the sticky room is held in place at all + // times, it is also responsible for ensuring our clone of the cachedRooms is up to + // date. If either of these desyncs, we see weird behaviour like duplicated rooms, + // outdated lists, and other nonsensical issues that aren't necessarily obvious. + + if (!this._stickyRoom) { + // If there's no sticky room, just do nothing useful. + if (!!this._cachedStickyRooms) { + // Clear the cache if we won't be needing it + this._cachedStickyRooms = null; + this.emit(LIST_UPDATED_EVENT); + } + return; + } + + if (!this._cachedStickyRooms || !updatedTag) { + console.log(`Generating clone of cached rooms for sticky room handling`); + const stickiedTagMap: ITagMap = {}; + for (const tagId of Object.keys(this.cachedRooms)) { + stickiedTagMap[tagId] = this.cachedRooms[tagId].map(r => r); // shallow clone + } + this._cachedStickyRooms = stickiedTagMap; + } + + if (updatedTag) { + // Update the tag indicated by the caller, if possible. This is mostly to ensure + // our cache is up to date. + console.log(`Replacing cached sticky rooms for ${updatedTag}`); + this._cachedStickyRooms[updatedTag] = this.cachedRooms[updatedTag].map(r => r); // shallow clone + } + + // Now try to insert the sticky room, if we need to. + // We need to if there's no updated tag (we regenned the whole cache) or if the tag + // we might have updated from the cache is also our sticky room. + const sticky = this._stickyRoom; + if (!updatedTag || updatedTag === sticky.tag) { + console.log(`Inserting sticky room ${sticky.room.roomId} at position ${sticky.position} in ${sticky.tag}`); + this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room); + } + + // Finally, trigger an update + this.emit(LIST_UPDATED_EVENT); + } + /** * Asks the Algorithm to regenerate all lists, using the tags given * as reference for which lists to generate and which way to generate @@ -174,7 +308,7 @@ export abstract class Algorithm extends EventEmitter { */ public getOrderedRooms(): ITagMap { if (!this.hasFilters) { - return this.cachedRooms; + return this._cachedStickyRooms || this.cachedRooms; } return this.filteredRooms; } diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index 6c4498dad3..ae288a4847 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -17,7 +17,7 @@ limitations under the License. import { Algorithm } from "./Algorithm"; import { Room } from "matrix-js-sdk/src/models/room"; -import { DefaultTagID, RoomUpdateCause, TagID } from "../../models"; +import { RoomUpdateCause, TagID } from "../../models"; import { ITagMap, SortAlgorithm } from "../models"; import { sortRoomsWithAlgorithm } from "../tag-sorting"; import * as Unread from '../../../../Unread'; @@ -82,15 +82,14 @@ export class ImportanceAlgorithm extends Algorithm { // HOW THIS WORKS // -------------- // - // This block of comments assumes you've read the README one level higher. + // This block of comments assumes you've read the README two levels higher. // You should do that if you haven't already. // // Tags are fed into the algorithmic functions from the Algorithm superclass, // which cause subsequent updates to the room list itself. Categories within // those tags are tracked as index numbers within the array (zero = top), with // each sticky room being tracked separately. Internally, the category index - // can be found from `this.indices[tag][category]` and the sticky room information - // from `this.stickyRoom`. + // can be found from `this.indices[tag][category]`. // // The room list store is always provided with the `this.cachedRooms` results, which are // updated as needed and not recalculated often. For example, when a room needs to @@ -102,17 +101,6 @@ export class ImportanceAlgorithm extends Algorithm { [tag: TagID]: ICategoryIndex; } = {}; - // TODO: Use this (see docs above) - private stickyRoom: { - roomId: string; - tag: TagID; - fromTop: number; - } = { - roomId: null, - tag: null, - fromTop: 0, - }; - constructor() { super(); console.log("Constructed an ImportanceAlgorithm"); @@ -195,6 +183,12 @@ export class ImportanceAlgorithm extends Algorithm { return; } + if (cause === RoomUpdateCause.RoomRemoved) { + // TODO: Be smarter and splice rather than regen the planet. + await this.setKnownRooms(this.rooms.filter(r => r !== room)); + return; + } + let tags = this.roomIdsToTags[room.roomId]; if (!tags) { console.warn(`No tags known for "${room.name}" (${room.roomId})`); @@ -251,6 +245,8 @@ export class ImportanceAlgorithm extends Algorithm { taggedRooms.splice(startIdx, 0, ...sorted); // Finally, flag that we've done something + this.recalculateFilteredRoomsForTag(tag); // update filter to re-sort the list + this.recalculateStickyRoom(tag); // update sticky room to make sure it appears if needed changed = true; } return changed; diff --git a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts index e129e98e6f..d544b1196f 100644 --- a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts @@ -46,11 +46,17 @@ export class NaturalAlgorithm extends Algorithm { console.warn(`No tags known for "${room.name}" (${room.roomId})`); return false; } + let changed = false; for (const tag of tags) { // TODO: Optimize this loop to avoid useless operations // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags this.cachedRooms[tag] = await sortRoomsWithAlgorithm(this.cachedRooms[tag], tag, this.sortAlgorithms[tag]); + + // Flag that we've done something + this.recalculateFilteredRoomsForTag(tag); // update filter to re-sort the list + this.recalculateStickyRoom(tag); // update sticky room to make sure it appears if needed + changed = true; } - return true; // assume we changed something + return changed; } } diff --git a/src/stores/room-list/models.ts b/src/stores/room-list/models.ts index 9a27569db4..188e23f7d7 100644 --- a/src/stores/room-list/models.ts +++ b/src/stores/room-list/models.ts @@ -40,4 +40,5 @@ export enum RoomUpdateCause { Timeline = "TIMELINE", RoomRead = "ROOM_READ", // TODO: Use this. NewRoom = "NEW_ROOM", + RoomRemoved = "ROOM_REMOVED", } From 0bb1eefdea6cf566f0a3ed4972c420700d938f65 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 19:47:15 -0600 Subject: [PATCH 053/181] Remove view_room listener as it isn't needed We use the RoomViewStore --- src/stores/room-list/RoomListStore2.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 7f7d2da0f6..882da2b62d 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -226,9 +226,6 @@ export class RoomListStore2 extends AsyncStore { // const roomPayload = (payload); // TODO: Type out the dispatcher types // console.log(`[RoomListDebug] Handling new room ${roomPayload.room.roomId}`); // await this.algorithm.handleRoomUpdate(roomPayload.room, RoomUpdateCause.NewRoom); - } else if (payload.action === 'view_room') { - // TODO: Update sticky room - console.log(payload); } } From 9b928b5a5dac48d2c13ed4d09bebea0c96e6aa80 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 5 Jun 2020 20:12:32 -0600 Subject: [PATCH 054/181] Handle remaining cases for room updates in new room list For https://github.com/vector-im/riot-web/issues/13635 This adds support for: * Tag changes * DM changes * Marking our own rooms as read * Our own membership changes The remaining branch we didn't need was the alternate 'new room' branch, so it was removed. This is not optimized - optimization is deferred. --- src/stores/room-list/RoomListStore2.ts | 52 +++++++++++++------ .../list-ordering/ImportanceAlgorithm.ts | 7 +++ src/stores/room-list/models.ts | 3 +- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index af9970d3cc..e71a8185bf 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -145,13 +145,19 @@ export class RoomListStore2 extends AsyncStore { // First see if the receipt event is for our own user. If it was, trigger // a room update (we probably read the room on a different device). if (readReceiptChangeIsFor(payload.event, this.matrixClient)) { - // TODO: Update room now that it's been read - console.log(payload); + console.log(`[RoomListDebug] Got own read receipt in ${payload.event.roomId}`); + const room = this.matrixClient.getRoom(payload.event.roomId); + if (!room) { + console.warn(`Own read receipt was in unknown room ${payload.event.roomId}`); + return; + } + await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt); return; } } else if (payload.action === 'MatrixActions.Room.tags') { - // TODO: Update room from tags - console.log(payload); + const roomPayload = (payload); // TODO: Type out the dispatcher types + console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`); + await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange); } else if (payload.action === 'MatrixActions.Room.timeline') { const eventPayload = (payload); // TODO: Type out the dispatcher types @@ -189,23 +195,39 @@ export class RoomListStore2 extends AsyncStore { // cause inaccuracies with the list ordering. We may have to decrypt the last N messages of every room :( await this.handleRoomUpdate(room, RoomUpdateCause.Timeline); } else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') { - // TODO: Update DMs - console.log(payload); + const eventPayload = (payload); // TODO: Type out the dispatcher types + console.log(`[RoomListDebug] Received updated DM map`); + const dmMap = eventPayload.event.getContent(); + for (const userId of Object.keys(dmMap)) { + const roomIds = dmMap[userId]; + for (const roomId of roomIds) { + const room = this.matrixClient.getRoom(roomId); + if (!room) { + console.warn(`${roomId} was found in DMs but the room is not in the store`); + continue; + } + + // We expect this RoomUpdateCause to no-op if there's no change, and we don't expect + // the user to have hundreds of rooms to update in one event. As such, we just hammer + // away at updates until the problem is solved. If we were expecting more than a couple + // of rooms to be updated at once, we would consider batching the rooms up. + await this.handleRoomUpdate(room, RoomUpdateCause.PossibleTagChange); + } + } } else if (payload.action === 'MatrixActions.Room.myMembership') { - // TODO: Improve new room check const membershipPayload = (payload); // TODO: Type out the dispatcher types - if (!membershipPayload.oldMembership && membershipPayload.membership === "join") { + if (membershipPayload.oldMembership !== "join" && membershipPayload.membership === "join") { console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`); await this.algorithm.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom); + return; } - // TODO: Update room from membership change - console.log(payload); - } else if (payload.action === 'MatrixActions.Room') { - // TODO: Improve new room check - // const roomPayload = (payload); // TODO: Type out the dispatcher types - // console.log(`[RoomListDebug] Handling new room ${roomPayload.room.roomId}`); - // await this.algorithm.handleRoomUpdate(roomPayload.room, RoomUpdateCause.NewRoom); + // If it's not a join, it's transitioning into a different list (possibly historical) + if (membershipPayload.oldMembership !== membershipPayload.membership) { + console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`); + await this.algorithm.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange); + return; + } } else if (payload.action === 'view_room') { // TODO: Update sticky room console.log(payload); diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index 6c4498dad3..fe13e1972b 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -189,6 +189,13 @@ export class ImportanceAlgorithm extends Algorithm { } public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { + if (cause === RoomUpdateCause.PossibleTagChange) { + // TODO: Be smarter and splice rather than regen the planet. + // TODO: No-op if no change. + await this.setKnownRooms(this.rooms); + return; + } + if (cause === RoomUpdateCause.NewRoom) { // TODO: Be smarter and insert rather than regen the planet. await this.setKnownRooms([room, ...this.rooms]); diff --git a/src/stores/room-list/models.ts b/src/stores/room-list/models.ts index 9a27569db4..43320809d9 100644 --- a/src/stores/room-list/models.ts +++ b/src/stores/room-list/models.ts @@ -38,6 +38,7 @@ export type TagID = string | DefaultTagID; export enum RoomUpdateCause { Timeline = "TIMELINE", - RoomRead = "ROOM_READ", // TODO: Use this. + PossibleTagChange = "POSSIBLE_TAG_CHANGE", + ReadReceipt = "READ_RECEIPT", NewRoom = "NEW_ROOM", } From e8c614d01e117a0721264452d4144fd87eb50767 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Sat, 6 Jun 2020 02:39:02 +0000 Subject: [PATCH 055/181] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2259 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index e4f9faa56b..6e808dd951 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2482,5 +2482,6 @@ "A new version of Riot is available!": "已有新版的 Riot!", "New version available. Update now.": "有可用的新版本。立刻更新。", "Emoji picker": "顏文字挑選器", - "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "您的伺服器管理員已在私人聊天室與直接訊息中預設停用端到端加密。" + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "您的伺服器管理員已在私人聊天室與直接訊息中預設停用端到端加密。", + "Show %(n)s more": "顯示另外 %(n)s 個" } From d960d5772198ed73328741ef130bebfd0ab5a0bb Mon Sep 17 00:00:00 2001 From: Tirifto Date: Sat, 6 Jun 2020 15:44:39 +0000 Subject: [PATCH 056/181] Translated using Weblate (Esperanto) Currently translated at 99.9% (2258 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 74cf3629bb..fa4fe703b6 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2456,5 +2456,6 @@ "Upgrade your Riot": "Gradaltigi vian Rioton", "A new version of Riot is available!": "Nova versio de Riot estas disponebla!", "New version available. Update now.": "Nova versio estas disponebla. Ĝisdatigu nun.", - "Emoji picker": "Elektilo de bildsignoj" + "Emoji picker": "Elektilo de bildsignoj", + "Show %(n)s more": "Montri %(n)s pliajn" } From 1af375ab0ef9fff44aabc816175f8646751a8405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Sat, 6 Jun 2020 08:10:54 +0000 Subject: [PATCH 057/181] Translated using Weblate (French) Currently translated at 100.0% (2259 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 45345e5c7d..5d19cb31e8 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2483,5 +2483,6 @@ "A new version of Riot is available!": "Une nouvelle version de Riot est disponible !", "New version available. Update now.": "Nouvelle version disponible. Faire la mise à niveau maintenant.", "Emoji picker": "Sélecteur d’émojis", - "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "L’administrateur de votre serveur a désactivé le chiffrement de bout en bout par défaut dans les salons privés et les messages directs." + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "L’administrateur de votre serveur a désactivé le chiffrement de bout en bout par défaut dans les salons privés et les messages directs.", + "Show %(n)s more": "En montrer %(n)s de plus" } From 36597e23edd02902aa77f43c0389b04bcf900c53 Mon Sep 17 00:00:00 2001 From: XoseM Date: Sat, 6 Jun 2020 04:43:48 +0000 Subject: [PATCH 058/181] Translated using Weblate (Galician) Currently translated at 67.3% (1521 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 315 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index ba67879d14..f479b4f038 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -1278,5 +1278,318 @@ "Show rooms with unread notifications first": "Mostrar primeiro as salas que teñen notificacións sen ler", "Show shortcuts to recently viewed rooms above the room list": "Mostrar atallos a salas vistas recentemente enriba da lista de salas", "Show hidden events in timeline": "Mostrar na cronoloxía eventos ocultos", - "Low bandwidth mode": "Modo de ancho de banda reducido" + "Low bandwidth mode": "Modo de ancho de banda reducido", + "Straight rows of keys are easy to guess": "Palabras de letras contiguas son doadas de adiviñar", + "Short keyboard patterns are easy to guess": "Patróns curtos de teclas son doados de adiviñar", + "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Permitir o servidor de apoio para chamadas turn.matrix.org cando o servidor propio non ofreza un (o teu IP compartirase durante a chamada)", + "Send read receipts for messages (requires compatible homeserver to disable)": "Enviar resgardos de lectura para as mensaxes (require servidor compatible para desactivar)", + "Show previews/thumbnails for images": "Mostrar miniaturas/vista previa das imaxes", + "Enable message search in encrypted rooms": "Activar a busca de mensaxes en salas cifradas", + "Keep recovery passphrase in memory for this session": "Manter a frase de paso de recuperación en memoria para esta sesión", + "How fast should messages be downloaded.": "Velocidade á que deberían descargarse as mensaxes.", + "Manually verify all remote sessions": "Verificar manualmente todas as sesións remotas", + "IRC display name width": "Ancho do nome mostrado de IRC", + "Messages containing my username": "Mensaxes que conteñen o meu nome de usuaria", + "Messages containing @room": "Mensaxes que conteñen @room", + "Encrypted messages in one-to-one chats": "Mensaxes cifradas en conversas 1:1", + "Encrypted messages in group chats": "Mensaxes cifradas en convesas en grupo", + "When rooms are upgraded": "Cando se actualizan as salas", + "My Ban List": "Listaxe de bloqueo", + "This is your list of users/servers you have blocked - don't leave the room!": "Esta é a listaxe de usuarias/servidores que ti bloqueaches - non deixes a sala!", + "The other party cancelled the verification.": "A outra parte cancelou a verificación.", + "Verified!": "Verificada!", + "You've successfully verified this user.": "Verificaches esta usuaria.", + "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "As mensaxes seguras con esta usuaria están cifradas extremo-a-extremo e non son lexibles por terceiras.", + "Got It": "Vale", + "Verify this session by completing one of the following:": "Verifica esta sesión completando un dos seguintes:", + "Scan this unique code": "Escanea este código único", + "or": "ou", + "Compare unique emoji": "Compara os emoji", + "Compare a unique set of emoji if you don't have a camera on either device": "Compara o conxunto único de emoticonas se non tes cámara no outro dispositivo", + "Start": "Comezar", + "Confirm the emoji below are displayed on both sessions, in the same order:": "Confirma que as emoticonas se mostran nas dúas sesións, na mesma orde:", + "Verify this user by confirming the following emoji appear on their screen.": "Verifica a usuaria confirmando que as emoticonas aparecen na súa pantalla.", + "Verify this session by confirming the following number appears on its screen.": "Verifica esta sesión confirmando que o seguinte número aparece na súa pantalla.", + "Verify this user by confirming the following number appears on their screen.": "Verifica esta usuaria confirmando que o seguinte número aparece na súa pantalla.", + "Unable to find a supported verification method.": "Non se atopa un método de verificación válido.", + "Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…": "Agardando pola outra sesión, %(deviceName)s %(deviceId)s, para verificar…", + "Waiting for your other session to verify…": "Agardando pola túa outra sesión para verificar…", + "Waiting for %(displayName)s to verify…": "Agardando por %(displayName)s para verificar…", + "Cancelling…": "Cancelando…", + "They match": "Concordan", + "They don't match": "Non concordan", + "To be secure, do this in person or use a trusted way to communicate.": "Para estar seguro, fai esto en persoa ou utiliza un xeito seguro para comunicarte.", + "Dog": "Can", + "Cat": "Gato", + "Lion": "León", + "Horse": "Cabalo", + "Unicorn": "Unicorno", + "Pig": "Porco", + "Elephant": "Elefante", + "Rabbit": "Coello", + "Panda": "Panda", + "Rooster": "Galo", + "Penguin": "Pingüino", + "Turtle": "Tartaruga", + "Fish": "Peixe", + "Octopus": "Polbo", + "Butterfly": "Bolboreta", + "Flower": "Flor", + "Tree": "Árbore", + "Cactus": "Cactus", + "Mushroom": "Cogomelo", + "Globe": "Globo", + "Moon": "Lúa", + "Cloud": "Nube", + "Fire": "Lume", + "Banana": "Plátano", + "Apple": "Mazá", + "Strawberry": "Amorodo", + "Corn": "Millo", + "Pizza": "Pizza", + "Cake": "Biscoito", + "Heart": "Corazón", + "Smiley": "Sorriso", + "Robot": "Robot", + "Hat": "Sombreiro", + "Glasses": "Gafas", + "Spanner": "Ferramenta", + "Santa": "Nöel", + "Thumbs up": "Oká", + "Umbrella": "Paraugas", + "Hourglass": "Reloxo area", + "Clock": "Reloxo", + "Gift": "Agasallo", + "Light bulb": "Lámpada", + "Book": "Libro", + "Pencil": "Lápis", + "Paperclip": "Prendedor", + "Scissors": "Tesoiras", + "Lock": "Cadeado", + "Key": "Chave", + "Hammer": "Martelo", + "Telephone": "Teléfono", + "Flag": "Bandeira", + "Train": "Tren", + "Bicycle": "Bicicleta", + "Aeroplane": "Aeroplano", + "Rocket": "Foguete", + "Trophy": "Trofeo", + "Ball": "Bola", + "Guitar": "Guitarra", + "Trumpet": "Trompeta", + "Bell": "Campá", + "Anchor": "Áncora", + "Headphones": "Auriculares", + "Folder": "Cartafol", + "Pin": "Pin", + "From %(deviceName)s (%(deviceId)s)": "Desde %(deviceName)s (%(deviceId)s)", + "Decline (%(counter)s)": "Rexeitar (%(counter)s)", + "Accept to continue:": "Acepta para continuar:", + "Upload": "Subir", + "This bridge was provisioned by .": "Esta ponte está proporcionada por .", + "This bridge is managed by .": "Esta ponte está xestionada por .", + "Workspace: %(networkName)s": "Espazo de traballo: %(networkName)s", + "Channel: %(channelName)s": "Canal: %(channelName)s", + "Show less": "Mostrar menos", + "Show more": "Mostrar máis", + "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Ao cambiar o contrasinal vas restablecer todas as chaves de cifrado extremo-a-extremo en tódalas sesións, facendo que o historial de conversa cifrado non sexa lexible, a menos que primeiro exportes todas as chaves das salas e as importes posteriormente. No futuro melloraremos o procedemento.", + "Your homeserver does not support cross-signing.": "O teu servidor non soporta a sinatura cruzada.", + "Cross-signing and secret storage are enabled.": "A sinatura cruzada e o almacenaxe segredo está activados.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "A túa conta ten unha identidade de sinatura cruzada no almacenaxe segredo, pero aínda non confiaches nela nesta sesión.", + "Cross-signing and secret storage are not yet set up.": "A sinatura cruzada e almacenaxe segredo aínda non se configuraron.", + "Reset cross-signing and secret storage": "Restablecer sinatura cruzada e almacenaxe segredo", + "Bootstrap cross-signing and secret storage": "Configurar sinatura cruzada e almacenaxe segredo", + "well formed": "ben formado", + "unexpected type": "tipo non agardado", + "Cross-signing public keys:": "Chaves públicas da sinatura cruzada:", + "in memory": "en memoria", + "not found": "non atopado", + "Cross-signing private keys:": "Chaves privadas da sinatura cruzada:", + "in secret storage": "no almacenaxe segredo", + "Self signing private key:": "Auto asinado da chave privada:", + "cached locally": "na caché local", + "not found locally": "non se atopa localmente", + "User signing private key:": "Chave privada de sinatura da usuaria:", + "Session backup key:": "Chave de apoio da sesión:", + "Secret storage public key:": "Chave pública da almacenaxe segreda:", + "in account data": "nos datos da conta", + "Homeserver feature support:": "Soporte de funcións do servidor:", + "exists": "existe", + "Your homeserver does not support session management.": "O teu servidor non soporta a xestión da sesión.", + "Unable to load session list": "Non se puido cargar a lista de sesións", + "Confirm deleting these sessions": "Confirma o borrado destas sesións", + "Click the button below to confirm deleting these sessions.|other": "Preme no botón inferior para confirmar o borrado das sesións.", + "Click the button below to confirm deleting these sessions.|one": "Preme no botón inferior para confirmar o borrado da sesión.", + "Delete sessions|other": "Borrar sesións", + "Delete sessions|one": "Borrar sesión", + "Delete %(count)s sessions|other": "Borrar %(count)s sesións", + "Delete %(count)s sessions|one": "Borrar %(count)s sesión", + "ID": "ID", + "Public Name": "Nome público", + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Verificar individualmente cada sesión utilizada pola usuaria para marcala como confiable, non confiando en dispositivos con sinatura cruzada.", + "Securely cache encrypted messages locally for them to appear in search results, using ": "Gardar de xeito seguro na caché mensaxes cifradas para que aparezan nos resultados de busca, usando ", + " to store messages from ": " para gardar mensaxes de ", + "rooms.": "salas.", + "Manage": "Xestionar", + "Securely cache encrypted messages locally for them to appear in search results.": "Gardar de xeito seguro mensaxes cifradas na caché local para que aparezan nos resultados de buscas.", + "Enable": "Activar", + "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.": "Falta un compoñente de Riot requerido para almacenar localmente mensaxes cifradas na caché. Se queres experimentar con esta función, compila unha versión personalizada de Riot Desktop cos compoñentes de busca engadidos.", + "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 non pode gardar de xeito seguro localmente as mensaxes cifradas se se executa nun navegador. Usa Riot Desktop para que as mensaxes cifradas aparezan nas buscas.", + "Connecting to integration manager...": "Conectando co xestor de integración...", + "Cannot connect to integration manager": "Non se puido conectar co xestor de intregración", + "The integration manager is offline or it cannot reach your homeserver.": "O xestor de integración non está en liña ou non é accesible desde o teu servidor.", + "Delete Backup": "Borrar copia de apoio", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Estás seguro? Perderás as mensaxes cifradas se non tes unha copia de apoio das chaves de cifrado.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "As mensaxes cifradas están seguras con cifrado de extremo-a-extremo. Só ti e o correpondente(s) tedes as chaves para ler as mensaxes.", + "Unable to load key backup status": "Non se puido cargar o estado das chaves de apoio", + "Restore from Backup": "Restaurar desde copia de apoio", + "This session is backing up your keys. ": "Esta sesión está gardando as túas chaves. ", + "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Esta sesión non está facendo copia das chaves, pero tes unha copia de apoio existente que podes restablecer e engadir para seguir adiante.", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Conecta esta sesión ao gardado das chaves antes de desconectarte para evitar perder calquera chave que só puidese estar nesta sesión.", + "Connect this session to Key Backup": "Conecta esta sesión a Copia de Apoio de chaves", + "not stored": "non gardado", + "Backing up %(sessionsRemaining)s keys...": "Copiando %(sessionsRemaining)s chaves...", + "All keys backed up": "Copiaronse todas as chaves", + "Backup has a valid signature from this user": "A copia ten unha sinatura válida desta usuaria", + "Backup has a invalid signature from this user": "A copia ten una sinatura non válida desta usuaria", + "Backup has a signature from unknown user with ID %(deviceId)s": "A copia ten unha sinatura dunha usuaria descoñecida con ID %(deviceId)s", + "Backup has a signature from unknown session with ID %(deviceId)s": "A copia ten unha sinatura dunha sesión descoñecida con ID %(deviceId)s", + "Backup has a valid signature from this session": "A copia ten unha sinatura válida desde esta sesión", + "Backup has an invalid signature from this session": "A copia ten unha sinatura non válida desde esta sesión", + "Backup has a valid signature from verified session ": "A copia ten unha sinatura válida desde a sesión verificada en ", + "Backup has a valid signature from unverified session ": "A copia ten unha sinatura válida desde a sesión non verificada en ", + "Backup has an invalid signature from verified session ": "A copia ten unha sinatura non válida desde a sesión verificada en ", + "Backup has an invalid signature from unverified session ": "A copia ten unha sinatura non válida desde a sesión non verificada en ", + "Backup is not signed by any of your sessions": "A copia non está asinada por ningunha das túas sesións", + "This backup is trusted because it has been restored on this session": "Esta copia é de confianza porque foi restaurada nesta sesión", + "Backup version: ": "Versión da copia: ", + "Algorithm: ": "Algoritmo: ", + "Backup key stored: ": "Chave de apoio gardada: ", + "Your keys are not being backed up from this session.": "As túas chaves non están a ser copiadas desde esta sesión.", + "Back up your keys before signing out to avoid losing them.": "Fai unha copia de apoio das chaves antes de desconectarte para evitar perdelas.", + "Start using Key Backup": "Fai unha Copia de apoio das chaves", + "Clear notifications": "Eliminar notificacións", + "Add an email address to configure email notifications": "Engade un enderezo de email para configurar as notificacións por email", + "Enable desktop notifications for this session": "Activa as notificacións de escritorio para esta sesión", + "Enable audible notifications for this session": "Activa as notificacións por son para esta sesión", + "Upgrade to your own domain": "Mellora e usa un dominio propio", + "Display Name": "Nome mostrado", + "Profile picture": "Imaxe de perfil", + "Identity Server URL must be HTTPS": "O URL do servidor de identidade debe comezar HTTPS", + "Not a valid Identity Server (status code %(code)s)": "Servidor de Identidade non válido (código de estado %(code)s)", + "Could not connect to Identity Server": "Non hai conexión co Servidor de Identidade", + "Checking server": "Comprobando servidor", + "Change identity server": "Cambiar de servidor de identidade", + "Disconnect from the identity server and connect to instead?": "Desconectar do servidor de identidade e conectar con ?", + "Terms of service not accepted or the identity server is invalid.": "Non se aceptaron os Termos do servizo ou o servidor de identidade non é válido.", + "The identity server you have chosen does not have any terms of service.": "O servidor de identidade escollido non ten establecidos termos do servizo.", + "Disconnect identity server": "Desconectar servidor de identidade", + "Disconnect from the identity server ?": "Desconectar do servidor de identidade ?", + "Disconnect": "Desconectar", + "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Deberías eliminar os datos personais do servidor de identidade antes de desconectar. Desgraciadamente, o servidor non está en liña e no se pode acceder.", + "You should:": "Deberías:", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "comprobar os engadidos do navegador por algún está bloqueando o servidor de identidade (como Privacy Badger)", + "contact the administrators of identity server ": "contactar coa administración do servidor de identidade ", + "wait and try again later": "agardar e probar máis tarde", + "Disconnect anyway": "Desconectar igualmente", + "You are still sharing your personal data on the identity server .": "Aínda estás compartindo datos personais no servidor de identidade .", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Recomendámosche que elimines os teus enderezos de email e números de teléfono do servidor de identidade antes de desconectar del.", + "Go back": "Atrás", + "Identity Server (%(server)s)": "Servidor de Identidade (%(server)s)", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Neste intre usas para atopar e ser atopado polos contactos existentes que coñeces. Aquí abaixo podes cambiar de servidor de identidade.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Se non queres usar para atopar e ser atopado polos contactos existentes que coñeces, escribe embaixo outro servidor de identidade.", + "Identity Server": "Servidor de Identidade", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Non estás a usar un servidor de identidade. Para atopar e ser atopado polos contactos existentes que coñeces, engade un embaixo.", + "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Ao desconectar do teu servidor de identidade non te poderán atopar as outras usuarias e non poderás convidar a outras polo seu email ou teléfono.", + "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Usar un servidor de identidade é optativo. Se escolles non usar un, non poderás ser atopado por outras usuarias e non poderás convidar a outras polo seu email ou teléfono.", + "Do not use an identity server": "Non usar un servidor de identidade", + "Enter a new identity server": "Escribe o novo servidor de identidade", + "Change": "Cambiar", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Usa un Xestor de Integración (%(serverName)s) para xestionar bots, widgets e paquetes de pegatinas.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Usa un Xestor de Integracións para xestionar bots, widgets e paquetes de pegatinas.", + "Manage integrations": "Xestionar integracións", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Os xestores de integracións reciben datos de configuración, e poden modificar os widgets, enviar convites das salas, e establecer roles no teu nome.", + "New version available. Update now.": "Nova versión dispoñible. Actualiza.", + "Size must be a number": "O tamaño ten que ser un número", + "Custom font size can only be between %(min)s pt and %(max)s pt": "O tamaño da fonte só pode estar entre %(min)s pt e %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Usa entre %(min)s pt e %(max)s pt", + "Appearance": "Aparencia", + "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Cambiouse o contrasinal. Non recibirás notificacións push noutras sesións ata que desconectes e voltes a conectar nelas", + "Email addresses": "Enderezos de email", + "Phone numbers": "Número de teléfono", + "Set a new account password...": "Establecer novo contrasinal da conta...", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Acepta os Termos do Servizo do servidor (%(serverName)s) para permitir que te atopen polo enderezo de email ou número de teléfono.", + "Account management": "Xestión da conta", + "Deactivating your account is a permanent action - be careful!": "A desactivación da conta será permanente - ten coidado!", + "Credits": "Créditos", + "Chat with Riot Bot": "Chat co Bot Riot", + "Bug reporting": "Informar de fallos", + "Clear cache and reload": "Baleirar caché e recargar", + "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.": "Para informar dun asunto relacionado coa seguridade de Matrix, le a Política de Revelación de Privacidade de Matrix.org.", + "FAQ": "PMF", + "Keyboard Shortcuts": "Atallos de teclado", + "Versions": "Versións", + "Customise your experience with experimental labs features. Learn more.": "Personaliza a túa experiencia con características experimentais. Coñecer máis.", + "Ignored/Blocked": "Ignorado/Bloqueado", + "Error adding ignored user/server": "Fallo ao engadir a ignorado usuaria/servidor", + "Something went wrong. Please try again or view your console for hints.": "Algo fallou. Inténtao outra vez o mira na consola para ter algunha pista.", + "Error subscribing to list": "Fallo ao subscribirse a lista", + "Please verify the room ID or address and try again.": "Comproba o ID da sala ou enderezo e proba outra vez.", + "Error removing ignored user/server": "Fallo ao eliminar a usuaria/servidor de ignorados", + "Error unsubscribing from list": "Fallo ao retirar a susbscrición a lista", + "Please try again or view your console for hints.": "Inténtao outra vez ou mira na consola para ter algunha pista.", + "None": "Nada", + "Ban list rules - %(roomName)s": "Regras de bloqueo - %(roomName)s", + "Server rules": "Regras do servidor", + "User rules": "Regras da usuaria", + "You have not ignored anyone.": "Non ignoraches a ninguén.", + "You are currently ignoring:": "Estás a ignorar a:", + "You are not subscribed to any lists": "Non estás subscrita a ningunha lista", + "Unsubscribe": "Baixa na subscrición", + "View rules": "Ver regras", + "You are currently subscribed to:": "Estas subscrito a:", + "Ignored users": "Usuarias ignoradas", + "⚠ These settings are meant for advanced users.": "⚠ Estos axustes van dirixidos a usuarias avanzadas.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Engade aquí usuarias e servidores que desexas ignorar. Usa asterisco que Riot usará como comodín. Exemplo, @bot* ignorará todas as usuarias de calquera servidor que teñan 'bot' no nome.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Ignorar a persoas faise a través de listaxes de bloqueo que conteñen regras. Subscribíndote a unha listaxe de bloqueo fará que esas usuarias/servidores sexan inaccesibles para ti.", + "Personal ban list": "Lista personal de bloqueo", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "A túa listaxe personal de bloqueo acolle as usuarias/servidores que personalmente non desexas ver. Tras ignorar a túa primeira usuaria/servidor, unha nova sala chamada 'Listaxe de bloqueos' aparecerá na listaxe de salas - non saias desta sala para que o bloqueo siga surtindo efecto.", + "Server or user ID to ignore": "ID de usuaria ou servidor a ignorar", + "eg: @bot:* or example.org": "ex: @bot:* ou exemplo.org", + "Subscribed lists": "Listaxes subscritas", + "If this isn't what you want, please use a different tool to ignore users.": "Se esto non é o que queres, usa unha ferramenta diferente para ignorar usuarias.", + "Room ID or address of ban list": "ID da sala ou enderezo da listaxe de bloqueo", + "Subscribe": "Subscribir", + "Always show the window menu bar": "Mostrar sempre a barra de menú da ventá", + "Show tray icon and minimize window to it on close": "Mostrar icona na bandexa do sistema e minizar nela ao pechar", + "Preferences": "Preferencias", + "Room list": "Listaxe de Salas", + "Composer": "Editor", + "Timeline": "Cronoloxía", + "Autocomplete delay (ms)": "Retraso no autocompletado (ms)", + "Read Marker lifetime (ms)": "Duración do marcador de lectura (ms)", + "Read Marker off-screen lifetime (ms)": "Duración do marcador de lectura fóra de pantall (ms)", + "Session ID:": "ID da sesión:", + "Session key:": "Chave da sesión:", + "Bulk options": "Opcións agrupadas", + "Accept all %(invitedRooms)s invites": "Aceptar os %(invitedRooms)s convites", + "Key backup": "Copia da Chave", + "Message search": "Buscar mensaxe", + "Cross-signing": "Sinatura cruzada", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "A administración do servidor desactivou por omisión o cifrado extremo-a-extremo en salas privadas e Mensaxes Directas.", + "A session's public name is visible to people you communicate with": "Un nome público de sesión é visible para a xente coa que te comunicas", + "Missing media permissions, click the button below to request.": "Falta permiso acceso multimedia, preme o botón para solicitalo.", + "Request media permissions": "Solicitar permiso a multimedia", + "Voice & Video": "Voz e Vídeo", + "Upgrade this room to the recommended room version": "Actualiza esta sala á versión recomendada", + "this room": "esta sala", + "View older messages in %(roomName)s.": "Ver mensaxes antigas en %(roomName)s.", + "Room information": "Información da sala", + "Internal room ID:": "ID interno da sala:", + "Room version": "Versión da sala", + "Room version:": "Versión da sala:", + "Developer options": "Opcións desenvolvemento", + "Open Devtools": "Open Devtools", + "This room is bridging messages to the following platforms. Learn more.": "Esta sala está enviando mensaxes ás seguintes plataformas. Coñece máis." } From d029696049bca0fc8a490ababc2e4d353f1774ac Mon Sep 17 00:00:00 2001 From: notramo Date: Sat, 6 Jun 2020 10:02:11 +0000 Subject: [PATCH 059/181] Translated using Weblate (Hungarian) Currently translated at 99.9% (2256 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 81526c2c69..896d9e8b24 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2415,7 +2415,7 @@ "Waiting for your other session to verify…": "A másik munkameneted ellenőrzésére várunk…", "You've successfully verified your device!": "Sikeresen ellenőrizted az eszközödet!", "Message deleted": "Üzenet törölve", - "Message deleted by %(name)s": "Üzenetet törölte: %(name)s", + "Message deleted by %(name)s": "%(name)s törölte az üzenetet", "QR Code": "QR kód", "To continue, use Single Sign On to prove your identity.": "A folytatáshoz a személyazonosságod megerősítéséhez használd az egyszeri bejelentkezést.", "Confirm to continue": "Erősítsd meg a továbblépéshez", From 43be2430338d39d5bf0e0767a3ea19c8373412bd Mon Sep 17 00:00:00 2001 From: Imre Kristoffer Eilertsen Date: Sun, 7 Jun 2020 18:23:20 +0000 Subject: [PATCH 060/181] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 56.8% (1283 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nb_NO/ --- src/i18n/strings/nb_NO.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/nb_NO.json b/src/i18n/strings/nb_NO.json index 8554e3d86b..c26cf7abeb 100644 --- a/src/i18n/strings/nb_NO.json +++ b/src/i18n/strings/nb_NO.json @@ -1344,5 +1344,10 @@ "Repeat your recovery passphrase...": "Gjenta gjenopprettingspassfrasen din …", "Other users may not trust it": "Andre brukere kan kanskje mistro den", " reacted with %(content)s": " reagerte med %(content)s", - "reacted with %(shortName)s": " reagerte med %(shortName)s" + "reacted with %(shortName)s": " reagerte med %(shortName)s", + "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s kan ikke forhåndsvises. Vil du bli med i den?", + "Messages in this room are end-to-end encrypted.": "Meldinger i dette rommet er start-til-slutt-kryptert.", + "Messages in this room are not end-to-end encrypted.": "Meldinger i dette rommet er ikke start-til-slutt-kryptert.", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Meldinger i dette rommet er start-til-slutt-kryptert. Lær mer og verifiser denne brukeren i brukerprofilen deres.", + "Use a different passphrase?": "Vil du bruke en annen passfrase?" } From 8dfed9e597452664449d8c3243fffafa680afaa3 Mon Sep 17 00:00:00 2001 From: Nils Haugen Date: Sun, 7 Jun 2020 10:33:15 +0000 Subject: [PATCH 061/181] Translated using Weblate (Norwegian Nynorsk) Currently translated at 56.6% (1278 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nn/ --- src/i18n/strings/nn.json | 105 ++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/src/i18n/strings/nn.json b/src/i18n/strings/nn.json index 16d2fd276d..52b2c2e83d 100644 --- a/src/i18n/strings/nn.json +++ b/src/i18n/strings/nn.json @@ -13,7 +13,7 @@ "Answer": "Svar", "You are already in a call.": "Du er allereie i ei samtale.", "VoIP is unsupported": "VoIP er ikkje støtta", - "You cannot place VoIP calls in this browser.": "Du kan ikkje utføre med anrop med VoIP i denne nettlesaren.", + "You cannot place VoIP calls in this browser.": "Du kan ikkje utføra samtalar med VoIP i denne nettlesaren.", "You cannot place a call with yourself.": "Du kan ikkje samtala med deg sjølv.", "Could not connect to the integration server": "Kunne ikkje kopla til integreringstenaren", "Call in Progress": "Ei Samtale er i Gang", @@ -167,7 +167,7 @@ "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s-widget lagt til av %(senderName)s", "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget fjerna av %(senderName)s", "Failure to create room": "Klarte ikkje å laga rommet", - "Server may be unavailable, overloaded, or you hit a bug.": "Serveren er kanskje utilgjengeleg, overlasta elles så traff du ein bug.", + "Server may be unavailable, overloaded, or you hit a bug.": "Tenaren er kanskje utilgjengeleg, overlasta elles så traff du ein bug.", "Send anyway": "Send likevel", "Send": "Send", "Unnamed Room": "Rom utan namn", @@ -305,7 +305,7 @@ "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (tilgangsnivå %(powerLevelNumber)s)", "Attachment": "Vedlegg", "Hangup": "Legg på", - "Voice call": "Taleanrop", + "Voice call": "Talesamtale", "Video call": "Videosamtale", "Upload file": "Last opp fil", "Send an encrypted reply…": "Send eit kryptert svar…", @@ -762,7 +762,7 @@ "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|other": "Send alle på nytt eller avbryt alle. Du kan og markere enkelte meldingar for å sende på nytt eller avbryte.", "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Send melding på nytt eller avbryt.", "Connectivity to the server has been lost.": "Tilkoplinga til tenaren vart tapt.", - "Sent messages will be stored until your connection has returned.": "Sendte meldingar vil blir lagra lokalt fram til nettverket er oppe igjen.", + "Sent messages will be stored until your connection has returned.": "Sende meldingar vil lagrast lokalt fram til nettverket er oppe att.", "Active call": "Pågåande samtale", "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Det er ingen andre her! Vil du invitera andre eller skru av varselet om det tomme rommet??", "You seem to be uploading files, are you sure you want to quit?": "Det ser ut til at du lastar opp filer, er du sikker på at du vil avslutte?", @@ -825,8 +825,8 @@ "Incorrect username and/or password.": "Feil brukarnamn og/eller passord.", "Please note you are logging into the %(hs)s server, not matrix.org.": "Merk deg at du loggar inn på %(hs)s-tenaren, ikkje matrix.org.", "The phone number entered looks invalid": "Det innskrivne telefonnummeret virkar å vere ugyldig", - "Error: Problem communicating with the given homeserver.": "Feil: Det gjekk ikkje an å kommunisere med den spesifiserte heimeserveren.", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kan ikkje koble til heimeserveren via HTTP fordi URL-adressa i nettlesaren er HTTPS. Bruk HTTPS, eller aktiver usikre skript.", + "Error: Problem communicating with the given homeserver.": "Feil: Det gjekk ikkje an å kommunisera med den spesifiserte heimetenaren.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kan ikkje kobla til heimetenaren via HTTP fordi URL-adressa i nettlesaren er HTTPS. Bruk HTTPS, eller aktiver usikre skript.", "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Kan ikkje kopla til heimtenaren - ver venleg og sjekk tilkoplinga di, og sjå til at heimtenaren din sitt CCL-sertifikat er stolt på og at ein nettlesartillegg ikkje hindrar førespurnader.", "Failed to fetch avatar URL": "Klarte ikkje å henta avatar-URLen", "Set a display name:": "Set eit visningsnamn:", @@ -894,7 +894,7 @@ "Clear filter": "Tøm filter", "Profile": "Brukar", "Access Token:": "Tilgangs-token:", - "This homeserver doesn't offer any login flows which are supported by this client.": "Heimeserveren tilbyr ingen påloggingsmetodar som er støtta av denne klienten.", + "This homeserver doesn't offer any login flows which are supported by this client.": "Heimetenaren tilbyr ingen innloggingsmetodar som er støtta av denne klienten.", "Claimed Ed25519 fingerprint key": "Gjorde krav på Ed25519-fingeravtrykksnøkkel", "Export room keys": "Eksporter romnøklar", "Export": "Eksporter", @@ -915,10 +915,10 @@ "Whether or not you're logged in (we don't record your username)": "Uansett om du er innlogga eller ikkje (så lagrar vi ikkje brukarnamnet ditt)", "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Fila %(fileName)s er større enn heimetenaren si grense for opplastningar", "Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen.", - "Your Riot is misconfigured": "Riot-klienten din er feilkonfiguert", + "Your Riot is misconfigured": "Riot-klienten din er sett opp feil", "Sign In": "Logg inn", "Explore rooms": "Utforsk romma", - "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldingen din vart ikkje sent for denne heimeserveren har nådd grensa for maksimalt aktive brukarar pr. månad. Kontakt systemadministratoren for å kunne vidare nytte denne tenesten.", + "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldinga di vart ikkje send, for denne heimetenaren har nådd grensa for maksimalt aktive brukarar pr. månad. Kontakt systemadministratoren for å vidare nytte denne tenesta.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Denne meldingen vart ikkje sendt fordi heimeserveren har nådd grensa for tilgjengelege systemressursar. Kontakt systemadministratoren for å vidare nytte denne tenesten.", "Add room": "Legg til rom", "You have %(count)s unread notifications in a prior version of this room.|other": "Du har %(count)s uleste varslingar i ein tidligare versjon av dette rommet.", @@ -928,23 +928,23 @@ "Could not load user profile": "Klarde ikkje å laste brukarprofilen", "Your Matrix account on %(serverName)s": "Din Matrix-konto på %(serverName)s", "Your Matrix account on ": "Din Matrix-konto på ", - "No identity server is configured: add one in server settings to reset your password.": "Ingen identitetsserver er konfigurert: legg til ein i innstillingane for å nullstille passordet ditt.", + "No identity server is configured: add one in server settings to reset your password.": "Ingen identitetstenar er satt opp: legg til ein i innstillingane for å nullstille passordet ditt.", "Sign in instead": "Logg inn istaden", "A verification email will be sent to your inbox to confirm setting your new password.": "For å stadfeste tilbakestilling av passordet, vil ein e-post vil bli sendt til din innboks.", "Your password has been reset.": "Passodet ditt vart nullstilt.", "Set a new password": "Sett nytt passord", - "Invalid homeserver discovery response": "Ugyldig svar frå heimeserveren (discovery response)", - "Failed to get autodiscovery configuration from server": "Kladte ikkje å hente automatisk oppsett frå server", - "Invalid base_url for m.homeserver": "Ugyldig base_url for m.homeserver", - "Homeserver URL does not appear to be a valid Matrix homeserver": "URL-adressa virkar ikkje til å vere ein gyldig Matrix-heimeserver", - "Invalid identity server discovery response": "Ugyldig svar frå identitetsserveren (discovery response)", - "Invalid base_url for m.identity_server": "Ugyldig base_url for m.identity_server", - "Identity server URL does not appear to be a valid identity server": "URL-adressa virkar ikkje til å vere ein gyldig identitetsserver", + "Invalid homeserver discovery response": "Feil svar frå heimetenaren (discovery response)", + "Failed to get autodiscovery configuration from server": "Klarde ikkje å hente automatisk oppsett frå tenaren", + "Invalid base_url for m.homeserver": "Feil base_url for m.homeserver", + "Homeserver URL does not appear to be a valid Matrix homeserver": "URL-adressa virkar ikkje til å vere ein gyldig Matrix-heimetenar", + "Invalid identity server discovery response": "Feil svar frå identitetstenaren (discovery response)", + "Invalid base_url for m.identity_server": "Feil base_url for m.identity_server", + "Identity server URL does not appear to be a valid identity server": "URL-adressa virkar ikkje til å vere ein gyldig identitetstenar", "General failure": "Generell feil", - "This homeserver does not support login using email address.": "Denne heimeserveren støttar ikkje innloggingar med e-postadresser.", + "This homeserver does not support login using email address.": "Denne heimetenaren støttar ikkje innloggingar med e-postadresser.", "Please contact your service administrator to continue using this service.": "Kontakt din systemadministrator for å vidare å bruke tenesta.", "This account has been deactivated.": "Denne kontoen har blitt deaktivert.", - "Failed to perform homeserver discovery": "Fekk ikkje til å utforske heimeserveren", + "Failed to perform homeserver discovery": "Fekk ikkje til å utforska heimetenaren", "Sign in with single sign-on": "Logg på med Single-Sign-On", "Create account": "Lag konto", "Registration has been disabled on this homeserver.": "Registrering er deaktivert på denne heimeserveren.", @@ -955,7 +955,7 @@ "You can now close this window or log in to your new account.": "Du kan lukke dette vindauget ellerlogge inn med din nye konto.", "Registration Successful": "Registrering fullført", "Create your account": "Lag din konto", - "Failed to re-authenticate due to a homeserver problem": "Fekk ikkje til å re-authentisere grunna ein feil på heimeserveren", + "Failed to re-authenticate due to a homeserver problem": "Fekk ikkje til å re-authentisere grunna ein feil på heimetenaren", "Failed to re-authenticate": "Fekk ikkje til å re-autentisere", "Enter your password to sign in and regain access to your account.": "Skriv inn ditt passord for å logge på og ta tilbake tilgang til kontoen din.", "Forgotten your password?": "Gløymt passord ?", @@ -1014,7 +1014,7 @@ "Replying With Files": "Send svar med filer", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Nett no er det ikkje mogleg å senda svar med ei fil. Vil du lasta opp denne fila utan å senda svaret?", "The file '%(fileName)s' failed to upload.": "Fila '%(fileName)s' vart ikkje lasta opp.", - "The server does not support the room version specified.": "Serveren støttar ikkje den spesifikke versjonen av rommet.", + "The server does not support the room version specified.": "Tenaren støttar ikkje den spesifikke versjonen av rommet.", "Name or Matrix ID": "Namn eller Matrix ID", "Registration Required": "Registrering er obligatorisk", "You need to register to do this. Would you like to register now?": "Du må registrera for å gjera dette. Ynskjer du å registrera no?", @@ -1062,7 +1062,7 @@ "Encryption upgrade available": "Kryptering kan oppgraderast", "Set up encryption": "Sett opp kryptering", "Unverified session": "Uverifisert sesjon", - "Identity server has no terms of service": "Identitetsserveren manglar bruksvilkår", + "Identity server has no terms of service": "Identitetstenaren manglar bruksvilkår", "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Denne handlinga krev kommunikasjon mot (standard identitetsserver) for å verifisere e-post eller telefonnummer, men serveren manglar bruksvilkår.", "Only continue if you trust the owner of the server.": "Gå vidare så lenge du har tillit til eigar av serveren.", "Trust": "Tillat", @@ -1094,13 +1094,13 @@ "%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s la til %(addedAddresses)s og tok vekk %(removedAddresses)s som adresser for dette rommet.", "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s satte standardadressa for dette rommet til %(address)s.", "%(senderName)s removed the main address for this room.": "%(senderName)s fjerna standardadressa for dette rommet.", - "%(senderName)s placed a voice call.": "%(senderName)s starta eit taleanrop.", - "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s starta eit taleanrop. (ikkje støtta av denne nettlesaren)", + "%(senderName)s placed a voice call.": "%(senderName)s starta ein talesamtale.", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s starta ein talesamtale. (ikkje støtta av denne nettlesaren)", "%(senderName)s placed a video call.": "%(senderName)s starta ein videosamtale.", "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s starta ein videosamtale. (ikkje støtta av denne nettlesaren)", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s trekte tilbake invitasjonen for at %(targetDisplayName)s kan bli medlem i rommet.", "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Du er administrator for dette fellesskapet. Du kan ikkje melde deg inn igjen utan at du har invitasjon frå ein annan administrator.", - "Want more than a community? Get your own server": "Treng du meir enn eit fellesskap? Skaff din eigen server", + "Want more than a community? Get your own server": "Treng du meir enn eit fellesskap? Skaff din eigen tenar", "Sign In or Create Account": "Logg inn eller opprett konto", "Create Account": "Opprett konto", "Sends the given emote coloured as a rainbow": "Sendar emojien med regnbogefargar", @@ -1197,12 +1197,12 @@ "Hint: Begin your message with // to start it with a slash.": "Hint: Start meldinga med // for å starte den med skråstrek.", "Send as message": "Send som melding", "Failed to revoke invite": "Fekk ikkje til å trekke invitasjonen", - "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Fekk ikkje til å trekke invitasjonen. Det kan ha oppstått eit midlertidig problem på serveren, eller så har ikkje du tilstrekkelege rettigheiter for å trekke invitasjonen.", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Fekk ikkje til å ta attende invitasjonen. Det kan ha oppstått ein mellombels feil på tenaren, eller så har ikkje du tilstrekkelege rettar for å ta attende invitasjonen.", "Revoke invite": "Trekk invitasjon", "Invited by %(sender)s": "Invitert av %(sender)s", "Mark all as read": "Merk alle som lesne", "Error updating main address": "Feil under oppdatering av hovedadresse", - "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Det skjedde ein feil under oppdatering av hovedadressa for rommet. Det kan hende at dette er midlertidig, eller at det ikkje er tillate på serveren.", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Det skjedde ein feil under oppdatering av hovudadressa for rommet. Det kan hende at dette er ein mellombels feil, eller at det ikkje er tillate på tenaren.", "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Feil under oppdatering av sekundæradresse. Det kan hende at dette er midlertidig, eller at det ikkje er tillate på serveren.", "Error creating alias": "Feil under oppretting av alias", "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "Det skjedde ein feil under oppretting av dette aliaset. Det kan hende at dette er midlertidig, eller at det ikkje er tillate på serveren.", @@ -1212,12 +1212,12 @@ "Main address": "Hovudadresse", "Local address": "Lokal adresse", "Published Addresses": "Publisert adresse", - "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.": "Publiserte adresser kan bli brukt av alle uansett server for å bli med i rommet. For å publisere ei adresse, må den vere sett som ei lokal adresse fyrst.", + "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.": "Publiserte adresser kan bli brukt av alle uansett tenar for å bli med i rommet. For å publisera ei adresse, må den vere sett som ei lokal adresse fyrst.", "Other published addresses:": "Andre publiserte adresser:", "No other published addresses yet, add one below": "Ingen publiserte adresser til no, legg til ei under", - "New published address (e.g. #alias:server)": "Ny publisert adresse (t.d. #alias:server)", + "New published address (e.g. #alias:server)": "Ny publisert adresse (t.d. #alias:tenar)", "Local Addresses": "Lokale adresser", - "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Sett adresse for dette rommet, slik at brukarar kan finne rommet på din heimeserver (%(localDomain)s)", + "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Sett adresse for dette rommet, slik at brukarar kan finne rommet på din heimetenar (%(localDomain)s)", "Error updating flair": "Oppdatering av etikett gjekk gale", "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Feil under oppdatering av etikett for dette rommet. Dette kan vere deaktivert på server , eller så oppstod det ein feil.", "Room Name": "Romnamn", @@ -1231,7 +1231,7 @@ "Match system theme": "Følg systemtema", "Show shortcuts to recently viewed rooms above the room list": "Vis snarvegar til sist synte rom over romkatalogen", "Show hidden events in timeline": "Vis skjulte hendelsar i historikken", - "This is your list of users/servers you have blocked - don't leave the room!": "Dette er di liste over brukarar/serverar du har blokkert - ikkje forlat rommet!", + "This is your list of users/servers you have blocked - don't leave the room!": "Dette er di liste over brukarar/tenarar du har blokkert - ikkje forlat rommet!", "Upload": "Last opp", "Show less": "Vis mindre", "Show more": "Vis meir", @@ -1270,7 +1270,7 @@ "Select the roles required to change various parts of the room": "Juster roller som er påkrevd for å endre ulike deler av rommet", "Once enabled, encryption cannot be disabled.": "Etter aktivering, kan ikkje kryptering bli deaktivert.", "Your display name": "Ditt visningsnamn", - "Can't find this server or its room list": "Klarde ikkje å finne serveren eller romkatalogen til den", + "Can't find this server or its room list": "Klarde ikkje å finna tenaren eller romkatalogen til den", "Upload completed": "Opplasting fullført", "Cancelled signature upload": "Kansellerte opplasting av signatur", "Unabled to upload": "Klarte ikkje å laste opp", @@ -1290,7 +1290,7 @@ "Enable Emoji suggestions while typing": "Aktiver Emoji-forslag under skriving", "Show a placeholder for removed messages": "Vis ein plassholdar for sletta meldingar", "Show avatar changes": "Vis avatar-endringar", - "Show read receipts sent by other users": "Vis lest-rapportar sendt av andre brukarar", + "Show read receipts sent by other users": "Vis lese-rapportar sendt av andre brukarar", "Enable big emoji in chat": "Aktiver store emolji-ar i samtalen", "Send typing notifications": "Kringkast \"skriv...\"-status til andre", "Show typing notifications": "Vis \"skriv...\"-status frå andre", @@ -1321,7 +1321,7 @@ "Jump to start/end of the composer": "Hopp til start/slutt av teksteditoren", "Navigate composer history": "Naviger historikk for teksteditor", "Use Single Sign On to continue": "Bruk Single-sign-on for å fortsette", - "Confirm adding this email address by using Single Sign On to prove your identity.": "Stadfest at du legger til denne e-postadressa, ved å bruke Single-sign-on for å bevise identiteten din.", + "Confirm adding this email address by using Single Sign On to prove your identity.": "Stadfest at du legger til denne e-postadressa, ved å bruke Single-sign-on for å stadfeste identiteten din.", "Single Sign On": "Single-sign-on", "Review Sessions": "Sjå gjennom økter", "Capitalization doesn't help very much": "Store bokstavar hjelp dessverre lite", @@ -1330,7 +1330,7 @@ "Keep secret storage passphrase in memory for this session": "Hald passfrase for hemmeleg lager i systemminnet for denne økta", "Cross-signing and secret storage are enabled.": "Krysssignering og hemmeleg lager er aktivert.", "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Kontoen din har ein kryss-signert identitet det hemmelege lageret, økta di stolar ikkje på denne enno.", - "Cross-signing and secret storage are not yet set up.": "Krysssignering og hemmeleg lager er endå ikkje konfiguert.", + "Cross-signing and secret storage are not yet set up.": "Krysssignering og hemmeleg lager er endå ikkje sett opp.", "Reset cross-signing and secret storage": "Tilbakestill krysssignering og hemmeleg lager", "Bootstrap cross-signing and secret storage": "Førebur krysssignering og hemmeleg lager", "in secret storage": "i hemmeleg lager", @@ -1338,10 +1338,10 @@ "Secret Storage key format:": "Nøkkel-format for hemmeleg lager:", "Keyboard Shortcuts": "Tastatursnarvegar", "Ignored users": "Ignorerte brukarar", - "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Legg til brukarar og serverar du vil ignorera her. Bruk stjerne/wildcard (*) for at markere eitkvart teikn. Til dømes, @bot* vil ignorere alle brukarar med namn 'bot' på uansett server.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Legg til brukarar og tenarar du vil ignorera her. Bruk stjerne/wildcard (*) for at markere eitkvart teikn. Til dømes, @bot* vil ignorere alle brukarar med namn 'bot' på uansett tenar.", "Server or user ID to ignore": "Server eller brukar-ID for å ignorere", "If this isn't what you want, please use a different tool to ignore users.": "Om det ikkje var dette du ville, bruk eit anna verktøy til å ignorera brukarar.", - "Enter the name of a new server you want to explore.": "Skriv inn namn på ny server du ynskjer å utforske.", + "Enter the name of a new server you want to explore.": "Skriv inn namn på ny tenar du ynskjer å utforske.", "Matrix rooms": "Matrix-rom", "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.": "Om du har meir info rundt korleis problemet oppstod, som kva du prøvde å gjere på det tidspunktet, brukar-IDar m.m ,inkluder gjerne den informasjonen her.", "Topic (optional)": "Emne (valfritt)", @@ -1364,7 +1364,7 @@ "Explore": "Utforsk", "%(creator)s created and configured the room.": "%(creator)s oppretta og konfiguerte dette rommet.", "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot klarde ikkje å hente protokolllister frå heimeserveren. Det kan hende at serveren er for gammal til å støtte tredjeparts-nettverk", - "The homeserver may be unavailable or overloaded.": "Heimeserveren kan vere overlasta eller utilgjengeleg.", + "The homeserver may be unavailable or overloaded.": "Heimetenaren kan vere overlasta eller utilgjengeleg.", "Preview": "Førehandsvis", "View": "Vis", "Find a room…": "Finn eit rom…", @@ -1382,5 +1382,32 @@ "Unable to set up secret storage": "Oppsett av hemmeleg lager feila", "This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Denne økta har oppdaga at gjenopprettingspassfrasen og nøkkelen for sikre meldingar vart fjerna.", "Toggle microphone mute": "Slå av/på demping av mikrofon", - "Confirm": "Stadfest" + "Confirm": "Stadfest", + "Confirm adding email": "Stadfest at du ynskjer å legga til e-postadressa", + "Click the button below to confirm adding this email address.": "Trykk på knappen under for å stadfesta at du ynskjer å legga til denne e-postadressa.", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Stadfest dette telefonnummeret for å bruka Single-sign-on for å bevisa din identitet.", + "Confirm adding phone number": "Stadfest dette telefonnummeret", + "Click the button below to confirm adding this phone number.": "Trykk på knappen nedanfor for å legge til dette telefonnummeret.", + "Room name or address": "Romnamn eller adresse", + "%(name)s is requesting verification": "%(name)s spør etter verifikasjon", + "Use your account or create a new one to continue.": "Bruk kontoen din eller opprett ein ny for å halda fram.", + "Sends a message as html, without interpreting it as markdown": "Sender melding som HTML, utan å tolka den som markdown", + "Failed to set topic": "Fekk ikkje til å setta emne", + "Joins room with given address": "Legg saman rom med spesifisert adresse", + "Unrecognised room address:": "Rom-adressa vart ikkje kjend att:", + "Command failed": "Kommandoen feila", + "Could not find user in room": "Klarde ikkje å finna brukaren i rommet", + "Please supply a widget URL or embed code": "Oppgje ein widget-URL eller innebygd kode", + "Displays information about a user": "Viser informasjon om ein brukar", + "Send a bug report with logs": "Send ein feilrapport med loggar", + "Opens chat with the given user": "Opna ein samtale med den spesifiserte brukaren", + "Sends a message to the given user": "Send ein melding til den spesifiserte brukaren", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s endra romnamnet frå %(oldRoomName)s til %(newRoomName)s.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s la til dei alternative adressene %(addresses)s for dette rommet.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s la til ei alternativ adresse %(addresses)s for dette rommet.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s tok vekk dei alternative adressene %(addresses)s for dette rommet.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s tok vekk den alternative adressa %(addresses)s for dette rommet.", + "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s endre den alternative adressa for dette rommet.", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s endra hovud- og alternativ-adressene for dette rommet.", + "%(senderName)s changed the addresses for this room.": "%(senderName)s endre adressene for dette rommet." } From 760333a0ae954d37822e18bf534be0155a1c011b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Jun 2020 13:08:25 -0600 Subject: [PATCH 062/181] Move function to a private function --- .../algorithms/list-ordering/Algorithm.ts | 123 +++++++++--------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/src/stores/room-list/algorithms/list-ordering/Algorithm.ts b/src/stores/room-list/algorithms/list-ordering/Algorithm.ts index e8058a2964..c3ac8c150f 100644 --- a/src/stores/room-list/algorithms/list-ordering/Algorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/Algorithm.ts @@ -65,66 +65,8 @@ export abstract class Algorithm extends EventEmitter { } public set stickyRoom(val: Room) { - // We wrap this in a closure because we can't use async setters. - // We need async so we can wait for handleRoomUpdate() to do its thing, otherwise - // we risk duplicating rooms. - (async () => { - // It's possible to have no selected room. In that case, clear the sticky room - if (!val) { - if (this._stickyRoom) { - // Lie to the algorithm and re-add the room to the algorithm - await this.handleRoomUpdate(this._stickyRoom.room, RoomUpdateCause.NewRoom); - } - this._stickyRoom = null; - return; - } - - // When we do have a room though, we expect to be able to find it - const tag = this.roomIdsToTags[val.roomId][0]; - if (!tag) throw new Error(`${val.roomId} does not belong to a tag and cannot be sticky`); - let position = this.cachedRooms[tag].indexOf(val); - if (position < 0) throw new Error(`${val.roomId} does not appear to be known and cannot be sticky`); - - // 🐉 Here be dragons. - // Before we can go through with lying to the underlying algorithm about a room - // we need to ensure that when we do we're ready for the innevitable sticky room - // update we'll receive. To prepare for that, we first remove the sticky room and - // recalculate the state ourselves so that when the underlying algorithm calls for - // the same thing it no-ops. After we're done calling the algorithm, we'll issue - // a new update for ourselves. - const lastStickyRoom = this._stickyRoom; - console.log(`Last sticky room:`, lastStickyRoom); - this._stickyRoom = null; - this.recalculateStickyRoom(); - - // When we do have the room, re-add the old room (if needed) to the algorithm - // and remove the sticky room from the algorithm. This is so the underlying - // algorithm doesn't try and confuse itself with the sticky room concept. - if (lastStickyRoom) { - // Lie to the algorithm and re-add the room to the algorithm - await this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom); - } - // Lie to the algorithm and remove the room from it's field of view - await this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); - - // Now that we're done lying to the algorithm, we need to update our position - // marker only if the user is moving further down the same list. If they're switching - // lists, or moving upwards, the position marker will splice in just fine but if - // they went downwards in the same list we'll be off by 1 due to the shifting rooms. - if (lastStickyRoom && lastStickyRoom.tag === tag && lastStickyRoom.position <= position) { - position++; - } - - this._stickyRoom = { - room: val, - position: position, - tag: tag, - }; - this.recalculateStickyRoom(); - - // Finally, trigger an update - this.emit(LIST_UPDATED_EVENT); - })(); + // setters can't be async, so we call a private function to do the work + this.updateStickyRoom(val); } protected get hasFilters(): boolean { @@ -175,6 +117,67 @@ export abstract class Algorithm extends EventEmitter { } } + private async updateStickyRoom(val: Room) { + // Note throughout: We need async so we can wait for handleRoomUpdate() to do its thing, + // otherwise we risk duplicating rooms. + + // It's possible to have no selected room. In that case, clear the sticky room + if (!val) { + if (this._stickyRoom) { + // Lie to the algorithm and re-add the room to the algorithm + await this.handleRoomUpdate(this._stickyRoom.room, RoomUpdateCause.NewRoom); + } + this._stickyRoom = null; + return; + } + + // When we do have a room though, we expect to be able to find it + const tag = this.roomIdsToTags[val.roomId][0]; + if (!tag) throw new Error(`${val.roomId} does not belong to a tag and cannot be sticky`); + let position = this.cachedRooms[tag].indexOf(val); + if (position < 0) throw new Error(`${val.roomId} does not appear to be known and cannot be sticky`); + + // 🐉 Here be dragons. + // Before we can go through with lying to the underlying algorithm about a room + // we need to ensure that when we do we're ready for the innevitable sticky room + // update we'll receive. To prepare for that, we first remove the sticky room and + // recalculate the state ourselves so that when the underlying algorithm calls for + // the same thing it no-ops. After we're done calling the algorithm, we'll issue + // a new update for ourselves. + const lastStickyRoom = this._stickyRoom; + console.log(`Last sticky room:`, lastStickyRoom); + this._stickyRoom = null; + this.recalculateStickyRoom(); + + // When we do have the room, re-add the old room (if needed) to the algorithm + // and remove the sticky room from the algorithm. This is so the underlying + // algorithm doesn't try and confuse itself with the sticky room concept. + if (lastStickyRoom) { + // Lie to the algorithm and re-add the room to the algorithm + await this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom); + } + // Lie to the algorithm and remove the room from it's field of view + await this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); + + // Now that we're done lying to the algorithm, we need to update our position + // marker only if the user is moving further down the same list. If they're switching + // lists, or moving upwards, the position marker will splice in just fine but if + // they went downwards in the same list we'll be off by 1 due to the shifting rooms. + if (lastStickyRoom && lastStickyRoom.tag === tag && lastStickyRoom.position <= position) { + position++; + } + + this._stickyRoom = { + room: val, + position: position, + tag: tag, + }; + this.recalculateStickyRoom(); + + // Finally, trigger an update + this.emit(LIST_UPDATED_EVENT); + } + protected recalculateFilteredRooms() { if (!this.hasFilters) { return; From 8e0247afe5ed1970e653aab6ec7083fb0e09d159 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Jun 2020 22:06:41 -0600 Subject: [PATCH 063/181] Add most of the UI for the new room list's menu button Incomplete implementation: buttons don't work, some text is missing, etc --- res/css/_components.scss | 1 + res/css/structures/_LeftPanel2.scss | 5 + res/css/structures/_UserMenuButton.scss | 157 +++++++++++++ res/img/feather-customised/archive.svg | 1 + .../feather-customised/more-horizontal.svg | 1 + res/img/feather-customised/sun.svg | 1 + res/themes/light/css/_light.scss | 1 + src/components/structures/LeftPanel2.tsx | 5 +- src/components/structures/LoggedInView.tsx | 4 +- .../structures/TopLeftMenuButton.js | 3 +- src/components/structures/UserMenuButton.tsx | 211 ++++++++++++++++++ src/dispatcher/actions.ts | 5 + src/i18n/strings/en_EN.json | 7 + 13 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 res/css/structures/_UserMenuButton.scss create mode 100644 res/img/feather-customised/archive.svg create mode 100644 res/img/feather-customised/more-horizontal.svg create mode 100644 res/img/feather-customised/sun.svg create mode 100644 src/components/structures/UserMenuButton.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 62bec5ad62..de4c1c677c 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -29,6 +29,7 @@ @import "./structures/_ToastContainer.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; +@import "./structures/_UserMenuButton.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_CompleteSecurity.scss"; @import "./structures/auth/_Login.scss"; diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 822a5ac399..d335df305f 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -73,6 +73,11 @@ $roomListMinimizedWidth: 50px; font-weight: 600; font-size: $font-15px; line-height: $font-20px; + flex: 1; + } + + .mx_LeftPanel2_headerButtons { + // No special styles: the rest of the layout happens to make it work. } .mx_LeftPanel2_breadcrumbsContainer { diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss new file mode 100644 index 0000000000..f7097311c9 --- /dev/null +++ b/res/css/structures/_UserMenuButton.scss @@ -0,0 +1,157 @@ +/* +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_UserMenuButton { + // No special styles on the button itself +} + +.mx_UserMenuButton_contextMenu { + width: 231px; + + // Put 20px of padding around the whole menu. We do this instead of a + // simple `padding: 20px` rule so the horizontal rules added by the + // optionLists is rendered correctly (full width). + >* { + padding-left: 20px; + padding-right: 20px; + + &:first-child { + padding-top: 20px; + } + + &:last-child { + padding-bottom: 20px; + } + } + + .mx_UserMenuButton_contextMenu_header { + // Create a flexbox to organize the header a bit easier + display: flex; + align-items: center; + + .mx_UserMenuButton_contextMenu_name { + // Create another flexbox of columns to handle large user IDs + display: flex; + flex-direction: column; + + // fit the container + flex: 1; + width: calc(100% - 40px); // 40px = 32px theme button + 8px margin to theme button + + * { + // Automatically grow all subelements to fit the container + flex: 1; + width: 100%; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_UserMenuButton_contextMenu_displayName { + font-weight: bold; + font-size: $font-15px; + line-height: $font-20px; + } + + .mx_UserMenuButton_contextMenu_userId { + font-size: $font-15px; + line-height: $font-24px; + } + } + + .mx_UserMenuButton_contextMenu_themeButton { + min-width: 32px; + max-width: 32px; + width: 32px; + height: 32px; + margin-left: 8px; + border-radius: 32px; + background-color: $theme-button-bg-color; + cursor: pointer; + + // to make alignment easier, create flexbox for the image + display: flex; + align-items: center; + justify-content: center; + } + } + + .mx_UserMenuButton_contextMenu_optionList { + margin-top: 20px; + + // This is a bit of a hack when we could just use a simple border-top property, + // however we have a (kinda) good reason for doing it this way: we need opacity. + // To get the right color, we need an opacity modifier which means we have to work + // around the problem. PostCSS doesn't support the opacity() function, and if we + // use something like postcss-functions we quickly run into an issue where the + // function we would define gets passed a CSS variable for custom themes, which + // can't be converted easily even when considering https://stackoverflow.com/a/41265350/7037379 + // + // Therefore, we just hack in a line and border the thing ourselves + &::before { + border-top: 1px solid $primary-fg-color; + opacity: 0.1; + content: ''; + + // Counteract the padding problems (width: 100% ignores the 40px padding, + // unless we position it absolutely then it does the right thing). + width: 100%; + position: absolute; + left: 0; + } + + ul { + list-style: none; + margin: 0; + padding: 0; + + li { + margin: 0; + padding: 20px 0 0; + + a { + text-decoration: none; + color: $primary-fg-color; + font-size: $font-15px; + line-height: $font-24px; + + // Create a flexbox to more easily define the list items + display: flex; + align-items: center; + + img { // icons + width: 16px; + min-width: 16px; + max-width: 16px; + } + + span { // labels + padding-left: 14px; + width: 100%; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } + } +} diff --git a/res/img/feather-customised/archive.svg b/res/img/feather-customised/archive.svg new file mode 100644 index 0000000000..428882c87b --- /dev/null +++ b/res/img/feather-customised/archive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/more-horizontal.svg b/res/img/feather-customised/more-horizontal.svg new file mode 100644 index 0000000000..dc6a85564e --- /dev/null +++ b/res/img/feather-customised/more-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/sun.svg b/res/img/feather-customised/sun.svg new file mode 100644 index 0000000000..7f51b94d1c --- /dev/null +++ b/res/img/feather-customised/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 5aeb125774..a50c34cf03 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -177,6 +177,7 @@ $header-divider-color: #91A1C0; $roomtile2-preview-color: #9e9e9e; $roomtile2-badge-color: #61708b; $roomtile2-selected-bg-color: #FFF; +$theme-button-bg-color: #e3e8f0; $roomtile-name-color: #61708b; $roomtile-badge-fg-color: $accent-fg-color; diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index c66c0a6799..2fd8612ff5 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -27,6 +27,7 @@ import { Action } from "../../dispatcher/actions"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import BaseAvatar from '../views/avatars/BaseAvatar'; import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; +import UserMenuButton from "./UserMenuButton"; /******************************************************************* * CAUTION * @@ -49,7 +50,6 @@ export default class LeftPanel2 extends React.Component { // TODO: Properly support TagPanel // TODO: Properly support searching/filtering // TODO: Properly support breadcrumbs - // TODO: Properly support TopLeftMenu (User Settings) // TODO: a11y // TODO: actually make this useful in general (match design proposals) // TODO: Fadable support (is this still needed?) @@ -115,6 +115,9 @@ export default class LeftPanel2 extends React.Component { /> {displayName} + + +
diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 0504e3a76a..19edf505e0 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -452,9 +452,7 @@ class LoggedInView extends React.PureComponent { // composer, so CTRL+` it is if (ctrlCmdOnly) { - dis.dispatch({ - action: 'toggle_top_left_menu', - }); + dis.fire(Action.ToggleUserMenu); handled = true; } break; diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js index 234dc661f9..71e7e61406 100644 --- a/src/components/structures/TopLeftMenuButton.js +++ b/src/components/structures/TopLeftMenuButton.js @@ -24,6 +24,7 @@ import * as Avatar from '../../Avatar'; import { _t } from '../../languageHandler'; import dis from "../../dispatcher/dispatcher"; import {ContextMenu, ContextMenuButton} from "./ContextMenu"; +import {Action} from "../../dispatcher/actions"; const AVATAR_SIZE = 28; @@ -75,7 +76,7 @@ export default class TopLeftMenuButton extends React.Component { onAction = (payload) => { // For accessibility - if (payload.action === "toggle_top_left_menu") { + if (payload.action === Action.ToggleUserMenu) { if (this._buttonRef) this._buttonRef.click(); } }; diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx new file mode 100644 index 0000000000..0968e0bdb6 --- /dev/null +++ b/src/components/structures/UserMenuButton.tsx @@ -0,0 +1,211 @@ +/* +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 * as React from "react"; +import {User} from "matrix-js-sdk/src/models/user"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; +import defaultDispatcher from "../../dispatcher/dispatcher"; +import { ActionPayload } from "../../dispatcher/payloads"; +import { Action } from "../../dispatcher/actions"; +import { createRef } from "react"; +import { _t } from "../../languageHandler"; +import {ContextMenu, ContextMenuButton} from "./ContextMenu"; + +interface IProps { +} + +interface IState { + user: User; + menuDisplayed: boolean; +} + +export default class UserMenuButton extends React.Component { + private dispatcherRef: string; + private buttonRef: React.RefObject = createRef(); + + constructor(props: IProps) { + super(props); + + this.state = { + menuDisplayed: false, + user: MatrixClientPeg.get().getUser(MatrixClientPeg.get().getUserId()), + }; + } + + private get displayName(): string { + if (MatrixClientPeg.get().isGuest()) { + return _t("Guest"); + } else if (this.state.user) { + return this.state.user.displayName; + } else { + return MatrixClientPeg.get().getUserId(); + } + } + + public componentDidMount() { + this.dispatcherRef = defaultDispatcher.register(this.onAction); + } + + private onAction = (ev: ActionPayload) => { + if (ev.action !== Action.ToggleUserMenu) return; // not interested + + // For accessibility + if (this.buttonRef.current) this.buttonRef.current.click(); + }; + + private onOpenMenuClick = (ev: InputEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + this.setState({menuDisplayed: true}); + }; + + private onCloseMenu = () => { + this.setState({menuDisplayed: false}); + }; + + private onSwitchThemeClick = () => { + console.log("TODO: Switch theme"); + }; + + private onSettingsOpen = (ev: React.MouseEvent, tabRef: string) => { + ev.preventDefault(); + ev.stopPropagation(); + + console.log("TODO: Open settings", tabRef); + }; + + private onShowArchived = (ev: React.MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + + console.log("TODO: Show archived rooms"); + }; + + private onProvideFeedback = (ev: React.MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + + console.log("TODO: Show feedback"); + }; + + private onSignOutClick = (ev: React.MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + + console.log("TODO: Sign out"); + }; + + public render() { + let contextMenu; + if (this.state.menuDisplayed) { + const elementRect = this.buttonRef.current.getBoundingClientRect(); + contextMenu = ( + + + + ); + } + + return ( + + + ... + + {contextMenu} + + ) + } +} diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 71493d6e44..60ef61a6e9 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -58,4 +58,9 @@ export enum Action { * Focuses the user's cursor to the composer. No additional payload information required. */ FocusComposer = "focus_composer", + + /** + * Opens the user menu (previously known as the top left menu). No additional payload information required. + */ + ToggleUserMenu = "toggle_user_menu", } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cf6dc2431a..8575b3a258 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2042,6 +2042,13 @@ "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s", "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", + "Switch to dark mode": "Switch to dark mode", + "Switch theme": "Switch theme", + "Security & privacy": "Security & privacy", + "All settings": "All settings", + "Archived rooms": "Archived rooms", + "Feedback": "Feedback", + "Account settings": "Account settings", "Could not load user profile": "Could not load user profile", "Verify this login": "Verify this login", "Session verified": "Session verified", From 8b6b117fbfe95f3f896972d30ca9fa08a410807a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Jun 2020 22:15:54 -0600 Subject: [PATCH 064/181] Appease the linter --- res/css/structures/_UserMenuButton.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index f7097311c9..aca5f4253a 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -24,7 +24,7 @@ limitations under the License. // Put 20px of padding around the whole menu. We do this instead of a // simple `padding: 20px` rule so the horizontal rules added by the // optionLists is rendered correctly (full width). - >* { + > * { padding-left: 20px; padding-right: 20px; From f05a1e532b5652d0fe2236b6a2794b889c43149d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sun, 7 Jun 2020 22:17:02 -0600 Subject: [PATCH 065/181] Point buttons at the right functions --- src/components/structures/UserMenuButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index 0968e0bdb6..abf4689aa1 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -171,7 +171,7 @@ export default class UserMenuButton extends React.Component {
  • - + {_t("Feedback")} @@ -181,7 +181,7 @@ export default class UserMenuButton extends React.Component {
    • - this.onSettingsOpen(e, 'notifications')}> + {_t("Sign out")} From 1315ed503b165c521a794218b5cf4eb6ad237098 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 5 Jun 2020 16:59:24 +0100 Subject: [PATCH 066/181] Upgrade deps --- yarn.lock | 1497 +++++++++++++++++++++++++++-------------------------- 1 file changed, 752 insertions(+), 745 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7e444a0e0f..333c5ccf20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,9 +3,9 @@ "@babel/cli@^7.7.5": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c" - integrity sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.1.tgz#b6e5cd43a17b8f639442ab027976408ebe6d79a0" + integrity sha512-cVB+dXeGhMOqViIaZs3A9OUAe4pKw4SBNdMw6yHJMYR7s4TB+Cei7ThquV/84O19PdIFWuwe03vxxES0BHUm5g== dependencies: commander "^4.0.1" convert-source-map "^1.1.0" @@ -18,35 +18,35 @@ optionalDependencies: chokidar "^2.1.8" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.5.5": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" + integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.1" -"@babel/compat-data@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.6.tgz#3f604c40e420131affe6f2c8052e9a275ae2049b" - integrity sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g== +"@babel/compat-data@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" + integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== dependencies: - browserslist "^4.11.1" + browserslist "^4.12.0" invariant "^2.2.4" semver "^5.5.0" "@babel/core@>=7.2.2", "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" - integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" + integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.6" - "@babel/parser" "^7.9.6" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.2" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helpers" "^7.10.1" + "@babel/parser" "^7.10.2" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" @@ -56,338 +56,346 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.8.3", "@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/generator@^7.10.1", "@babel/generator@^7.10.2", "@babel/generator@^7.4.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" + integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== dependencies: - "@babel/types" "^7.9.6" + "@babel/types" "^7.10.2" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" - integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== +"@babel/helper-annotate-as-pure@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" + integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" + integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-explode-assignable-expression" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-builder-react-jsx-experimental@^7.9.0": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.5.tgz#0b4b3e04e6123f03b404ca4dfd6528fe6bb92fe3" - integrity sha512-HAagjAC93tk748jcXpZ7oYRZH485RCq/+yEv9SIWezHRPv9moZArTnkUNciUNzvwHUABmiWKlcxJvMcu59UwTg== +"@babel/helper-builder-react-jsx-experimental@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz#9a7d58ad184d3ac3bafb1a452cec2bad7e4a0bc8" + integrity sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-module-imports" "^7.8.3" - "@babel/types" "^7.9.5" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-module-imports" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-builder-react-jsx@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32" - integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw== +"@babel/helper-builder-react-jsx@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz#a327f0cf983af5554701b1215de54a019f09b532" + integrity sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/types" "^7.9.0" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-compilation-targets@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz#1e05b7ccc9d38d2f8b40b458b380a04dcfadd38a" - integrity sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw== +"@babel/helper-compilation-targets@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" + integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== dependencies: - "@babel/compat-data" "^7.9.6" - browserslist "^4.11.1" + "@babel/compat-data" "^7.10.1" + browserslist "^4.12.0" invariant "^2.2.4" levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.8.3", "@babel/helper-create-class-features-plugin@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz#965c8b0a9f051801fd9d3b372ca0ccf200a90897" - integrity sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow== +"@babel/helper-create-class-features-plugin@^7.10.1": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67" + integrity sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ== dependencies: - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.9.6" - "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" -"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" - integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== +"@babel/helper-create-regexp-features-plugin@^7.10.1", "@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" + integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-regex" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-regex" "^7.10.1" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" - integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== +"@babel/helper-define-map@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" + integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== +"@babel/helper-explode-assignable-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" + integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== +"@babel/helper-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" + integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== +"@babel/helper-get-function-arity@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" + integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-hoist-variables@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" - integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== +"@babel/helper-hoist-variables@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" + integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-member-expression-to-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" + integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-module-imports@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" + integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== +"@babel/helper-module-transforms@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" + integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== +"@babel/helper-optimise-call-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" + integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" - integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" + integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== -"@babel/helper-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" - integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== +"@babel/helper-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.1.tgz#021cf1a7ba99822f993222a001cc3fec83255b96" + integrity sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g== dependencies: lodash "^4.17.13" -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== +"@babel/helper-remap-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" + integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-wrap-function" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6", "@babel/helper-replace-supers@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444" - integrity sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA== +"@babel/helper-replace-supers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" + integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== +"@babel/helper-simple-access@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" + integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== +"@babel/helper-split-export-declaration@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" + integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== +"@babel/helper-validator-identifier@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" + integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== +"@babel/helper-wrap-function@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" + integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helpers@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" - integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== +"@babel/helpers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" + integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== +"@babel/highlight@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" + integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.1" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.10.2", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" + integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== +"@babel/plugin-proposal-async-generator-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" + integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz#5e06654af5cd04b608915aada9b2a6788004464e" - integrity sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA== +"@babel/plugin-proposal-class-properties@^7.10.1", "@babel/plugin-proposal-class-properties@^7.7.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01" + integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-proposal-decorators@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz#2156860ab65c5abf068c3f67042184041066543e" - integrity sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.1.tgz#9373c2d8db45345c6e30452ad77b469758e5c8f7" + integrity sha512-xBfteh352MTke2U1NpclzMDmAmCdQ2fBZjhZQQfGTjXw6qcRYMkt528sA1U8o0ThDCSeuETXIj5bOGdxN+5gkw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-decorators" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-decorators" "^7.10.1" -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== +"@babel/plugin-proposal-dynamic-import@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" + integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-dynamic-import" "^7.8.0" "@babel/plugin-proposal-export-default-from@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.8.3.tgz#4cb7c2fdeaed490b60d9bfd3dc8a20f81f9c2e7c" - integrity sha512-PYtv2S2OdCdp7GSPDg5ndGZFm9DmWFvuLoS5nBxZCgOBggluLnhTScspJxng96alHQzPyrrHxvC9/w4bFuspeA== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.10.1.tgz#59ea2a4f09dbb0358c73dab27def3d21a27bd370" + integrity sha512-Xfc1CfHapIkwZ/+AI+j4Ha3g233ol0EEdy6SmnUuQQiZX78SfQXHd8tmntc5zqCkwPnIHoiZa6l6p0OAvxYXHw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-export-default-from" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-export-default-from" "^7.10.1" -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== +"@babel/plugin-proposal-json-strings@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" + integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" + integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.7.4", "@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== +"@babel/plugin-proposal-numeric-separator@^7.10.1", "@babel/plugin-proposal-numeric-separator@^7.7.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" + integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" -"@babel/plugin-proposal-object-rest-spread@^7.7.4", "@babel/plugin-proposal-object-rest-spread@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz#7a093586fcb18b08266eb1a7177da671ac575b63" - integrity sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A== +"@babel/plugin-proposal-object-rest-spread@^7.10.1", "@babel/plugin-proposal-object-rest-spread@^7.7.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" + integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.9.5" + "@babel/plugin-transform-parameters" "^7.10.1" -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== +"@babel/plugin-proposal-optional-catch-binding@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" + integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== +"@babel/plugin-proposal-optional-chaining@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" + integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" - integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== +"@babel/plugin-proposal-private-methods@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598" + integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.8" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" + integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-async-generators@^7.8.0": version "7.8.4" @@ -396,12 +404,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-decorators@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz#8d2c15a9f1af624b0025f961682a9d53d3001bda" - integrity sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ== +"@babel/plugin-syntax-class-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" + integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-syntax-decorators@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.1.tgz#16b869c4beafc9a442565147bda7ce0967bd4f13" + integrity sha512-a9OAbQhKOwSle1Vr0NJu/ISg1sPfdEkfRKWpgPuzhnWWzForou2gIeUIIwjAMHRekhhpJ7eulZlYs0H14Cbi+g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" @@ -410,19 +425,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.8.3.tgz#f1e55ce850091442af4ba9c2550106035b29d678" - integrity sha512-a1qnnsr73KLNIQcQlcQ4ZHxqqfBKM6iNQZW2OMTyxNbA2WC7SHWHtGVpFzWtQAuS2pspkWVzdEBXXx8Ik0Za4w== +"@babel/plugin-syntax-export-default-from@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.10.1.tgz#634f58f36b5d6320d80f75441fdc61e1c05c33b0" + integrity sha512-+rcL4S/mN1Ss4zhSCbxzv1Wsf12eauvgTjWi0krXEeX1zd6qSxYnJoniE5Ssr5w2WPt61oUCJyXIFQIqO/29zw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-flow@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz#f2c883bd61a6316f2c89380ae5122f923ba4527f" - integrity sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg== +"@babel/plugin-syntax-flow@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.10.1.tgz#cd4bbca62fb402babacb174f64f8734310d742f0" + integrity sha512-b3pWVncLBYoPP60UOTc7NMlbtsHQ6ITim78KQejNHK6WJ2mzV5kCcg4mIWpasAfJEgwVTibwo2e+FU7UEIKQUg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-json-strings@^7.8.0": version "7.8.3" @@ -431,12 +446,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94" - integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== +"@babel/plugin-syntax-jsx@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz#0ae371134a42b91d5418feb3c8c8d43e1565d2da" + integrity sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": version "7.8.3" @@ -445,12 +460,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== +"@babel/plugin-syntax-numeric-separator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" + integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0": version "7.8.3" @@ -473,184 +488,184 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== +"@babel/plugin-syntax-top-level-await@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" + integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-typescript@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz#c1f659dda97711a569cef75275f7e15dcaa6cabc" - integrity sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg== +"@babel/plugin-syntax-typescript@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.1.tgz#5e82bc27bb4202b93b949b029e699db536733810" + integrity sha512-X/d8glkrAtra7CaQGMiGs/OGa6XgUzqPcBXCIGFCpCqnfGlT0Wfbzo/B89xHhnInTaItPK8LALblVXcUOEh95Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== +"@babel/plugin-transform-arrow-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" + integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== +"@babel/plugin-transform-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" + integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== +"@babel/plugin-transform-block-scoped-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" + integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== +"@babel/plugin-transform-block-scoping@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" + integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" lodash "^4.17.13" -"@babel/plugin-transform-classes@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" - integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== +"@babel/plugin-transform-classes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" + integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-define-map" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== +"@babel/plugin-transform-computed-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" + integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-destructuring@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" - integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== +"@babel/plugin-transform-destructuring@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" + integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" - integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== +"@babel/plugin-transform-dotall-regex@^7.10.1", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" + integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== +"@babel/plugin-transform-duplicate-keys@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" + integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== +"@babel/plugin-transform-exponentiation-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" + integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-transform-flow-comments@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-comments/-/plugin-transform-flow-comments-7.8.3.tgz#0a7e6c49224ac24271e4da25774da0600605ef2c" - integrity sha512-SEmbGPsaUig0x3QkB/Nai3Snk1sRxODBN2EGjdQqgBb5TMcbEejV2TtMGi2XiLmw9Cy/BvJX7CAnfJMctuyglg== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-comments/-/plugin-transform-flow-comments-7.10.1.tgz#1befb98f8d37245b70770a1f83c67057e41bd4a9" + integrity sha512-A1yDAD/3pU+NyCHRvm+GQE2xiyRV6mGDyhMdTKPqgs5aRcc2MR4wmnHJJIB91f8NwMNl8dxmN6nmj/7FCr6Jgw== dependencies: - "@babel/generator" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-flow" "^7.8.3" + "@babel/generator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-flow" "^7.10.1" -"@babel/plugin-transform-flow-strip-types@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz#8a3538aa40434e000b8f44a3c5c9ac7229bd2392" - integrity sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg== +"@babel/plugin-transform-flow-strip-types@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.1.tgz#59eafbff9ae85ec8932d4c16c068654be814ec5e" + integrity sha512-i4o0YwiJBIsIx7/liVCZ3Q2WkWr1/Yu39PksBOnh/khW2SwIFsGa5Ze+MSon5KbDfrEHP9NeyefAgvUSXzaEkw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-flow" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-flow" "^7.10.1" -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== +"@babel/plugin-transform-for-of@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" + integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== +"@babel/plugin-transform-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" + integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== +"@babel/plugin-transform-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" + integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== +"@babel/plugin-transform-member-expression-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" + integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-modules-amd@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz#8539ec42c153d12ea3836e0e3ac30d5aae7b258e" - integrity sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw== +"@babel/plugin-transform-modules-amd@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" + integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" - integrity sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ== +"@babel/plugin-transform-modules-commonjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" + integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz#207f1461c78a231d5337a92140e52422510d81a4" - integrity sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg== +"@babel/plugin-transform-modules-systemjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" + integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== dependencies: - "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-hoist-variables" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== +"@babel/plugin-transform-modules-umd@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" + integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": version "7.8.3" @@ -659,229 +674,248 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.8.3" -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== +"@babel/plugin-transform-new-target@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" + integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== +"@babel/plugin-transform-object-super@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" + integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" -"@babel/plugin-transform-parameters@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" - integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== +"@babel/plugin-transform-parameters@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" + integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== +"@babel/plugin-transform-property-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" + integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-react-display-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz#70ded987c91609f78353dd76d2fb2a0bb991e8e5" - integrity sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A== +"@babel/plugin-transform-react-display-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz#e6a33f6d48dfb213dda5e007d0c7ff82b6a3d8ef" + integrity sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-react-jsx-development@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz#3c2a130727caf00c2a293f0aed24520825dbf754" - integrity sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw== +"@babel/plugin-transform-react-jsx-development@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz#1ac6300d8b28ef381ee48e6fec430cc38047b7f3" + integrity sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg== dependencies: - "@babel/helper-builder-react-jsx-experimental" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" + "@babel/helper-builder-react-jsx-experimental" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-jsx" "^7.10.1" -"@babel/plugin-transform-react-jsx-self@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz#f4f26a325820205239bb915bad8e06fcadabb49b" - integrity sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ== +"@babel/plugin-transform-react-jsx-self@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz#22143e14388d72eb88649606bb9e46f421bc3821" + integrity sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-jsx" "^7.10.1" -"@babel/plugin-transform-react-jsx-source@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz#89ef93025240dd5d17d3122294a093e5e0183de0" - integrity sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== +"@babel/plugin-transform-react-jsx-source@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz#30db3d4ee3cdebbb26a82a9703673714777a4273" + integrity sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-jsx" "^7.10.1" -"@babel/plugin-transform-react-jsx@^7.9.4": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.4.tgz#86f576c8540bd06d0e95e0b61ea76d55f6cbd03f" - integrity sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw== +"@babel/plugin-transform-react-jsx@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz#91f544248ba131486decb5d9806da6a6e19a2896" + integrity sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw== dependencies: - "@babel/helper-builder-react-jsx" "^7.9.0" - "@babel/helper-builder-react-jsx-experimental" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" + "@babel/helper-builder-react-jsx" "^7.10.1" + "@babel/helper-builder-react-jsx-experimental" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-jsx" "^7.10.1" -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== +"@babel/plugin-transform-react-pure-annotations@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70" + integrity sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-transform-regenerator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" + integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== +"@babel/plugin-transform-reserved-words@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" + integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-transform-runtime@^7.8.3": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" - integrity sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.1.tgz#fd1887f749637fb2ed86dc278e79eb41df37f4b1" + integrity sha512-4w2tcglDVEwXJ5qxsY++DgWQdNJcCCsPxfT34wCUwIf2E7dI7pMpH8JczkMBbgBTNzBX62SZlNJ9H+De6Zebaw== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== +"@babel/plugin-transform-shorthand-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" + integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== +"@babel/plugin-transform-spread@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" + integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== +"@babel/plugin-transform-sticky-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" + integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-regex" "^7.10.1" -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== +"@babel/plugin-transform-template-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" + integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== +"@babel/plugin-transform-typeof-symbol@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" + integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-typescript@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.6.tgz#2248971416a506fc78278fc0c0ea3179224af1e9" - integrity sha512-8OvsRdvpt3Iesf2qsAn+YdlwAJD7zJ+vhFZmDCa4b8dTp7MmHtKk5FF2mCsGxjZwuwsy/yIIay/nLmxST1ctVQ== +"@babel/plugin-transform-typescript@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.1.tgz#2c54daea231f602468686d9faa76f182a94507a6" + integrity sha512-v+QWKlmCnsaimLeqq9vyCsVRMViZG1k2SZTlcZvB+TqyH570Zsij8nvVUZzOASCRiQFUxkLrn9Wg/kH0zgy5OQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.9.6" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-typescript" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-typescript" "^7.10.1" -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== +"@babel/plugin-transform-unicode-escapes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940" + integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-transform-unicode-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" + integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/preset-env@^7.7.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.6.tgz#df063b276c6455ec6fcfc6e53aacc38da9b0aea6" - integrity sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ== + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb" + integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA== dependencies: - "@babel/compat-data" "^7.9.6" - "@babel/helper-compilation-targets" "^7.9.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.6" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/compat-data" "^7.10.1" + "@babel/helper-compilation-targets" "^7.10.2" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-proposal-async-generator-functions" "^7.10.1" + "@babel/plugin-proposal-class-properties" "^7.10.1" + "@babel/plugin-proposal-dynamic-import" "^7.10.1" + "@babel/plugin-proposal-json-strings" "^7.10.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" + "@babel/plugin-proposal-numeric-separator" "^7.10.1" + "@babel/plugin-proposal-object-rest-spread" "^7.10.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.1" + "@babel/plugin-proposal-optional-chaining" "^7.10.1" + "@babel/plugin-proposal-private-methods" "^7.10.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.1" "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.1" "@babel/plugin-syntax-dynamic-import" "^7.8.0" "@babel/plugin-syntax-json-strings" "^7.8.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.5" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.9.5" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.6" - "@babel/plugin-transform-modules-commonjs" "^7.9.6" - "@babel/plugin-transform-modules-systemjs" "^7.9.6" - "@babel/plugin-transform-modules-umd" "^7.9.0" + "@babel/plugin-syntax-top-level-await" "^7.10.1" + "@babel/plugin-transform-arrow-functions" "^7.10.1" + "@babel/plugin-transform-async-to-generator" "^7.10.1" + "@babel/plugin-transform-block-scoped-functions" "^7.10.1" + "@babel/plugin-transform-block-scoping" "^7.10.1" + "@babel/plugin-transform-classes" "^7.10.1" + "@babel/plugin-transform-computed-properties" "^7.10.1" + "@babel/plugin-transform-destructuring" "^7.10.1" + "@babel/plugin-transform-dotall-regex" "^7.10.1" + "@babel/plugin-transform-duplicate-keys" "^7.10.1" + "@babel/plugin-transform-exponentiation-operator" "^7.10.1" + "@babel/plugin-transform-for-of" "^7.10.1" + "@babel/plugin-transform-function-name" "^7.10.1" + "@babel/plugin-transform-literals" "^7.10.1" + "@babel/plugin-transform-member-expression-literals" "^7.10.1" + "@babel/plugin-transform-modules-amd" "^7.10.1" + "@babel/plugin-transform-modules-commonjs" "^7.10.1" + "@babel/plugin-transform-modules-systemjs" "^7.10.1" + "@babel/plugin-transform-modules-umd" "^7.10.1" "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.9.5" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.10.1" + "@babel/plugin-transform-object-super" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-property-literals" "^7.10.1" + "@babel/plugin-transform-regenerator" "^7.10.1" + "@babel/plugin-transform-reserved-words" "^7.10.1" + "@babel/plugin-transform-shorthand-properties" "^7.10.1" + "@babel/plugin-transform-spread" "^7.10.1" + "@babel/plugin-transform-sticky-regex" "^7.10.1" + "@babel/plugin-transform-template-literals" "^7.10.1" + "@babel/plugin-transform-typeof-symbol" "^7.10.1" + "@babel/plugin-transform-unicode-escapes" "^7.10.1" + "@babel/plugin-transform-unicode-regex" "^7.10.1" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.6" - browserslist "^4.11.1" + "@babel/types" "^7.10.2" + browserslist "^4.12.0" core-js-compat "^3.6.2" invariant "^2.2.2" levenary "^1.1.1" semver "^5.5.0" "@babel/preset-flow@^7.7.4": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.9.0.tgz#fee847c3e090b0b2d9227c1949e4da1d1379280d" - integrity sha512-88uSmlshIrlmPkNkEcx3UpSZ6b8n0UGBq0/0ZMZCF/uxAW0XIAUuDHBhIOAh0pvweafH4RxOwi/H3rWhtqOYPA== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.10.1.tgz#29498ec23baf9aa6dae50c568ceba09d71692b82" + integrity sha512-FuQsibb5PaX07fF1XUO5gjjxdEZbcJv8+ugPDaeFEsBIvUTib8hCtEJow/c2F0jq9ZUjpHCQ8IQKNHRvKE1kJQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-flow-strip-types" "^7.9.0" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-flow-strip-types" "^7.10.1" "@babel/preset-modules@^0.1.3": version "0.1.3" @@ -895,29 +929,30 @@ esutils "^2.0.2" "@babel/preset-react@^7.7.4": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.9.4.tgz#c6c97693ac65b6b9c0b4f25b948a8f665463014d" - integrity sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.1.tgz#e2ab8ae9a363ec307b936589f07ed753192de041" + integrity sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-react-display-name" "^7.8.3" - "@babel/plugin-transform-react-jsx" "^7.9.4" - "@babel/plugin-transform-react-jsx-development" "^7.9.0" - "@babel/plugin-transform-react-jsx-self" "^7.9.0" - "@babel/plugin-transform-react-jsx-source" "^7.9.0" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-react-display-name" "^7.10.1" + "@babel/plugin-transform-react-jsx" "^7.10.1" + "@babel/plugin-transform-react-jsx-development" "^7.10.1" + "@babel/plugin-transform-react-jsx-self" "^7.10.1" + "@babel/plugin-transform-react-jsx-source" "^7.10.1" + "@babel/plugin-transform-react-pure-annotations" "^7.10.1" "@babel/preset-typescript@^7.7.4": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz#87705a72b1f0d59df21c179f7c3d2ef4b16ce192" - integrity sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.1.tgz#a8d8d9035f55b7d99a2461a0bdc506582914d07e" + integrity sha512-m6GV3y1ShiqxnyQj10600ZVOFrSSAa8HQ3qIUk2r+gcGtHTIRw0dJnFLt1WNXpKjtVw7yw1DAPU/6ma2ZvgJuA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-typescript" "^7.9.0" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-transform-typescript" "^7.10.1" "@babel/register@^7.7.4": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.9.0.tgz#02464ede57548bddbb5e9f705d263b7c3f43d48b" - integrity sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.10.1.tgz#b6567c5cb5049f44bbf8c35d6ff68ca3c43238ed" + integrity sha512-sl96+kB3IA2B9EzpwwBmYadOT14vw3KaXOknGDbJaZCOj52GDA4Tivudq9doCJcB+bEIKCEARZYwRgBBsCGXyg== dependencies: find-cache-dir "^2.0.0" lodash "^4.17.13" @@ -926,50 +961,50 @@ source-map-support "^0.5.16" "@babel/runtime-corejs3@^7.8.3": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.9.6.tgz#67aded13fffbbc2cb93247388cf84d77a4be9a71" - integrity sha512-6toWAfaALQjt3KMZQc6fABqZwUDDuWzz+cAfPhqyEnzxvdWOAkjwPNxgF8xlmo7OWLsSjaKjsskpKHRLaMArOA== + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.2.tgz#3511797ddf9a3d6f3ce46b99cc835184817eaa4e" + integrity sha512-+a2M/u7r15o3dV1NEizr9bRi+KUVnrs/qYxF0Z06DAPx/4VCWaz1WA7EcbE+uqGgt39lp5akWGmHsTseIkHkHg== dependencies: core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" - integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" + integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@^7.10.1", "@babel/template@^7.4.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" + integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/code-frame" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.1", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" + integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" - integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== +"@babel/types@^7.0.0", "@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" + integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== dependencies: - "@babel/helper-validator-identifier" "^7.9.5" + "@babel/helper-validator-identifier" "^7.10.1" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -1143,9 +1178,9 @@ integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== "@peculiar/asn1-schema@^2.0.1", "@peculiar/asn1-schema@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.3.tgz#c6c097e842ebb8a07d198b68cd49f2cf9f3571b5" - integrity sha512-STqC+Tfx2dTiIGRmokjsKOeqsfhoV6WaBwFr7BVicSfHLAVSPrZXiugyD8AELrjQdJ9INWpL3N7YSJyU5a1ZwA== + version "2.0.5" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.5.tgz#ba6c5a107eec16a23804d0176a3595837b53c0e9" + integrity sha512-VIKJjsgMkv+yyWx3C+D4xo6/NeCg0XFBgNlavtkxELijV+aKAq53du5KkOJbeZtm1nn9CinQKny2PqL8zCfpeA== dependencies: "@types/asn1js" "^0.0.1" asn1js "^2.0.26" @@ -1185,9 +1220,9 @@ "@types/pvutils" "*" "@types/babel__core@^7.1.0": - version "7.1.7" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" - integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== + version "7.1.8" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" + integrity sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -1211,9 +1246,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.11.tgz#1ae3010e8bf8851d324878b42acec71986486d18" - integrity sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q== + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.12.tgz#22f49a028e69465390f87bb103ebd61bd086b8f5" + integrity sha512-t4CoEokHTfcyfb4hUaF9oOHu9RmmNWnm1CP0YmMqOOfClKascOmvlEM736vlqeScuGvBDsHkf8R2INd4DWreQA== dependencies: "@babel/types" "^7.3.0" @@ -1222,11 +1257,6 @@ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ== -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== - "@types/fbemitter@*": version "2.0.32" resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c" @@ -1241,11 +1271,10 @@ "@types/react" "*" "@types/glob@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + version "7.1.2" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.2.tgz#06ca26521353a545d94a0adc74f38a59d232c987" + integrity sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA== dependencies: - "@types/events" "*" "@types/minimatch" "*" "@types/node" "*" @@ -1275,9 +1304,9 @@ integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== "@types/lodash@^4.14.152": - version "4.14.152" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.152.tgz#7e7679250adce14e749304cdb570969f77ec997c" - integrity sha512-Vwf9YF2x1GE3WNeUMjT5bTHa2DqgUo87ocdgTScupY2JclZ5Nn7W2RLM/N0+oreexUk8uaVugR81NnTY/jNNXg== + version "4.14.155" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.155.tgz#e2b4514f46a261fd11542e47519c20ebce7bc23a" + integrity sha512-vEcX7S7aPhsBCivxMwAANQburHBtfN9RdyXFk84IJmu2Z4Hkg1tOFgaslRiEqqvoLtbCBi6ika1EMspE+NZ9Lg== "@types/minimatch@*": version "3.0.3" @@ -1290,14 +1319,14 @@ integrity sha512-jhMOZSS0UGYTS9pqvt6q3wtT3uvOSve5piTEmTMx3zzTuBLvSIMxSIBIc3d5lajVD5h4xc41AMZD2M5orN3PxA== "@types/node@*": - version "14.0.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" - integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA== + version "14.0.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.11.tgz#61d4886e2424da73b7b25547f59fdcb534c165a3" + integrity sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg== "@types/node@^12.12.41": - version "12.12.42" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.42.tgz#d0d1149336bd07540dd1ea576692829d575dec34" - integrity sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg== + version "12.12.44" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.44.tgz#0d400a1453adcb359b133acceae4dd8bb0e0a159" + integrity sha512-jM6QVv0Sm5d3nW+nUD5jSzPcO6oPqboitSNcwgBay9hifVq/Rauq1PYnROnsmuw45JMBiTnsPAno0bKu2e2xrg== "@types/prop-types@*": version "15.7.3" @@ -2158,7 +2187,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.11.1, browserslist@^4.12.0, browserslist@^4.8.5: +browserslist@^4.12.0, browserslist@^4.8.5: version "4.12.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== @@ -2322,9 +2351,9 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001061: - version "1.0.30001065" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001065.tgz#e8d7fef61cdfd8a7107493ad6bf551a4eb59c68f" - integrity sha512-DDxCLgJ266YnAHQv0jS1wdOaihRFF52Zgmlag39sQJVy2H46oROpJp4hITstqhdB8qnHSrKNoAEkQA9L/oYF9A== + version "1.0.30001079" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001079.tgz#ed3e5225cd9a6850984fdd88bf24ce45d69b9c22" + integrity sha512-2KaYheg0iOY+CMmDuAB3DHehrXhhb4OZU4KBVGDr/YKyYAcpudaiUQ9PJ9rxrPlKEoJ3ATasQ5AN48MqpwS43Q== capture-exit@^2.0.0: version "2.0.0" @@ -3128,9 +3157,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.3.413: - version "1.3.451" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.451.tgz#0c075af3e2f06d706670bde0279432802ca8c83f" - integrity sha512-2fvco0F2bBIgqzO8GRP0Jt/91pdrf9KfZ5FsmkYkjERmIJG585cFeFZV4+CO6oTmU3HmCTgfcZuEa7kW8VUh3A== + version "1.3.464" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5" + integrity sha512-Oo+0+CN9d2z6FToQW6Hwvi9ez09Y/usKwr0tsDsyg43a871zVJCi1nR0v03djLbRNcaCKjtrnVf2XJhTxEpPCg== elliptic@^6.0.0, elliptic@^6.5.2: version "6.5.2" @@ -3213,9 +3242,9 @@ entities@^1.1.1, "entities@~ 1.1.1", entities@~1.1.1: integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" - integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" + integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== enzyme-adapter-react-16@^1.15.1: version "1.15.2" @@ -3361,9 +3390,9 @@ escape-string-regexp@^1.0.5: integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escodegen@^1.9.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== + version "1.14.2" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.2.tgz#14ab71bf5026c2aa08173afba22c6f3173284a84" + integrity sha512-InuOIiKk8wwuOFg6x9BQXbzjrQhtyXh46K9bqVTPzSo2FnyMBaYGBMC6PhQy7yxxil9vIedFBweQBMK74/7o8A== dependencies: esprima "^4.0.1" estraverse "^4.2.0" @@ -3392,9 +3421,9 @@ eslint-plugin-flowtype@^2.30.0: lodash "^4.17.10" eslint-plugin-jest@^23.0.4: - version "23.13.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.13.1.tgz#b2ce83f76064ad8ba1f1f26f322b86a86e44148e" - integrity sha512-TRLJH6M6EDvGocD98a7yVThrAOCK9WJfo9phuUb0MJptcrOYZeCKzC9aOzZCD93sxXCsiJVZywaTHdI/mAi0FQ== + version "23.13.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.13.2.tgz#7b7993b4e09be708c696b02555083ddefd7e4cc7" + integrity sha512-qZit+moTXTyZFNDqSIR88/L3rdBlTU7CuW6XmyErD2FfHEkdoLgThkRbiQjzgYnX6rfgLx3Ci4eJmF4Ui5v1Cw== dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" @@ -3434,9 +3463,9 @@ eslint-scope@^4.0.3: estraverse "^4.1.1" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" + integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -3456,9 +3485,9 @@ eslint-utils@^2.0.0: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + version "1.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa" + integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ== eslint@^5.12.0: version "5.16.0" @@ -3704,9 +3733,9 @@ extsprintf@^1.2.0: integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^2.2.6: version "2.2.7" @@ -4428,9 +4457,9 @@ ignore@^4.0.3, ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.0.4: - version "5.1.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.6.tgz#643194ad4bf2712f37852e386b6998eff0db2106" - integrity sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA== + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== immutable@^3.7.4: version "3.8.2" @@ -4658,9 +4687,9 @@ is-buffer@^2.0.0: integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== is-callable@^1.0.4, is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== is-ci@^2.0.0: version "2.0.0" @@ -4850,11 +4879,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: isobject "^3.0.1" is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" + integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw== dependencies: - has "^1.0.3" + has-symbols "^1.0.1" is-regexp@^1.0.0: version "1.0.0" @@ -5629,21 +5658,11 @@ lodash-es@^4.2.1: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= -lodash.escaperegexp@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" - integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= - lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" @@ -5654,21 +5673,6 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= - -lodash.mergewith@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" - integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -5900,9 +5904,9 @@ merge-stream@^2.0.0: integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" - integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" @@ -6167,9 +6171,9 @@ node-notifier@^5.4.2: which "^1.3.0" node-releases@^1.1.53: - version "1.1.56" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.56.tgz#bc054a417d316e3adac90eafb7e1932802f28705" - integrity sha512-EVo605FhWLygH8a64TjgpjyHYOihkxECwX1bHHr8tETJKWEiWS2YJjPbvsX2jFjnjTNEgBCmk9mLjKG1Mf11cw== + version "1.1.58" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" + integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.5.0" @@ -6582,9 +6586,9 @@ path-type@^3.0.0: pify "^3.0.0" pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" - integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -6770,9 +6774,9 @@ postcss-value-parser@^4.1.0: integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.30, postcss@^7.0.6, postcss@^7.0.7: - version "7.0.30" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.30.tgz#cc9378beffe46a02cbc4506a0477d05fcea9a8e2" - integrity sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ== + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -7004,7 +7008,7 @@ randexp@0.4.6: discontinuous-range "1.0.0" ret "~0.1.10" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -7256,9 +7260,9 @@ regenerate-unicode-properties@^8.2.0: regenerate "^1.4.0" regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f" + integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A== regenerator-runtime@^0.11.0: version "0.11.1" @@ -7638,17 +7642,13 @@ sane@^4.0.3: walker "~1.0.5" sanitize-html@^1.18.4: - version "1.24.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.24.0.tgz#9cd42f236512bfcf6259424e958551148c165a7f" - integrity sha512-TAIFx39V/y06jDd4YUz7ntCdMUXN5Z28pSG7sTP2BCLXwHA9+ermacDpQs35Evo4p6YSgmaPdSbGiX4Fgptuuw== + version "1.26.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.26.0.tgz#ab38d671526b9b7c08aa7af7f9ad5a73fcc1bbe4" + integrity sha512-xriDBT2FbfN0ZKCcX6H6svkh1bZpO2e5ny05RQGZY6vFOMAU13La2L5YYf3XpcjXSksCYXzPj7YPvyGp5wbaUA== dependencies: chalk "^2.4.1" htmlparser2 "^4.1.0" - lodash.clonedeep "^4.5.0" - lodash.escaperegexp "^4.1.2" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.mergewith "^4.6.2" + lodash "^4.17.15" postcss "^7.0.27" srcset "^2.0.1" xtend "^4.0.1" @@ -7700,6 +7700,13 @@ serialize-javascript@^2.1.2: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serialize-javascript@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" + integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== + dependencies: + randombytes "^2.1.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -8318,15 +8325,15 @@ tapable@^1.0.0, tapable@^1.1.3: integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== terser-webpack-plugin@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" - integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== + version "1.4.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz#2c63544347324baafa9a56baaddf1634c8abfc2f" + integrity sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^2.1.2" + serialize-javascript "^3.1.0" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" @@ -8558,9 +8565,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.7.3: - version "3.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" - integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== + version "3.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" + integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== ua-parser-js@^0.7.18: version "0.7.21" @@ -8741,9 +8748,9 @@ url@^0.11.0: querystring "0.2.0" use-callback-ref@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.3.tgz#9f939dfb5740807bbf9dd79cdd4e99d27e827756" - integrity sha512-DPBPh1i2adCZoIArRlTuKRy7yue7QogtEnfv0AKrWsY+GA+4EKe37zhRDouNnyWMoNQFYZZRF+2dLHsWE4YvJA== + version "1.2.4" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.4.tgz#d86d1577bfd0b955b6e04aaf5971025f406bea3c" + integrity sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ== use-sidecar@^1.0.1: version "1.0.2" @@ -8964,9 +8971,9 @@ webpack@^4.20.2: webpack-sources "^1.4.1" what-input@^5.2.6: - version "5.2.9" - resolved "https://registry.yarnpkg.com/what-input/-/what-input-5.2.9.tgz#e484628c00404d2ad5d747ac2f0fb22008f7757a" - integrity sha512-/tuM/4ngvfYB1QF3yekJsmFpIhkiHEDKCl/VYDikyHZVxoFn3U/lNgiNt7aqC8RerkoPUMxc9ihKsW9KwAx2Rg== + version "5.2.10" + resolved "https://registry.yarnpkg.com/what-input/-/what-input-5.2.10.tgz#f79f5b65cf95d75e55e6d580bb0a6b98174cad4e" + integrity sha512-7AQoIMGq7uU8esmKniOtZG3A+pzlwgeyFpkS3f/yzRbxknSL68tvn5gjE6bZ4OMFxCPjpaBd2udUTqlZ0HwrXQ== whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.5" From dbc2f9cbfc5b1ee6aea5bdb61970d2832f1021a5 Mon Sep 17 00:00:00 2001 From: Michael Albert Date: Mon, 8 Jun 2020 08:27:12 +0000 Subject: [PATCH 067/181] Translated using Weblate (German) Currently translated at 99.7% (2253 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index f55d94f8fc..d51ef94814 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1569,7 +1569,7 @@ "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Ihr Passwort wurde erfolgreich geändert. Sie erhalten keine Push-Benachrichtigungen zu anderen Sitzungen, bis Sie sich wieder bei diesen anmelden", "Sessions": "Sitzungen", "Notification sound": "Benachrichtigungston", - "Set a new custom sound": "Setze einen neuen benutzerdefinierten Sound", + "Set a new custom sound": "Setze einen neuen benutzerdefinierten Ton", "Browse": "Durchsuche", "Direct Messages": "Direktnachrichten", "You can use /help to list available commands. Did you mean to send this as a message?": "Sie können /help benutzen, um verfügbare Befehle aufzulisten. Wollten Sie dies als Nachricht senden?", @@ -1584,8 +1584,8 @@ "To help us prevent this in future, please send us logs.": "Um uns zu helfen, dies in Zukunft zu vermeiden, senden Sie uns bitte Logs.", "We recommend you go through the verification process for each session to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Wir empfehlen Ihnen, den Verifizierungsprozess für jede Sitzung zu durchlaufen, um zu bestätigen, dass sie ihrem rechtmäßigen Eigentümer gehören, aber Sie können die Nachricht auch ohne Verifizierung erneut senden, wenn Sie dies bevorzugen.", "Notification settings": "Benachrichtigungseinstellungen", - "Help": "Hilfe", - "Filter": "Filter", + "Help": "Hilf uns", + "Filter": "Filtern", "Filter rooms…": "Räume filtern…", "You have %(count)s unread notifications in a prior version of this room.|one": "Sie haben %(count)s ungelesene Benachrichtigungen in einer früheren Version dieses Raumes.", "Go Back": "Gehe zurück", @@ -2389,5 +2389,37 @@ "Create room": "Raum erstellen", "Jump to oldest unread message": "Zur ältesten ungelesenen Nachricht springen", "Upload a file": "Eine Datei hochladen", - "Dismiss read marker and jump to bottom": "Entferne Lesemarker und springe nach unten" + "Dismiss read marker and jump to bottom": "Entferne Lesemarker und springe nach unten", + "Room name or address": "Raumname oder -adresse", + "Joins room with given address": "Tritt dem Raum unter der angegebenen Adresse bei", + "Unrecognised room address:": "Unbekannte Raumadresse:", + "Help us improve Riot": "Hilf uns Riot zu verbessern", + "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Hilf uns Riot zu verbessern, indem du anonyme Nutzungsdaten schickst. Dies wird ein Cookie verwenden.", + "I want to help": "Ich möchte helfen", + "Your homeserver has exceeded its user limit.": "Dein Heimserver hat das Benutzerlimit erreicht.", + "Your homeserver has exceeded one of its resource limits.": "Dein Heimserver hat eine seiner Ressourcengrenzen erreicht.", + "Contact your server admin.": "Kontaktiere deinen Heimserver Administrator.", + "Ok": "Ok", + "Set password": "Setze Passwort", + "To return to your account in future you need to set a password": "Um dein Konto zukünftig wieder verwenden zu können, setze ein Passwort", + "Restart": "Neustarten", + "Upgrade your Riot": "Aktualisiere dein Riot", + "A new version of Riot is available!": "Eine neue Version von Riot ist verfügbar!", + "New version available. Update now.": "Neue Version verfügbar. Jetzt aktualisieren.", + "Please verify the room ID or address and try again.": "Bitte überprüfe die Raum-ID oder -adresse und versuche es erneut.", + "To link to this room, please add an address.": "Um den Raum zu verlinken, füge bitte eine Adresse hinzu.", + "Emoji picker": "Emoji Auswahl", + "Show %(n)s more": "%(n)s weitere anzeigen", + "Error creating address": "Fehler beim Anlegen der Adresse", + "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.": "Es gab einen Fehler beim Anlegen der Adresse. Entweder erlaubt es der Server nicht oder es gab ein temporäres Problem.", + "You don't have permission to delete the address.": "Du hast nicht die Berechtigung die Adresse zu löschen.", + "Error removing address": "Fehler beim Löschen der Adresse", + "Categories": "Kategorien", + "Room address": "Raumadresse", + "Please provide a room address": "Bitte gib eine Raumadresse an", + "This address is available to use": "Diese Adresse ist verfügbar", + "This address is already in use": "Diese Adresse wird bereits verwendet", + "Address (optional)": "Adresse (optional)", + "delete the address.": "lösche die Adresse.", + "Use a different passphrase?": "Eine andere Passphrase verwenden?" } From 4b920c88dc9cbd5e99d627ce1c5a7969fd9db95d Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Mon, 8 Jun 2020 08:55:02 +0000 Subject: [PATCH 068/181] Translated using Weblate (Slovak) Currently translated at 68.7% (1552 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 0fe60e4321..0756f8d3e8 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -1565,7 +1565,7 @@ "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s odstránil/a alternatívne adresy %(addresses)s pre túto miestnosť.", "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s odstránil/a alternatívnu adresu %(addresses)s pre túto miestnosť.", "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s zmenil/a alternatívne adresy pre túto miestnosť.", - "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s zmenil/a hlavnú a alternatívnu/e adresy pre túto miestnosť.", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s zmenil/a hlavnú a alternatívne adresy pre túto miestnosť.", "%(senderName)s changed the addresses for this room.": "%(senderName)s zmenil/a adresy pre túto miestnosť.", "You signed in to a new session without verifying it:": "Prihlásili ste sa do novej relácie bez jej overenia:", "Verify your other session using one of the options below.": "Overte svoje ostatné relácie pomocou jednej z nižšie uvedených možností.", @@ -1708,7 +1708,7 @@ "Manage": "Spravovať", "Securely cache encrypted messages locally for them to appear in search results.": "Bezpečne cachovať šifrované správy lokálne, aby sa mohli zobraziť vo vyhľadávaní.", "Enable": "Povoliť", - "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.": "Riotu chýbajú niektoré komponenty potrebné na bezpečné cachovanie šifrovaných správ lokálne. Pokiaľ chcete experimentovať s touto funkciou, spravte si svoj vlastný Riot Desktop s pridanými vyhľadávacími komponentami.", + "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.": "Riotu chýbajú niektoré komponenty potrebné na bezpečné cachovanie šifrovaných správ lokálne. Pokiaľ chcete experimentovať s touto funkciou, spravte si svoj vlastný Riot Desktop s pridanými vyhľadávacími komponentami.", "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.": "Riotu nemôže bezpečne cachovať šifrované správy lokálne keď beží v prehliadači. Použite Riot Desktop, aby sa šifrované správy zobrazili vo vyhľadávaní.", "This session is backing up your keys. ": "Táto relácia zálohuje vaše kľúče. ", "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Táto relácia nezálohuje vaše kľúče, ale už máte jednu existujúcu zálohu z ktorej sa môžete obnoviť a postupne pridávať.", @@ -1727,5 +1727,17 @@ "Enable desktop notifications for this session": "Povoliť desktopové notifikácie pre túto reláciu", "Enable audible notifications for this session": "Povoliť zvukové notifikácie pre túto reláciu", "Size must be a number": "Veľkosť musí byť číslo", - "Custom font size can only be between %(min)s pt and %(max)s pt": "Vlastná veľkosť písma môže byť len v rozmedzí %(min)s pt až %(max)s pt" + "Custom font size can only be between %(min)s pt and %(max)s pt": "Vlastná veľkosť písma môže byť len v rozmedzí %(min)s pt až %(max)s pt", + "Help us improve Riot": "Pomôžte nám zlepšovať Riot", + "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Posielať anonymné dáta o používaní, ktoré nám pomôžu zlepšiť Riot. Toto bude vyžadovať sušienku.", + "I want to help": "Chcem pomôcť", + "Your homeserver has exceeded its user limit.": "Na vašom domovskom serveri bol prekročený limit počtu používateľov.", + "Your homeserver has exceeded one of its resource limits.": "Na vašom domovskom serveri bol prekročený jeden z limitov systémových zdrojov.", + "Contact your server admin.": "Kontaktujte svojho administrátora serveru.", + "Ok": "Ok", + "Set password": "Nastaviť heslo", + "To return to your account in future you need to set a password": "Aby ste sa k účtu mohli vrátiť aj neskôr, je potrebné nastaviť heslo", + "Restart": "Reštartovať", + "Upgrade your Riot": "Upgradujte svoj Riot", + "A new version of Riot is available!": "Nová verzia Riotu je dostupná!" } From 6fe5196a193ca3b1c814cf27463a86db0563186b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2020 11:43:50 +0100 Subject: [PATCH 069/181] send state of lowBandwidth in rageshakes Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/rageshake/submit-rageshake.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index 9f9d7898cb..64a1ea0c33 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -145,6 +145,10 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp if (enabledLabs.length) { body.append('enabled_labs', enabledLabs.join(', ')); } + // if low bandwidth mode is enabled, say so over rageshake, it causes many issues + if (SettingsStore.getValue("lowBandwidth")) { + body.append("lowBandwidth", "enabled"); + } // add storage persistence/quota information if (navigator.storage && navigator.storage.persisted) { From 458bea20bebdbff420643306b0f1217f38964e3a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 14:31:53 +0100 Subject: [PATCH 070/181] Load correct fonstSize default value --- src/settings/watchers/FontWatcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index 05c707a57b..5527284cd0 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -32,7 +32,7 @@ export class FontWatcher implements IWatcher { } public start() { - this.setRootFontSize(SettingsStore.getValue("baseFontSize") + FontWatcher.SIZE_DIFF); + this.setRootFontSize(SettingsStore.getValue("baseFontSize")); this.dispatcherRef = dis.register(this.onAction); } From a7bc722b3fb15fc87abcfb41d0b5be1d1ba5ccc4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 14:45:12 +0100 Subject: [PATCH 071/181] Use px where images use px width --- src/components/views/rooms/ReadReceiptMarker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 2fe577377d..1c490f019e 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; -import {toRem} from "../../../utils/units"; +import {toPx} from "../../../utils/units"; let bounce = false; try { @@ -149,7 +149,7 @@ export default createReactClass({ // start at the old height and in the old h pos startStyles.push({ top: startTopOffset+"px", - left: toRem(oldInfo.left) }); + left: toPx(oldInfo.left) }); const reorderTransitionOpts = { duration: 100, @@ -182,7 +182,7 @@ export default createReactClass({ } const style = { - left: toRem(this.props.leftOffset), + left: toPx(this.props.leftOffset), top: '0px', visibility: this.props.hidden ? 'hidden' : 'visible', }; From de18af35ff58b8ffd4f323a47130ff4f05b331ed Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 08:20:15 -0600 Subject: [PATCH 072/181] Support minimum to open user settings to a particular tab Tabs now have IDs, and we use those IDs to open things. This doesn't do any conversion to typescript, and doesn't add the same feature to the room settings out of concern for the size of diff. --- src/components/structures/MatrixChat.tsx | 8 +++-- src/components/structures/TabbedView.tsx | 30 ++++++++----------- src/components/structures/UserMenuButton.tsx | 14 +++++---- .../views/dialogs/RoomSettingsDialog.js | 13 ++++++++ .../views/dialogs/UserSettingsDialog.js | 24 ++++++++++++++- src/dispatcher/actions.ts | 1 + src/dispatcher/payloads/OpenToTabPayload.ts | 27 +++++++++++++++++ 7 files changed, 92 insertions(+), 25 deletions(-) create mode 100644 src/dispatcher/payloads/OpenToTabPayload.ts diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 69f91047b7..e08381d8fa 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -72,6 +72,7 @@ import { hideToast as hideAnalyticsToast } from "../../toasts/AnalyticsToast"; import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; +import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; /** constants for MatrixChat.state.view */ export enum Views { @@ -604,9 +605,12 @@ export default class MatrixChat extends React.PureComponent { this.viewIndexedRoom(payload.roomIndex); break; case Action.ViewUserSettings: { + const tabPayload = payload as OpenToTabPayload; const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, - /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, + {initialTabId: tabPayload.initialTabId}, + /*className=*/null, /*isPriority=*/false, /*isStatic=*/true + ); // View the welcome or home page if we need something to look at this.viewSomethingBehindModal(); diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index c0e0e58db8..704dbf8832 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -27,25 +27,20 @@ import { ReactNode } from "react"; * Represents a tab for the TabbedView. */ export class Tab { - public label: string; - public icon: string; - public body: React.ReactNode; - /** * Creates a new tab. - * @param {string} tabLabel The untranslated tab label. - * @param {string} tabIconClass The class for the tab icon. This should be a simple mask. - * @param {React.ReactNode} tabJsx The JSX for the tab container. + * @param {string} id The tab's ID. + * @param {string} label The untranslated tab label. + * @param {string} icon The class for the tab icon. This should be a simple mask. + * @param {React.ReactNode} body The JSX for the tab container. */ - constructor(tabLabel: string, tabIconClass: string, tabJsx: React.ReactNode) { - this.label = tabLabel; - this.icon = tabIconClass; - this.body = tabJsx; + constructor(public id: string, public label: string, public icon: string, public body: React.ReactNode) { } } interface IProps { tabs: Tab[]; + initialTabId?: string; } interface IState { @@ -53,16 +48,17 @@ interface IState { } export default class TabbedView extends React.Component { - static propTypes = { - // The tabs to show - tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired, - }; - constructor(props: IProps) { super(props); + let activeTabIndex = 0; + if (props.initialTabId) { + const tabIndex = props.tabs.findIndex(t => t.id === props.initialTabId); + if (tabIndex >= 0) activeTabIndex = tabIndex; + } + this.state = { - activeTabIndex: 0, + activeTabIndex, }; } diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index abf4689aa1..827a279d98 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -23,6 +23,8 @@ import { Action } from "../../dispatcher/actions"; import { createRef } from "react"; import { _t } from "../../languageHandler"; import {ContextMenu, ContextMenuButton} from "./ContextMenu"; +import {USER_NOTIFICATIONS_TAB, USER_SECURITY_TAB} from "../views/dialogs/UserSettingsDialog"; +import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; interface IProps { } @@ -80,11 +82,13 @@ export default class UserMenuButton extends React.Component { console.log("TODO: Switch theme"); }; - private onSettingsOpen = (ev: React.MouseEvent, tabRef: string) => { + private onSettingsOpen = (ev: React.MouseEvent, tabId: string) => { ev.preventDefault(); ev.stopPropagation(); - console.log("TODO: Open settings", tabRef); + const payload: OpenToTabPayload = {action: Action.ViewUserSettings, initialTabId: tabId}; + defaultDispatcher.dispatch(payload); + this.setState({menuDisplayed: false}); // also close the menu }; private onShowArchived = (ev: React.MouseEvent) => { @@ -147,19 +151,19 @@ export default class UserMenuButton extends React.Component {
      • - this.onSettingsOpen(e, 'notifications')}> + this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}> {_t("Notification settings")}
      • - this.onSettingsOpen(e, 'security')}> + this.onSettingsOpen(e, USER_SECURITY_TAB)}> {_t("Security & privacy")}
      • - this.onSettingsOpen(e, 'all')}> + this.onSettingsOpen(e, null)}> {_t("All settings")} diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index c2b98cd9f3..7ad1001f75 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -30,6 +30,13 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg"; import dis from "../../../dispatcher/dispatcher"; import SettingsStore from "../../../settings/SettingsStore"; +export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB"; +export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB"; +export const ROOM_ROLES_TAB = "ROOM_ROLES_TAB"; +export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB"; +export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB"; +export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB"; + export default class RoomSettingsDialog extends React.Component { static propTypes = { roomId: PropTypes.string.isRequired, @@ -56,21 +63,25 @@ export default class RoomSettingsDialog extends React.Component { const tabs = []; tabs.push(new Tab( + ROOM_GENERAL_TAB, _td("General"), "mx_RoomSettingsDialog_settingsIcon", , )); tabs.push(new Tab( + ROOM_SECURITY_TAB, _td("Security & Privacy"), "mx_RoomSettingsDialog_securityIcon", , )); tabs.push(new Tab( + ROOM_ROLES_TAB, _td("Roles & Permissions"), "mx_RoomSettingsDialog_rolesIcon", , )); tabs.push(new Tab( + ROOM_NOTIFICATIONS_TAB, _td("Notifications"), "mx_RoomSettingsDialog_notificationsIcon", , @@ -78,6 +89,7 @@ export default class RoomSettingsDialog extends React.Component { if (SettingsStore.isFeatureEnabled("feature_bridge_state")) { tabs.push(new Tab( + ROOM_BRIDGES_TAB, _td("Bridges"), "mx_RoomSettingsDialog_bridgesIcon", , @@ -85,6 +97,7 @@ export default class RoomSettingsDialog extends React.Component { } tabs.push(new Tab( + ROOM_ADVANCED_TAB, _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", , diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 4592d921a9..1f1a8d1523 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -33,9 +33,21 @@ import * as sdk from "../../../index"; import SdkConfig from "../../../SdkConfig"; import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab"; +export const USER_GENERAL_TAB = "USER_GENERAL_TAB"; +export const USER_APPEARANCE_TAB = "USER_APPEARANCE_TAB"; +export const USER_FLAIR_TAB = "USER_FLAIR_TAB"; +export const USER_NOTIFICATIONS_TAB = "USER_NOTIFICATIONS_TAB"; +export const USER_PREFERENCES_TAB = "USER_PREFERENCES_TAB"; +export const USER_VOICE_TAB = "USER_VOICE_TAB"; +export const USER_SECURITY_TAB = "USER_SECURITY_TAB"; +export const USER_LABS_TAB = "USER_LABS_TAB"; +export const USER_MJOLNIR_TAB = "USER_MJOLNIR_TAB"; +export const USER_HELP_TAB = "USER_HELP_TAB"; + export default class UserSettingsDialog extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, + initialTabId: PropTypes.string, }; constructor() { @@ -63,42 +75,50 @@ export default class UserSettingsDialog extends React.Component { const tabs = []; tabs.push(new Tab( + USER_GENERAL_TAB, _td("General"), "mx_UserSettingsDialog_settingsIcon", , )); tabs.push(new Tab( + USER_APPEARANCE_TAB, _td("Appearance"), "mx_UserSettingsDialog_appearanceIcon", , )); tabs.push(new Tab( + USER_FLAIR_TAB, _td("Flair"), "mx_UserSettingsDialog_flairIcon", , )); tabs.push(new Tab( + USER_NOTIFICATIONS_TAB, _td("Notifications"), "mx_UserSettingsDialog_bellIcon", , )); tabs.push(new Tab( + USER_PREFERENCES_TAB, _td("Preferences"), "mx_UserSettingsDialog_preferencesIcon", , )); tabs.push(new Tab( + USER_VOICE_TAB, _td("Voice & Video"), "mx_UserSettingsDialog_voiceIcon", , )); tabs.push(new Tab( + USER_SECURITY_TAB, _td("Security & Privacy"), "mx_UserSettingsDialog_securityIcon", , )); if (SdkConfig.get()['showLabsSettings'] || SettingsStore.getLabsFeatures().length > 0) { tabs.push(new Tab( + USER_LABS_TAB, _td("Labs"), "mx_UserSettingsDialog_labsIcon", , @@ -106,12 +126,14 @@ export default class UserSettingsDialog extends React.Component { } if (this.state.mjolnirEnabled) { tabs.push(new Tab( + USER_MJOLNIR_TAB, _td("Ignored users"), "mx_UserSettingsDialog_mjolnirIcon", , )); } tabs.push(new Tab( + USER_HELP_TAB, _td("Help & About"), "mx_UserSettingsDialog_helpIcon", , @@ -127,7 +149,7 @@ export default class UserSettingsDialog extends React.Component {
        - +
        ); diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 60ef61a6e9..c9b5d9e3ad 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -36,6 +36,7 @@ export enum Action { /** * Open the user settings. No additional payload information required. + * Optionally can include an OpenToTabPayload. */ ViewUserSettings = "view_user_settings", diff --git a/src/dispatcher/payloads/OpenToTabPayload.ts b/src/dispatcher/payloads/OpenToTabPayload.ts new file mode 100644 index 0000000000..2877ee053e --- /dev/null +++ b/src/dispatcher/payloads/OpenToTabPayload.ts @@ -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. +*/ + +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +export interface OpenToTabPayload extends ActionPayload { + action: Action.ViewUserSettings | string, // TODO: Add room settings action + + /** + * The tab ID to open in the settings view to start, if possible. + */ + initialTabId?: string; +} From acf78ae475799d955eac3b03017a0278c29ce600 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 09:04:43 -0600 Subject: [PATCH 073/181] Wire up the remaining dialogs --- src/components/structures/UserMenuButton.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index 827a279d98..35b5cc4d4e 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -25,6 +25,9 @@ import { _t } from "../../languageHandler"; import {ContextMenu, ContextMenuButton} from "./ContextMenu"; import {USER_NOTIFICATIONS_TAB, USER_SECURITY_TAB} from "../views/dialogs/UserSettingsDialog"; import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; +import RedesignFeedbackDialog from "../views/dialogs/RedesignFeedbackDialog"; +import Modal from "../../Modal"; +import LogoutDialog from "../views/dialogs/LogoutDialog"; interface IProps { } @@ -95,6 +98,7 @@ export default class UserMenuButton extends React.Component { ev.preventDefault(); ev.stopPropagation(); + // TODO: Archived room view (deferred) console.log("TODO: Show archived rooms"); }; @@ -102,14 +106,16 @@ export default class UserMenuButton extends React.Component { ev.preventDefault(); ev.stopPropagation(); - console.log("TODO: Show feedback"); + Modal.createTrackedDialog('Report bugs & give feedback', '', RedesignFeedbackDialog); + this.setState({menuDisplayed: false}); // also close the menu }; private onSignOutClick = (ev: React.MouseEvent) => { ev.preventDefault(); ev.stopPropagation(); - console.log("TODO: Sign out"); + Modal.createTrackedDialog('Logout from LeftPanel', '', LogoutDialog); + this.setState({menuDisplayed: false}); // also close the menu }; public render() { From 94ce23aa4b9e835be45df49d4889f9639592b3b4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 09:24:08 -0600 Subject: [PATCH 074/181] Wire up theme changer --- src/components/structures/UserMenuButton.tsx | 31 ++++++++++++++++++-- src/theme.js | 2 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index 35b5cc4d4e..40b53a35fb 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -28,6 +28,8 @@ import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; import RedesignFeedbackDialog from "../views/dialogs/RedesignFeedbackDialog"; import Modal from "../../Modal"; import LogoutDialog from "../views/dialogs/LogoutDialog"; +import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; +import {getCustomTheme} from "../../theme"; interface IProps { } @@ -35,10 +37,12 @@ interface IProps { interface IState { user: User; menuDisplayed: boolean; + isDarkTheme: boolean; } export default class UserMenuButton extends React.Component { private dispatcherRef: string; + private themeWatcherRef: string; private buttonRef: React.RefObject = createRef(); constructor(props: IProps) { @@ -47,6 +51,7 @@ export default class UserMenuButton extends React.Component { this.state = { menuDisplayed: false, user: MatrixClientPeg.get().getUser(MatrixClientPeg.get().getUserId()), + isDarkTheme: this.isUserOnDarkTheme(), }; } @@ -62,8 +67,26 @@ export default class UserMenuButton extends React.Component { public componentDidMount() { this.dispatcherRef = defaultDispatcher.register(this.onAction); + this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged); } + public componentWillUnmount() { + if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef); + if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); + } + + private isUserOnDarkTheme(): boolean { + const theme = SettingsStore.getValue("theme"); + if (theme.startsWith("custom-")) { + return getCustomTheme(theme.substring(0, 7)).is_dark; + } + return theme === "dark"; + } + + private onThemeChanged = () => { + this.setState({isDarkTheme: this.isUserOnDarkTheme()}); + }; + private onAction = (ev: ActionPayload) => { if (ev.action !== Action.ToggleUserMenu) return; // not interested @@ -82,7 +105,11 @@ export default class UserMenuButton extends React.Component { }; private onSwitchThemeClick = () => { - console.log("TODO: Switch theme"); + // Disable system theme matching if the user hits this button + SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false); + + const newTheme = this.state.isDarkTheme ? "light" : "dark"; + SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme); }; private onSettingsOpen = (ev: React.MouseEvent, tabId: string) => { @@ -142,7 +169,7 @@ export default class UserMenuButton extends React.Component {
        Date: Mon, 8 Jun 2020 09:32:16 -0600 Subject: [PATCH 075/181] Add hosting link --- res/css/structures/_UserMenuButton.scss | 5 ++++ src/components/structures/UserMenuButton.tsx | 27 +++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index aca5f4253a..0d35c1cbb2 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -42,6 +42,11 @@ limitations under the License. display: flex; align-items: center; + &:nth-child(n + 1) { + // The first header will have appropriate padding, subsequent ones need a margin. + margin-top: 10px; + } + .mx_UserMenuButton_contextMenu_name { // Create another flexbox of columns to handle large user IDs display: flex; diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index 40b53a35fb..dc4415ea54 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -30,6 +30,7 @@ import Modal from "../../Modal"; import LogoutDialog from "../views/dialogs/LogoutDialog"; import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import {getCustomTheme} from "../../theme"; +import {getHostingLink} from "../../utils/HostingLink"; interface IProps { } @@ -148,6 +149,28 @@ export default class UserMenuButton extends React.Component { public render() { let contextMenu; if (this.state.menuDisplayed) { + let hostingLink; + const signupLink = getHostingLink("user-context-menu"); + if (signupLink) { + hostingLink = ( +
        + {_t( + "Upgrade to your own domain", {}, + { + a: sub => ( + {sub} + ), + }, + )} +
        + ); + } + const elementRect = this.buttonRef.current.getBoundingClientRect(); contextMenu = ( { />
      -
      - TODO: Upgrade prompt -
      + {hostingLink}
      • From fd8c056200723a3a9e1aa82e5436e93aa1e16b09 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 09:40:03 -0600 Subject: [PATCH 076/181] Fix i18n --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8575b3a258..3520446c2b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2042,6 +2042,7 @@ "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s", "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", + "Switch to light mode": "Switch to light mode", "Switch to dark mode": "Switch to dark mode", "Switch theme": "Switch theme", "Security & privacy": "Security & privacy", From 35ecaff3995751fde12bdc152a65e34fd588d827 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 16:48:32 +0100 Subject: [PATCH 077/181] Move Settings flag to ts --- ...cessibleButton.js => AccessibleButton.tsx} | 65 ++++++++-------- .../{SettingsFlag.js => SettingsFlag.tsx} | 76 ++++++++++++------- .../{ToggleSwitch.js => ToggleSwitch.tsx} | 17 ++--- 3 files changed, 89 insertions(+), 69 deletions(-) rename src/components/views/elements/{AccessibleButton.js => AccessibleButton.tsx} (71%) rename src/components/views/elements/{SettingsFlag.js => SettingsFlag.tsx} (54%) rename src/components/views/elements/{ToggleSwitch.js => ToggleSwitch.tsx} (82%) diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.tsx similarity index 71% rename from src/components/views/elements/AccessibleButton.js rename to src/components/views/elements/AccessibleButton.tsx index d708a44ab2..f35cd52734 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.tsx @@ -15,7 +15,6 @@ */ import React from 'react'; -import PropTypes from 'prop-types'; import {Key} from '../../../Keyboard'; @@ -27,11 +26,20 @@ import {Key} from '../../../Keyboard'; * @param {Object} props react element properties * @returns {Object} rendered react */ -export default function AccessibleButton(props) { - const {element, onClick, children, kind, disabled, ...restProps} = props; +export default function AccessibleButton({ + element, + onClick, + children, + kind, + disabled, + inputRef, + className, + ...restProps +}: IProps) { + const newProps: IAccessibleButtonProps = restProps; if (!disabled) { - restProps.onClick = onClick; + newProps.onClick = onClick, // We need to consume enter onKeyDown and space onKeyUp // otherwise we are risking also activating other keyboard focusable elements // that might receive focus as a result of the AccessibleButtonClick action @@ -39,7 +47,7 @@ export default function AccessibleButton(props) { // And divs which we report as role button to assistive technologies. // Browsers handle space and enter keypresses differently and we are only adjusting to the // inconsistencies here - restProps.onKeyDown = function(e) { + newProps.onKeyDown = (e) => { if (e.key === Key.ENTER) { e.stopPropagation(); e.preventDefault(); @@ -49,8 +57,8 @@ export default function AccessibleButton(props) { e.stopPropagation(); e.preventDefault(); } - }; - restProps.onKeyUp = function(e) { + }, + newProps.onKeyUp = (e) => { if (e.key === Key.SPACE) { e.stopPropagation(); e.preventDefault(); @@ -60,26 +68,26 @@ export default function AccessibleButton(props) { e.stopPropagation(); e.preventDefault(); } - }; + } } // Pass through the ref - used for keyboard shortcut access to some buttons - restProps.ref = restProps.inputRef; - delete restProps.inputRef; + newProps.ref = inputRef; - restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton"; + newProps.className = (className ? className + " " : "") + "mx_AccessibleButton"; if (kind) { // We apply a hasKind class to maintain backwards compatibility with // buttons which might not know about kind and break - restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind; + newProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind; } if (disabled) { - restProps.className += " mx_AccessibleButton_disabled"; - restProps["aria-disabled"] = true; + newProps.className += " mx_AccessibleButton_disabled"; + newProps["aria-disabled"] = true; } + // React.createElement expects InputHTMLAttributes return React.createElement(element, restProps, children); } @@ -89,28 +97,25 @@ export default function AccessibleButton(props) { * onClick: (required) Event handler for button activation. Should be * implemented exactly like a normal onClick handler. */ -AccessibleButton.propTypes = { - children: PropTypes.node, - inputRef: PropTypes.oneOfType([ - // Either a function - PropTypes.func, - // Or the instance of a DOM native element - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - ]), - element: PropTypes.string, - onClick: PropTypes.func.isRequired, - +interface IProps extends React.InputHTMLAttributes { + inputRef?: React.Ref, + element?: string; // The kind of button, similar to how Bootstrap works. // See available classes for AccessibleButton for options. - kind: PropTypes.string, + kind?: string, // The ARIA role - role: PropTypes.string, + role?: string, // The tabIndex - tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - - disabled: PropTypes.bool, + tabIndex?: number, + disabled?: boolean, + className?: string, + onClick(e?: React.MouseEvent | React.KeyboardEvent): void; }; +interface IAccessibleButtonProps extends React.InputHTMLAttributes { + ref?: React.Ref, +} + AccessibleButton.defaultProps = { element: 'div', role: 'button', diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.tsx similarity index 54% rename from src/components/views/elements/SettingsFlag.js rename to src/components/views/elements/SettingsFlag.tsx index 15f17805a8..2ae9bc3d87 100644 --- a/src/components/views/elements/SettingsFlag.js +++ b/src/components/views/elements/SettingsFlag.tsx @@ -21,58 +21,76 @@ import createReactClass from 'create-react-class'; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from '../../../languageHandler'; import ToggleSwitch from "./ToggleSwitch"; +import StyledCheckbox from "./StyledCheckbox"; -export default createReactClass({ - displayName: 'SettingsFlag', - propTypes: { - name: PropTypes.string.isRequired, - level: PropTypes.string.isRequired, - roomId: PropTypes.string, // for per-room settings - label: PropTypes.string, // untranslated - onChange: PropTypes.func, - isExplicit: PropTypes.bool, - }, +interface IProps { + name: string, + level: string, + roomId?: string, // for per-room settings + label?: string, // untranslated + isExplicit: boolean, + // XXX: once design replaces all toggles make this the default + useCheckbox?: boolean, + onChange(checked: boolean): void, +} - getInitialState: function() { - return { +interface IState { + // XXX: make this generic when the settings store is typed + value: any; +} + +export default class SettingsFlag extends React.Component { + + constructor(props: IProps) { + super(props); + + this.state = { value: SettingsStore.getValueAt( this.props.level, this.props.name, this.props.roomId, this.props.isExplicit, ), - }; - }, - - onChange: function(checked) { - if (this.props.group && !checked) return; + } + } + private onChange = (checked: boolean): void => { this.save(checked); this.setState({ value: checked }); if (this.props.onChange) this.props.onChange(checked); - }, + } - save: function(val = undefined) { + private checkBoxOnChange = (e: React.ChangeEvent) => { + this.onChange(e.target.checked); + } + + private save = (val?: any): void => { return SettingsStore.setValue( this.props.name, this.props.roomId, this.props.level, val !== undefined ? val : this.state.value, ); - }, + } - render: function() { + public render() { const canChange = SettingsStore.canSetValue(this.props.name, this.props.roomId, this.props.level); let label = this.props.label; if (!label) label = SettingsStore.getDisplayName(this.props.name, this.props.level); else label = _t(label); - return ( -
        - {label} - -
        - ); - }, -}); + if (this.props.useCheckbox) { + return + {label} + ; + } else { + return ( +
        + {label} + +
        + ); + } + } +} diff --git a/src/components/views/elements/ToggleSwitch.js b/src/components/views/elements/ToggleSwitch.tsx similarity index 82% rename from src/components/views/elements/ToggleSwitch.js rename to src/components/views/elements/ToggleSwitch.tsx index bea1a85555..4cb2fa1ef1 100644 --- a/src/components/views/elements/ToggleSwitch.js +++ b/src/components/views/elements/ToggleSwitch.tsx @@ -15,14 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; -import PropTypes from "prop-types"; +import React, { EventHandler } from "react"; import classNames from "classnames"; import * as sdk from "../../../index"; // Controlled Toggle Switch element, written with Accessibility in mind -const ToggleSwitch = ({checked, disabled=false, onChange, ...props}) => { - const _onClick = (e) => { +export default ({checked, disabled=false, onChange, ...props}: IProps) => { + const _onClick = () => { if (disabled) return; onChange(!checked); }; @@ -47,15 +46,13 @@ const ToggleSwitch = ({checked, disabled=false, onChange, ...props}) => { ); }; -ToggleSwitch.propTypes = { +interface IProps { // Whether or not this toggle is in the 'on' position. - checked: PropTypes.bool.isRequired, + checked: boolean, // Whether or not the user can interact with the switch - disabled: PropTypes.bool, + disabled: boolean, // Called when the checked state changes. First argument will be the new state. - onChange: PropTypes.func.isRequired, + onChange(checked: boolean): void, }; - -export default ToggleSwitch; From 26eaef848b7649a7796fabb3ebb668099a6e70f5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 16:53:19 +0100 Subject: [PATCH 078/181] Use Element instead of HTMLElement --- src/components/views/elements/AccessibleButton.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index f35cd52734..6dcadaf63f 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -97,8 +97,8 @@ export default function AccessibleButton({ * onClick: (required) Event handler for button activation. Should be * implemented exactly like a normal onClick handler. */ -interface IProps extends React.InputHTMLAttributes { - inputRef?: React.Ref, +interface IProps extends React.InputHTMLAttributes { + inputRef?: React.Ref, element?: string; // The kind of button, similar to how Bootstrap works. // See available classes for AccessibleButton for options. @@ -109,11 +109,11 @@ interface IProps extends React.InputHTMLAttributes { tabIndex?: number, disabled?: boolean, className?: string, - onClick(e?: React.MouseEvent | React.KeyboardEvent): void; + onClick(e?: React.MouseEvent | React.KeyboardEvent): void; }; -interface IAccessibleButtonProps extends React.InputHTMLAttributes { - ref?: React.Ref, +interface IAccessibleButtonProps extends React.InputHTMLAttributes { + ref?: React.Ref, } AccessibleButton.defaultProps = { From d6a532040ec60bc2e752703f98276f1b1d92a602 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 16:57:39 +0100 Subject: [PATCH 079/181] lint --- src/components/views/elements/AccessibleButton.tsx | 2 +- src/components/views/elements/ToggleSwitch.tsx | 2 +- src/components/views/rooms/RoomSublist2.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index 6dcadaf63f..ecd4847d0d 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -109,7 +109,7 @@ interface IProps extends React.InputHTMLAttributes { tabIndex?: number, disabled?: boolean, className?: string, - onClick(e?: React.MouseEvent | React.KeyboardEvent): void; + onClick?(e?: React.MouseEvent | React.KeyboardEvent): void; }; interface IAccessibleButtonProps extends React.InputHTMLAttributes { diff --git a/src/components/views/elements/ToggleSwitch.tsx b/src/components/views/elements/ToggleSwitch.tsx index 4cb2fa1ef1..902538052b 100644 --- a/src/components/views/elements/ToggleSwitch.tsx +++ b/src/components/views/elements/ToggleSwitch.tsx @@ -20,7 +20,7 @@ import classNames from "classnames"; import * as sdk from "../../../index"; // Controlled Toggle Switch element, written with Accessibility in mind -export default ({checked, disabled=false, onChange, ...props}: IProps) => { +export default ({checked, disabled = false, onChange, ...props}: IProps) => { const _onClick = () => { if (disabled) return; onChange(!checked); diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index d3bb19729d..590c38931e 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -182,7 +182,7 @@ export default class RoomSublist2 extends React.Component { tabIndex={tabIndex} className={"mx_RoomSubList_label"} role="treeitem" - aria-level="1" + aria-level={1} > {chevron} {this.props.label} From bd58f6ea7bfe77decb86f20bcfa5b696a6240cdc Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 17:38:07 +0100 Subject: [PATCH 080/181] Hide checkbox on dark backgrounds --- res/css/views/elements/_StyledCheckbox.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index 14081f1e99..9cb82349ca 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -48,6 +48,8 @@ limitations under the License. border-radius: $border-radius; img { + display: none; + height: 100%; width: 100%; filter: invert(100%); @@ -57,6 +59,10 @@ limitations under the License. &:checked + label > .mx_Checkbox_background { background: $accent-color; border-color: $accent-color; + + img { + display: block; + } } & + label > *:not(.mx_Checkbox_background) { From de4c2fe3d9b2fb09e07daf0ff9190d7f838a98a6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 11:06:21 -0600 Subject: [PATCH 081/181] Use real buttons in user menu --- res/css/structures/_UserMenuButton.scss | 2 +- src/components/structures/UserMenuButton.tsx | 25 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss index 0d35c1cbb2..1f4183f8d6 100644 --- a/res/css/structures/_UserMenuButton.scss +++ b/res/css/structures/_UserMenuButton.scss @@ -129,7 +129,7 @@ limitations under the License. margin: 0; padding: 20px 0 0; - a { + .mx_AccessibleButton { text-decoration: none; color: $primary-fg-color; font-size: $font-15px; diff --git a/src/components/structures/UserMenuButton.tsx b/src/components/structures/UserMenuButton.tsx index dc4415ea54..d8f96d4a91 100644 --- a/src/components/structures/UserMenuButton.tsx +++ b/src/components/structures/UserMenuButton.tsx @@ -31,6 +31,7 @@ import LogoutDialog from "../views/dialogs/LogoutDialog"; import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import {getCustomTheme} from "../../theme"; import {getHostingLink} from "../../utils/HostingLink"; +import AccessibleButton from "../views/elements/AccessibleButton"; interface IProps { } @@ -205,44 +206,44 @@ export default class UserMenuButton extends React.Component { From b4bdb23f5f7bf88205c7f2a50508f9840ff81b0b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Mon, 8 Jun 2020 19:02:36 +0100 Subject: [PATCH 082/181] Clean up font scaling appearance --- .../settings/tabs/user/_AppearanceUserSettingsTab.scss | 4 ++++ .../views/settings/tabs/user/AppearanceUserSettingsTab.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index e82ae3c575..7308bb7177 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -43,3 +43,7 @@ limitations under the License. padding-left: 20px; padding-right: 5px; } + +.mx_SettingsTab_customFontSizeField { + margin-left: calc($font-16px + 10px); +} diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 2b58e0e28e..e7f22d5ea2 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -281,7 +281,7 @@ export default class AppearanceUserSettingsTab extends React.Component ""} + displayFunc={_ => ""} disabled={this.state.useCustomFontSize} />
        Aa
        @@ -290,9 +290,10 @@ export default class AppearanceUserSettingsTab extends React.Component this.setState({useCustomFontSize: checked})} + useCheckbox={true} /> this.setState({fontSize: value.target.value})} disabled={!this.state.useCustomFontSize} + className="mx_SettingsTab_customFontSizeField" />
      ; } From 21c8611300e27ef6d731b6f60a4b5b21115165d9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 12:14:10 -0600 Subject: [PATCH 083/181] Convert FormattingUtils to TypeScript and add badge utility function The new function is to be used in the new room list. --- ...{FormattingUtils.js => FormattingUtils.ts} | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) rename src/utils/{FormattingUtils.js => FormattingUtils.ts} (81%) diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.ts similarity index 81% rename from src/utils/FormattingUtils.js rename to src/utils/FormattingUtils.ts index b932214530..ed1727d190 100644 --- a/src/utils/FormattingUtils.js +++ b/src/utils/FormattingUtils.ts @@ -1,6 +1,6 @@ /* Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 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,8 +21,8 @@ import { _t } from '../languageHandler'; * formats numbers to fit into ~3 characters, suitable for badge counts * e.g: 999, 9.9K, 99K, 0.9M, 9.9M, 99M, 0.9B, 9.9B */ -export function formatCount(count) { - if (count < 1000) return count; +export function formatCount(count: number): string { + if (count < 1000) return count.toString(); if (count < 10000) return (count / 1000).toFixed(1) + "K"; if (count < 100000) return (count / 1000).toFixed(0) + "K"; if (count < 10000000) return (count / 1000000).toFixed(1) + "M"; @@ -34,7 +34,7 @@ export function formatCount(count) { * Format a count showing the whole number but making it a bit more readable. * e.g: 1000 => 1,000 */ -export function formatCountLong(count) { +export function formatCountLong(count: number): string { const formatter = new Intl.NumberFormat(); return formatter.format(count) } @@ -43,7 +43,7 @@ export function formatCountLong(count) { * format a size in bytes into a human readable form * e.g: 1024 -> 1.00 KB */ -export function formatBytes(bytes, decimals = 2) { +export function formatBytes(bytes: number, decimals = 2): string { if (bytes === 0) return '0 Bytes'; const k = 1024; @@ -62,7 +62,7 @@ export function formatBytes(bytes, decimals = 2) { * * @return {string} */ -export function formatCryptoKey(key) { +export function formatCryptoKey(key: string): string { return key.match(/.{1,4}/g).join(" "); } /** @@ -72,7 +72,7 @@ export function formatCryptoKey(key) { * * @return {number} */ -export function hashCode(str) { +export function hashCode(str: string): number { let hash = 0; let i; let chr; @@ -87,7 +87,7 @@ export function hashCode(str) { return Math.abs(hash); } -export function getUserNameColorClass(userId) { +export function getUserNameColorClass(userId: string): string { const colorNumber = (hashCode(userId) % 8) + 1; return `mx_Username_color${colorNumber}`; } @@ -103,7 +103,7 @@ export function getUserNameColorClass(userId) { * @returns {string} a string constructed by joining `items` with a comma * between each item, but with the last item appended as " and [lastItem]". */ -export function formatCommaSeparatedList(items, itemLimit) { +export function formatCommaSeparatedList(items: string[], itemLimit?: number): string { const remaining = itemLimit === undefined ? 0 : Math.max( items.length - itemLimit, 0, ); @@ -119,3 +119,13 @@ export function formatCommaSeparatedList(items, itemLimit) { return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem }); } } + +/** + * Formats a number into a 'minimal' badge count (9, 99, 99+). + * @param count The number to convert + * @returns The badge count, stringified. + */ +export function formatMinimalBadgeCount(count: number): string { + if (count < 100) return count.toString(); + return "99+"; +} From 086b9101fa8720f698644b934e6886ad1563e567 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 13:42:18 -0600 Subject: [PATCH 084/181] Add sublist badge counts to new room list Also add IDLE state to rooms --- res/css/_components.scss | 1 + res/css/views/rooms/_NotificationBadge.scss | 72 +++++ res/css/views/rooms/_RoomSublist2.scss | 35 ++- res/css/views/rooms/_RoomTile2.scss | 34 +-- .../views/rooms/NotificationBadge.tsx | 279 ++++++++++++++++++ src/components/views/rooms/RoomSublist2.tsx | 64 ++-- src/components/views/rooms/RoomTile2.tsx | 109 +------ 7 files changed, 408 insertions(+), 186 deletions(-) create mode 100644 res/css/views/rooms/_NotificationBadge.scss create mode 100644 src/components/views/rooms/NotificationBadge.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 62bec5ad62..61e3018725 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -170,6 +170,7 @@ @import "./views/rooms/_MemberList.scss"; @import "./views/rooms/_MessageComposer.scss"; @import "./views/rooms/_MessageComposerFormatBar.scss"; +@import "./views/rooms/_NotificationBadge.scss"; @import "./views/rooms/_PinnedEventTile.scss"; @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; diff --git a/res/css/views/rooms/_NotificationBadge.scss b/res/css/views/rooms/_NotificationBadge.scss new file mode 100644 index 0000000000..609e41c583 --- /dev/null +++ b/res/css/views/rooms/_NotificationBadge.scss @@ -0,0 +1,72 @@ +/* +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_NotificationBadge { + &:not(.mx_NotificationBadge_visible) { + display: none; + } + + // Badges are structured a bit weirdly to work around issues with non-monospace + // font styles. The badge pill is actually a background div and the count floats + // within that. For example: + // + // ( 99+ ) <-- Rounded pill is a _bg class. + // ^- The count is an element floating within that. + + &.mx_NotificationBadge_visible { + background-color: $roomtile2-badge-color; + margin-right: 14px; + + // Create a flexbox to order the count a bit easier + display: flex; + align-items: center; + justify-content: center; + + &.mx_NotificationBadge_highlighted { + // TODO: Use a more specific variable + background-color: $warning-color; + } + + // These are the 3 background types + + &.mx_NotificationBadge_dot { + width: 6px; + height: 6px; + border-radius: 6px; + margin-right: 18px; + } + + &.mx_NotificationBadge_2char { + width: 16px; + height: 16px; + border-radius: 16px; + } + + &.mx_NotificationBadge_3char { + width: 26px; + height: 16px; + border-radius: 16px; + } + + // The following is the floating badge + + .mx_NotificationBadge_count { + font-size: $font-10px; + line-height: $font-14px; + color: #fff; // TODO: Variable + } + } +} diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index e6e5af3b48..cfb9bc3b6d 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -30,11 +30,36 @@ limitations under the License. margin-bottom: 12px; .mx_RoomSublist2_headerContainer { - text-transform: uppercase; - opacity: 0.5; - line-height: $font-16px; - font-size: $font-12px; - padding-bottom: 8px; + // Create a flexbox to make ordering easy + display: flex; + align-items: center; + + .mx_RoomSublist2_badgeContainer { + opacity: 0.8; + padding-right: 7px; + + // Create another flexbox row because it's super easy to position the badge at + // the end this way. + display: flex; + align-items: center; + justify-content: flex-end; + } + + .mx_RoomSublist2_headerText { + text-transform: uppercase; + opacity: 0.5; + line-height: $font-16px; + font-size: $font-12px; + padding-bottom: 8px; + + width: 100%; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } } .mx_RoomSublist2_resizeBox { diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index 3151bb8716..41c9469bc1 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -50,11 +50,14 @@ limitations under the License. // TODO: Ellipsis on the name and preview .mx_RoomTile2_name { - font-weight: 600; font-size: $font-14px; line-height: $font-19px; } + .mx_RoomTile2_name.mx_RoomTile2_nameHasUnreadEvents { + font-weight: 600; + } + .mx_RoomTile2_messagePreview { font-size: $font-13px; line-height: $font-18px; @@ -70,34 +73,5 @@ limitations under the License. display: flex; align-items: center; justify-content: flex-end; - - .mx_RoomTile2_badge { - background-color: $roomtile2-badge-color; - - &:not(.mx_RoomTile2_badgeEmpty) { - border-radius: 16px; - font-size: $font-10px; - line-height: $font-14px; - text-align: center; - font-weight: bold; - margin-right: 14px; - color: #fff; // TODO: Variable - - // TODO: Confirm padding on counted badges - padding: 2px 5px; - } - - &.mx_RoomTile2_badgeEmpty { - width: 6px; - height: 6px; - border-radius: 6px; - margin-right: 18px; - } - - &.mx_RoomTile2_badgeHighlight { - // TODO: Use a more specific variable - background-color: $warning-color; - } - } } } diff --git a/src/components/views/rooms/NotificationBadge.tsx b/src/components/views/rooms/NotificationBadge.tsx new file mode 100644 index 0000000000..a77d2fc8d0 --- /dev/null +++ b/src/components/views/rooms/NotificationBadge.tsx @@ -0,0 +1,279 @@ +/* +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 classNames from "classnames"; +import { formatMinimalBadgeCount } from "../../../utils/FormattingUtils"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; +import AccessibleButton from "../../views/elements/AccessibleButton"; +import RoomAvatar from "../../views/avatars/RoomAvatar"; +import dis from '../../../dispatcher/dispatcher'; +import { Key } from "../../../Keyboard"; +import * as RoomNotifs from '../../../RoomNotifs'; +import { EffectiveMembership, getEffectiveMembership } from "../../../stores/room-list/membership"; +import * as Unread from '../../../Unread'; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import ActiveRoomObserver from "../../../ActiveRoomObserver"; +import { EventEmitter } from "events"; +import { arrayDiff } from "../../../utils/arrays"; + +export const NOTIFICATION_STATE_UPDATE = "update"; + +export enum NotificationColor { + // Inverted (None -> Red) because we do integer comparisons on this + None, // nothing special + Bold, // no badge, show as unread + Grey, // unread notified messages + Red, // unread pings +} + +export interface INotificationState extends EventEmitter { + symbol?: string; + count: number; + color: NotificationColor; +} + +interface IProps { + notification: INotificationState; + + /** + * If true, the badge will conditionally display a badge without count for the user. + */ + allowNoCount: boolean; +} + +interface IState { +} + +export default class NotificationBadge extends React.PureComponent { + constructor(props: IProps) { + super(props); + this.props.notification.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); + } + + public componentDidUpdate(prevProps: Readonly) { + if (prevProps.notification) { + prevProps.notification.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); + } + + this.props.notification.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); + } + + private onNotificationUpdate = () => { + this.forceUpdate(); // notification state changed - update + }; + + public render(): React.ReactElement { + // Don't show a badge if we don't need to + if (this.props.notification.color <= NotificationColor.Bold) return null; + + const hasNotif = this.props.notification.color >= NotificationColor.Red; + const hasCount = this.props.notification.color >= NotificationColor.Grey; + const isEmptyBadge = this.props.allowNoCount && !localStorage.getItem("mx_rl_rt_badgeCount"); + + let symbol = this.props.notification.symbol || formatMinimalBadgeCount(this.props.notification.count); + if (isEmptyBadge) symbol = ""; + + const classes = classNames({ + 'mx_NotificationBadge': true, + 'mx_NotificationBadge_visible': hasCount, + 'mx_NotificationBadge_highlighted': hasNotif, + 'mx_NotificationBadge_dot': isEmptyBadge, + 'mx_NotificationBadge_2char': symbol.length > 0 && symbol.length < 3, + 'mx_NotificationBadge_3char': symbol.length > 2, + }); + + return ( +
      + {symbol} +
      + ); + } +} + +export class RoomNotificationState extends EventEmitter { + private _symbol: string; + private _count: number; + private _color: NotificationColor; + + constructor(private room: Room) { + super(); + this.room.on("Room.receipt", this.handleRoomEventUpdate); + this.room.on("Room.timeline", this.handleRoomEventUpdate); + this.room.on("Room.redaction", this.handleRoomEventUpdate); + MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate); + this.updateNotificationState(); + } + + public get symbol(): string { + return this._symbol; + } + + public get count(): number { + return this._count; + } + + public get color(): NotificationColor { + return this._color; + } + + private get roomIsInvite(): boolean { + return getEffectiveMembership(this.room.getMyMembership()) === EffectiveMembership.Invite; + } + + public dispose(): void { + this.room.removeListener("Room.receipt", this.handleRoomEventUpdate); + this.room.removeListener("Room.timeline", this.handleRoomEventUpdate); + this.room.removeListener("Room.redaction", this.handleRoomEventUpdate); + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate); + } + } + + private handleRoomEventUpdate = (event: MatrixEvent) => { + const roomId = event.getRoomId(); + + if (roomId !== this.room.roomId) return; // ignore - not for us + this.updateNotificationState(); + }; + + private updateNotificationState() { + const before = {count: this.count, symbol: this.symbol, color: this.color}; + + if (this.roomIsInvite) { + this._color = NotificationColor.Red; + this._symbol = "!"; + this._count = 1; // not used, technically + } else { + const redNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'highlight'); + const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'total'); + + // For a 'true count' we pick the grey notifications first because they include the + // red notifications. If we don't have a grey count for some reason we use the red + // count. If that count is broken for some reason, assume zero. This avoids us showing + // a badge for 'NaN' (which formats as 'NaNB' for NaN Billion). + const trueCount = greyNotifs ? greyNotifs : (redNotifs ? redNotifs : 0); + + // Note: we only set the symbol if we have an actual count. We don't want to show + // zero on badges. + + if (redNotifs > 0) { + this._color = NotificationColor.Red; + this._count = trueCount; + this._symbol = null; // symbol calculated by component + } else if (greyNotifs > 0) { + this._color = NotificationColor.Grey; + this._count = trueCount; + this._symbol = null; // symbol calculated by component + } else { + // We don't have any notified messages, but we might have unread messages. Let's + // find out. + const hasUnread = Unread.doesRoomHaveUnreadMessages(this.room); + if (hasUnread) { + this._color = NotificationColor.Bold; + } else { + this._color = NotificationColor.None; + } + + // no symbol or count for this state + this._count = 0; + this._symbol = null; + } + } + + // finally, publish an update if needed + const after = {count: this.count, symbol: this.symbol, color: this.color}; + if (JSON.stringify(before) !== JSON.stringify(after)) { + this.emit(NOTIFICATION_STATE_UPDATE); + } + } +} + +export class ListNotificationState extends EventEmitter { + private _count: number; + private _color: NotificationColor; + private rooms: Room[] = []; + private states: { [roomId: string]: RoomNotificationState } = {}; + + constructor(private byTileCount = false) { + super(); + } + + public get symbol(): string { + return null; // This notification state doesn't support symbols + } + + public get count(): number { + return this._count; + } + + public get color(): NotificationColor { + return this._color; + } + + public setRooms(rooms: Room[]) { + // If we're only concerned about the tile count, don't bother setting up listeners. + if (this.byTileCount) { + this.rooms = rooms; + this.calculateTotalState(); + return; + } + + const oldRooms = this.rooms; + const diff = arrayDiff(oldRooms, rooms); + for (const oldRoom of diff.removed) { + const state = this.states[oldRoom.roomId]; + delete this.states[oldRoom.roomId]; + state.off(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate); + state.dispose(); + } + for (const newRoom of diff.added) { + const state = new RoomNotificationState(newRoom); + state.on(NOTIFICATION_STATE_UPDATE, this.onRoomNotificationStateUpdate); + this.states[newRoom.roomId] = state; + } + + this.calculateTotalState(); + } + + private onRoomNotificationStateUpdate = () => { + this.calculateTotalState(); + }; + + private calculateTotalState() { + const before = {count: this.count, symbol: this.symbol, color: this.color}; + + if (this.byTileCount) { + this._color = NotificationColor.Red; + this._count = this.rooms.length; + } else { + this._count = 0; + this._color = NotificationColor.None; + for (const state of Object.values(this.states)) { + this._count += state.count; + this._color = Math.max(this.color, state.color); + } + } + + // finally, publish an update if needed + const after = {count: this.count, symbol: this.symbol, color: this.color}; + if (JSON.stringify(before) !== JSON.stringify(after)) { + this.emit(NOTIFICATION_STATE_UPDATE); + } + } +} diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index 650a3ae645..cd27156cbd 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -26,7 +26,7 @@ import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomTile2 from "./RoomTile2"; import { ResizableBox, ResizeCallbackData } from "react-resizable"; import { ListLayout } from "../../../stores/room-list/ListLayout"; -import { DefaultTagID, TagID } from "../../../stores/room-list/models"; +import NotificationBadge, { ListNotificationState } from "./NotificationBadge"; /******************************************************************* * CAUTION * @@ -56,13 +56,19 @@ interface IProps { } interface IState { + notificationState: ListNotificationState; } export default class RoomSublist2 extends React.Component { private headerButton = createRef(); - private hasTiles(): boolean { - return this.numTiles > 0; + constructor(props: IProps) { + super(props); + + this.state = { + notificationState: new ListNotificationState(this.props.isInvite), + }; + this.state.notificationState.setRooms(this.props.rooms); } private get numTiles(): number { @@ -70,6 +76,10 @@ export default class RoomSublist2 extends React.Component { return (this.props.rooms || []).length; } + public componentDidUpdate() { + this.state.notificationState.setRooms(this.props.rooms); + } + private onAddRoom = (e) => { e.stopPropagation(); if (this.props.onAddRoom) this.props.onAddRoom(); @@ -106,13 +116,6 @@ export default class RoomSublist2 extends React.Component { } private renderHeader(): React.ReactElement { - // TODO: Handle badge count - // const notifications = !this.props.isInvite - // ? RoomNotifs.aggregateNotificationCount(this.props.rooms) - // : {count: 0, highlight: true}; - // const notifCount = notifications.count; - // const notifHighlight = notifications.highlight; - // TODO: Title on collapsed // TODO: Incoming call box @@ -123,42 +126,8 @@ export default class RoomSublist2 extends React.Component { const tabIndex = isActive ? 0 : -1; // TODO: Collapsed state - // TODO: Handle badge count - // let badge; - // if (true) { // !isCollapsed - // const showCount = localStorage.getItem("mx_rls_count") || notifHighlight; - // const badgeClasses = classNames({ - // 'mx_RoomSublist2_badge': true, - // 'mx_RoomSublist2_badgeHighlight': notifHighlight, - // 'mx_RoomSublist2_badgeEmpty': !showCount, - // }); - // // Wrap the contents in a div and apply styles to the child div so that the browser default outline works - // if (notifCount > 0) { - // const count =
      {FormattingUtils.formatCount(notifCount)}
      ; - // badge = ( - // - // {showCount ? count : null} - // - // ); - // } else if (this.props.isInvite && this.hasTiles()) { - // // Render the `!` badge for invites - // badge = ( - // - //
      - // {FormattingUtils.formatCount(this.numTiles)} - //
      - //
      - // ); - // } - // } + + const badge = ; // TODO: Aux button // let addRoomButton = null; @@ -185,6 +154,9 @@ export default class RoomSublist2 extends React.Component { > {this.props.label} +
      + {badge} +
    ); }} diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 09d7b46ba5..d4f64e4571 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -25,13 +25,8 @@ import AccessibleButton from "../../views/elements/AccessibleButton"; import RoomAvatar from "../../views/avatars/RoomAvatar"; import dis from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; -import * as RoomNotifs from '../../../RoomNotifs'; -import { EffectiveMembership, getEffectiveMembership } from "../../../stores/room-list/membership"; -import * as Unread from '../../../Unread'; -import * as FormattingUtils from "../../../utils/FormattingUtils"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import ActiveRoomObserver from "../../../ActiveRoomObserver"; +import NotificationBadge, { INotificationState, NotificationColor, RoomNotificationState } from "./NotificationBadge"; /******************************************************************* * CAUTION * @@ -41,14 +36,6 @@ import ActiveRoomObserver from "../../../ActiveRoomObserver"; * warning disappears. * *******************************************************************/ -enum NotificationColor { - // Inverted (None -> Red) because we do integer comparisons on this - None, // nothing special - Bold, // no badge, show as unread - Grey, // unread notified messages - Red, // unread pings -} - interface IProps { room: Room; showMessagePreview: boolean; @@ -58,11 +45,6 @@ interface IProps { // TODO: Incoming call boxes? } -interface INotificationState { - symbol: string; - color: NotificationColor; -} - interface IState { hover: boolean; notificationState: INotificationState; @@ -88,89 +70,17 @@ export default class RoomTile2 extends React.Component { this.state = { hover: false, - notificationState: this.getNotificationState(), + notificationState: new RoomNotificationState(this.props.room), selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId, }; - this.props.room.on("Room.receipt", this.handleRoomEventUpdate); - this.props.room.on("Room.timeline", this.handleRoomEventUpdate); - this.props.room.on("Room.redaction", this.handleRoomEventUpdate); - MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate); ActiveRoomObserver.addListener(this.props.room.roomId, this.onActiveRoomUpdate); } public componentWillUnmount() { if (this.props.room) { - this.props.room.removeListener("Room.receipt", this.handleRoomEventUpdate); - this.props.room.removeListener("Room.timeline", this.handleRoomEventUpdate); - this.props.room.removeListener("Room.redaction", this.handleRoomEventUpdate); ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate); } - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate); - } - } - - // XXX: This is a bit of an awful-looking hack. We should probably be using state for - // this, but instead we're kinda forced to either duplicate the code or thread a variable - // through the code paths. This feels like the least evil option. - private get roomIsInvite(): boolean { - return getEffectiveMembership(this.props.room.getMyMembership()) === EffectiveMembership.Invite; - } - - private handleRoomEventUpdate = (event: MatrixEvent) => { - const roomId = event.getRoomId(); - - // Sanity check: should never happen - if (roomId !== this.props.room.roomId) return; - - this.updateNotificationState(); - }; - - private updateNotificationState() { - this.setState({notificationState: this.getNotificationState()}); - } - - private getNotificationState(): INotificationState { - const state: INotificationState = { - color: NotificationColor.None, - symbol: null, - }; - - if (this.roomIsInvite) { - state.color = NotificationColor.Red; - state.symbol = "!"; - } else { - const redNotifs = RoomNotifs.getUnreadNotificationCount(this.props.room, 'highlight'); - const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.props.room, 'total'); - - // For a 'true count' we pick the grey notifications first because they include the - // red notifications. If we don't have a grey count for some reason we use the red - // count. If that count is broken for some reason, assume zero. This avoids us showing - // a badge for 'NaN' (which formats as 'NaNB' for NaN Billion). - const trueCount = greyNotifs ? greyNotifs : (redNotifs ? redNotifs : 0); - - // Note: we only set the symbol if we have an actual count. We don't want to show - // zero on badges. - - if (redNotifs > 0) { - state.color = NotificationColor.Red; - state.symbol = FormattingUtils.formatCount(trueCount); - } else if (greyNotifs > 0) { - state.color = NotificationColor.Grey; - state.symbol = FormattingUtils.formatCount(trueCount); - } else { - // We don't have any notified messages, but we might have unread messages. Let's - // find out. - const hasUnread = Unread.doesRoomHaveUnreadMessages(this.props.room); - if (hasUnread) { - state.color = NotificationColor.Bold; - // no symbol for this state - } - } - } - - return state; } private onTileMouseEnter = () => { @@ -206,19 +116,7 @@ export default class RoomTile2 extends React.Component { 'mx_RoomTile2_selected': this.state.selected, }); - let badge; - const hasBadge = this.state.notificationState.color > NotificationColor.Bold; - if (hasBadge) { - const hasNotif = this.state.notificationState.color >= NotificationColor.Red; - const isEmptyBadge = !localStorage.getItem("mx_rl_rt_badgeCount"); - const badgeClasses = classNames({ - 'mx_RoomTile2_badge': true, - 'mx_RoomTile2_badgeHighlight': hasNotif, - 'mx_RoomTile2_badgeEmpty': isEmptyBadge, - }); - const symbol = this.state.notificationState.symbol; - badge =
    {isEmptyBadge ? null : symbol}
    ; - } + const badge = ; // TODO: the original RoomTile uses state for the room name. Do we need to? let name = this.props.room.name; @@ -237,6 +135,7 @@ export default class RoomTile2 extends React.Component { const nameClasses = classNames({ "mx_RoomTile2_name": true, "mx_RoomTile2_nameWithPreview": !!messagePreview, + "mx_RoomTile2_nameHasUnreadEvents": this.state.notificationState.color >= NotificationColor.Bold, }); const avatarSize = 32; From 0354bf9b6d432354c4c11f376a1c82b4a9705e0e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 17:11:58 -0600 Subject: [PATCH 085/181] Reimplement breadcrumbs for new room list This all-new component handles breadcrumbs a bit more smoothly for the app by always listening to changes even if the component isn't present. This allows the breadcrumbs to remain up to date for when the user re-enables breadcrumbs. The new behaviour is that we turn breadcrumbs on once the user has a room, and we don't turn it back off for them. This also introduces a new animation which is more stable and not laggy, though instead of sliding the breadcrumbs pop. This might be undesirable - to be reviewed. --- res/css/_components.scss | 1 + res/css/structures/_LeftPanel2.scss | 2 +- res/css/views/rooms/_RoomBreadcrumbs2.scss | 53 ++++++ src/components/structures/LeftPanel2.tsx | 34 +++- .../views/rooms/RoomBreadcrumbs2.tsx | 90 ++++++++++ src/i18n/strings/en_EN.json | 1 + src/settings/SettingsStore.js | 2 + src/stores/AsyncStoreWithClient.ts | 53 ++++++ src/stores/BreadcrumbsStore.ts | 154 ++++++++++++++++++ 9 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 res/css/views/rooms/_RoomBreadcrumbs2.scss create mode 100644 src/components/views/rooms/RoomBreadcrumbs2.tsx create mode 100644 src/stores/AsyncStoreWithClient.ts create mode 100644 src/stores/BreadcrumbsStore.ts diff --git a/res/css/_components.scss b/res/css/_components.scss index 62bec5ad62..8958aee2fc 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -175,6 +175,7 @@ @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; @import "./views/rooms/_RoomBreadcrumbs.scss"; +@import "./views/rooms/_RoomBreadcrumbs2.scss"; @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 822a5ac399..502ed18a87 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -76,9 +76,9 @@ $roomListMinimizedWidth: 50px; } .mx_LeftPanel2_breadcrumbsContainer { - // TODO: Improve CSS for breadcrumbs (currently shoved into the view rather than placed) width: 100%; overflow: hidden; + margin-top: 8px; } } diff --git a/res/css/views/rooms/_RoomBreadcrumbs2.scss b/res/css/views/rooms/_RoomBreadcrumbs2.scss new file mode 100644 index 0000000000..aa0b0ecb08 --- /dev/null +++ b/res/css/views/rooms/_RoomBreadcrumbs2.scss @@ -0,0 +1,53 @@ +/* +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. +*/ + +@keyframes breadcrumb-popin { + 0% { + // Ideally we'd use `width` instead of `opacity`, but we only + // have 16 nanoseconds to render the frame, and width is expensive. + opacity: 0; + transform: scale(0); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +.mx_RoomBreadcrumbs2 { + // Create a flexbox for the crumbs + display: flex; + flex-direction: row; + align-items: flex-start; + width: 100%; + + .mx_RoomBreadcrumbs2_crumb { + margin-right: 8px; + width: 32px; + + // React loves to add elements, so only target the one we want to animate + &:first-child { + animation: breadcrumb-popin 0.3s; + } + } + + .mx_RoomBreadcrumbs2_placeholder { + font-weight: 600; + font-size: $font-14px; + line-height: 32px; // specifically to match the height this is not scaled + height: 32px; + } +} diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index c66c0a6799..b42da0be09 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -26,7 +26,9 @@ import TopLeftMenuButton from "./TopLeftMenuButton"; import { Action } from "../../dispatcher/actions"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import BaseAvatar from '../views/avatars/BaseAvatar'; -import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; +import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; +import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore"; +import { UPDATE_EVENT } from "../../stores/AsyncStore"; /******************************************************************* * CAUTION * @@ -43,6 +45,7 @@ interface IProps { interface IState { searchExpanded: boolean; searchFilter: string; // TODO: Move search into room list? + showBreadcrumbs: boolean; } export default class LeftPanel2 extends React.Component { @@ -60,7 +63,14 @@ export default class LeftPanel2 extends React.Component { this.state = { searchExpanded: false, searchFilter: "", + showBreadcrumbs: BreadcrumbsStore.instance.visible, }; + + BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); + } + + public componentWillUnmount() { + BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); } private onSearch = (term: string): void => { @@ -85,6 +95,13 @@ export default class LeftPanel2 extends React.Component { } } + private onBreadcrumbsUpdate = () => { + const newVal = BreadcrumbsStore.instance.visible; + if (newVal !== this.state.showBreadcrumbs) { + this.setState({showBreadcrumbs: newVal}); + } + }; + private renderHeader(): React.ReactNode { // TODO: Update when profile info changes // TODO: Presence @@ -100,6 +117,16 @@ export default class LeftPanel2 extends React.Component { displayName = myUser.rawDisplayName; avatarUrl = myUser.avatarUrl; } + + let breadcrumbs; + if (this.state.showBreadcrumbs) { + breadcrumbs = ( +
    + +
    + ); + } + return (
    @@ -116,9 +143,7 @@ export default class LeftPanel2 extends React.Component { {displayName}
    -
    - -
    + {breadcrumbs}
    ); } @@ -152,7 +177,6 @@ export default class LeftPanel2 extends React.Component { onBlur={() => {/*TODO*/}} />; - // TODO: Breadcrumbs // TODO: Conference handling / calls const containerClasses = classNames({ diff --git a/src/components/views/rooms/RoomBreadcrumbs2.tsx b/src/components/views/rooms/RoomBreadcrumbs2.tsx new file mode 100644 index 0000000000..195757ccf0 --- /dev/null +++ b/src/components/views/rooms/RoomBreadcrumbs2.tsx @@ -0,0 +1,90 @@ +/* +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 { BreadcrumbsStore } from "../../../stores/BreadcrumbsStore"; +import AccessibleButton from "../elements/AccessibleButton"; +import RoomAvatar from "../avatars/RoomAvatar"; +import { _t } from "../../../languageHandler"; +import { Room } from "matrix-js-sdk/src/models/room"; +import defaultDispatcher from "../../../dispatcher/dispatcher"; +import Analytics from "../../../Analytics"; +import { UPDATE_EVENT } from "../../../stores/AsyncStore"; + +/******************************************************************* + * CAUTION * + ******************************************************************* + * This is a work in progress implementation and isn't complete or * + * even useful as a component. Please avoid using it until this * + * warning disappears. * + *******************************************************************/ + +interface IProps { +} + +interface IState { +} + +export default class RoomBreadcrumbs2 extends React.PureComponent { + private isMounted = true; + + constructor(props: IProps) { + super(props); + + BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); + } + + public componentWillUnmount() { + this.isMounted = false; + BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); + } + + private onBreadcrumbsUpdate = () => { + if (!this.isMounted) return; + this.forceUpdate(); // we have no state, so this is the best we can do + }; + + private viewRoom = (room: Room, index: number) => { + Analytics.trackEvent("Breadcrumbs", "click_node", index); + defaultDispatcher.dispatch({action: "view_room", room_id: room.roomId}); + }; + + public render(): React.ReactElement { + // TODO: Decorate crumbs with icons + const tiles = BreadcrumbsStore.instance.rooms.map((r, i) => { + return ( + this.viewRoom(r, i)} + aria-label={_t("Room %(name)s", {name: r.name})} + > + + + ) + }); + + if (tiles.length === 0) { + tiles.push( +
    + {_t("No recently visited rooms")} +
    + ); + } + + return
    {tiles}
    ; + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cf6dc2431a..75caf5b593 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1069,6 +1069,7 @@ "Replying": "Replying", "Room %(name)s": "Room %(name)s", "Recent rooms": "Recent rooms", + "No recently visited rooms": "No recently visited rooms", "No rooms to show": "No rooms to show", "Unnamed room": "Unnamed room", "World readable": "World readable", diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 4b18a27c6c..dcdde46631 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -181,6 +181,8 @@ export default class SettingsStore { * @param {String} roomId The room ID to monitor for changes in. Use null for all rooms. */ static monitorSetting(settingName, roomId) { + roomId = roomId || null; // the thing wants null specifically to work, so appease it. + if (!this._monitors[settingName]) this._monitors[settingName] = {}; const registerWatcher = () => { diff --git a/src/stores/AsyncStoreWithClient.ts b/src/stores/AsyncStoreWithClient.ts new file mode 100644 index 0000000000..ce7fd45eec --- /dev/null +++ b/src/stores/AsyncStoreWithClient.ts @@ -0,0 +1,53 @@ +/* +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 { MatrixClient } from "matrix-js-sdk/src/client"; +import { AsyncStore } from "./AsyncStore"; +import { ActionPayload } from "../dispatcher/payloads"; + + +export abstract class AsyncStoreWithClient extends AsyncStore { + protected matrixClient: MatrixClient; + + protected abstract async onAction(payload: ActionPayload); + + protected async onReady() { + // Default implementation is to do nothing. + } + + protected async onNotReady() { + // Default implementation is to do nothing. + } + + protected async onDispatch(payload: ActionPayload) { + await this.onAction(payload); + + if (payload.action === 'MatrixActions.sync') { + // Filter out anything that isn't the first PREPARED sync. + if (!(payload.prevState === 'PREPARED' && payload.state !== 'PREPARED')) { + return; + } + + this.matrixClient = payload.matrixClient; + await this.onReady(); + } else if (payload.action === 'on_client_not_viable' || payload.action === 'on_logged_out') { + if (this.matrixClient) { + await this.onNotReady(); + this.matrixClient = null; + } + } + } +} diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts new file mode 100644 index 0000000000..783b38e62f --- /dev/null +++ b/src/stores/BreadcrumbsStore.ts @@ -0,0 +1,154 @@ +/* +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 SettingsStore, { SettingLevel } from "../settings/SettingsStore"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { ActionPayload } from "../dispatcher/payloads"; +import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; +import defaultDispatcher from "../dispatcher/dispatcher"; +import { arrayHasDiff } from "../utils/arrays"; + +const MAX_ROOMS = 20; // arbitrary +const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up + +interface IState { + enabled?: boolean; + rooms?: Room[]; +} + +export class BreadcrumbsStore extends AsyncStoreWithClient { + private static internalInstance = new BreadcrumbsStore(); + + private waitingRooms: { roomId: string, addedTs: number }[] = []; + + private constructor() { + super(defaultDispatcher); + + SettingsStore.monitorSetting("breadcrumb_rooms", null); + SettingsStore.monitorSetting("breadcrumbs", null); + } + + public static get instance(): BreadcrumbsStore { + return BreadcrumbsStore.internalInstance; + } + + public get rooms(): Room[] { + return this.state.rooms || []; + } + + public get visible(): boolean { + return this.state.enabled; + } + + protected async onAction(payload: ActionPayload) { + if (!this.matrixClient) return; + + if (payload.action === 'setting_updated') { + if (payload.settingName === 'breadcrumb_rooms') { + await this.updateRooms(); + } else if (payload.settingName === 'breadcrumbs') { + await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)}); + } + } else if (payload.action === 'view_room') { + if (payload.auto_join && !this.matrixClient.getRoom(payload.room_id)) { + // Queue the room instead of pushing it immediately. We're probably just + // waiting for a room join to complete. + this.waitingRooms.push({roomId: payload.room_id, addedTs: Date.now()}); + } else { + await this.appendRoom(this.matrixClient.getRoom(payload.room_id)); + } + } + } + + protected async onReady() { + await this.updateRooms(); + await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)}); + + this.matrixClient.on("Room.myMembership", this.onMyMembership); + this.matrixClient.on("Room", this.onRoom); + } + + protected async onNotReady() { + this.matrixClient.removeListener("Room.myMembership", this.onMyMembership); + this.matrixClient.removeListener("Room", this.onRoom); + } + + private onMyMembership = async (room: Room) => { + // We turn on breadcrumbs by default once the user has at least 1 room to show. + if (!this.state.enabled) { + await SettingsStore.setValue("breadcrumbs", null, SettingLevel.ACCOUNT, true); + } + }; + + private onRoom = async (room: Room) => { + const waitingRoom = this.waitingRooms.find(r => r.roomId === room.roomId); + if (!waitingRoom) return; + this.waitingRooms.splice(this.waitingRooms.indexOf(waitingRoom), 1); + + if ((Date.now() - waitingRoom.addedTs) > AUTOJOIN_WAIT_THRESHOLD_MS) return; // Too long ago. + await this.appendRoom(room); + }; + + private async updateRooms() { + let roomIds = SettingsStore.getValue("breadcrumb_rooms"); + if (!roomIds || roomIds.length === 0) roomIds = []; + + const rooms = roomIds.map(r => this.matrixClient.getRoom(r)).filter(r => !!r); + const currentRooms = this.state.rooms || []; + if (!arrayHasDiff(rooms, currentRooms)) return; // no change (probably echo) + await this.updateState({rooms}); + } + + private async appendRoom(room: Room) { + const rooms = this.state.rooms.slice(); // cheap clone + + // If the room is upgraded, use that room instead. We'll also splice out + // any children of the room. + const history = this.matrixClient.getRoomUpgradeHistory(room.roomId); + if (history.length > 1) { + room = history[history.length - 1]; // Last room is most recent in history + + // Take out any room that isn't the most recent room + for (let i = 0; i < history.length - 1; i++) { + const idx = rooms.findIndex(r => r.roomId === history[i].roomId); + if (idx !== -1) rooms.splice(idx, 1); + } + } + + // Remove the existing room, if it is present + const existingIdx = rooms.findIndex(r => r.roomId === room.roomId); + if (existingIdx !== -1) { + rooms.splice(existingIdx, 1); + } + + // Splice the room to the start of the list + rooms.splice(0, 0, room); + + if (rooms.length > MAX_ROOMS) { + // This looks weird, but it's saying to start at the MAX_ROOMS point in the + // list and delete everything after it. + rooms.splice(MAX_ROOMS, rooms.length - MAX_ROOMS); + } + + // Update the breadcrumbs + await this.updateState({rooms}); + const roomIds = rooms.map(r => r.roomId); + if (roomIds.length > 0) { + await SettingsStore.setValue("breadcrumb_rooms", null, SettingLevel.ACCOUNT, roomIds); + } + } + +} From 04566e12b202c50d10045fda65bdaf0e288bfecc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 17:14:40 -0600 Subject: [PATCH 086/181] Fix indentation in styles --- res/css/views/rooms/_RoomBreadcrumbs2.scss | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/res/css/views/rooms/_RoomBreadcrumbs2.scss b/res/css/views/rooms/_RoomBreadcrumbs2.scss index aa0b0ecb08..2db0fdca08 100644 --- a/res/css/views/rooms/_RoomBreadcrumbs2.scss +++ b/res/css/views/rooms/_RoomBreadcrumbs2.scss @@ -15,16 +15,16 @@ limitations under the License. */ @keyframes breadcrumb-popin { - 0% { - // Ideally we'd use `width` instead of `opacity`, but we only - // have 16 nanoseconds to render the frame, and width is expensive. - opacity: 0; - transform: scale(0); - } - 100% { - opacity: 1; - transform: scale(1); - } + 0% { + // Ideally we'd use `width` instead of `opacity`, but we only + // have 16 nanoseconds to render the frame, and width is expensive. + opacity: 0; + transform: scale(0); + } + 100% { + opacity: 1; + transform: scale(1); + } } .mx_RoomBreadcrumbs2 { From 8292ce56b0edda8fd44f23827d5d35c08c20314b Mon Sep 17 00:00:00 2001 From: "J. A. Durieux" Date: Mon, 8 Jun 2020 18:06:28 +0000 Subject: [PATCH 087/181] Translated using Weblate (Dutch) Currently translated at 90.1% (2036 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nl/ --- src/i18n/strings/nl.json | 49 ++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index b4bb5dc5bd..69909e942e 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -350,7 +350,7 @@ "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Zo te zien is uw e-mailadres op deze thuisserver niet aan een Matrix-ID gekoppeld.", "You seem to be in a call, are you sure you want to quit?": "Het ziet er naar uit dat u in gesprek bent, weet u zeker dat u wilt afsluiten?", "You seem to be uploading files, are you sure you want to quit?": "Het ziet er naar uit dat u bestanden aan het uploaden bent, weet u zeker dat u wilt afsluiten?", - "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "U kunt deze veranderingen niet ongedaan maken aangezien u de gebruiker tot hetzelfde niveau als uzelf promoveert.", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "U zult deze veranderingen niet terug kunnen draaien, daar u de gebruiker tot uw eigen niveau promoveert.", "This server does not support authentication with a phone number.": "Deze server biedt geen ondersteuning voor authenticatie met een telefoonnummer.", "An error occurred: %(error_string)s": "Er is een fout opgetreden: %(error_string)s", "Make Moderator": "Benoemen tot moderator", @@ -382,7 +382,7 @@ "Failed to invite": "Uitnodigen is mislukt", "Failed to invite the following users to the %(roomName)s room:": "Kon de volgende gebruikers niet uitnodigen voor gesprek %(roomName)s:", "Confirm Removal": "Verwijdering bevestigen", - "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Weet u zeker dat u deze gebeurtenis wilt verwijderen? Wees u er wel van bewust dat als u een gespreksnaam of onderwerpswijziging verwijdert, u de verandering mogelijk ongedaan maakt.", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Weet u zeker dat u deze gebeurtenis wilt verwijderen? Besef wel dat het verwijderen van een van een gespreksnaams- of onderwerpswijziging die wijziging mogelijk teniet doet.", "Unknown error": "Onbekende fout", "Incorrect password": "Onjuist wachtwoord", "To continue, please enter your password.": "Voer uw wachtwoord in om verder te gaan.", @@ -501,7 +501,7 @@ "Unban this user?": "Deze gebruiker ontbannen?", "Ban this user?": "Deze gebruiker verbannen?", "Mirror local video feed": "Lokale videoaanvoer ook elders opslaan (spiegelen)", - "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "U kunt deze actie niet ongedaan maken omdat u zichzelf degradeert. Als u de laatste bevoorrechte gebruiker in het gesprek bent, is het onmogelijk deze rechten terug te krijgen.", + "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Zelfdegradatie is onomkeerbaar. Als u de laatste bevoorrechte gebruiker in het gesprek bent zullen deze rechten voorgoed verloren gaan.", "Unignore": "Niet meer negeren", "Ignore": "Negeren", "Jump to read receipt": "Naar het laatst gelezen bericht gaan", @@ -952,7 +952,7 @@ "User %(userId)s is already in the room": "De gebruiker %(userId)s is al aanwezig", "User %(user_id)s does not exist": "Er bestaat geen gebruiker ‘%(user_id)s’", "User %(user_id)s may or may not exist": "Er bestaat mogelijk geen gebruiker ‘%(user_id)s’", - "The user must be unbanned before they can be invited.": "De gebruiker kan niet uitgenodigd worden voordat diens ban ongedaan is gemaakt.", + "The user must be unbanned before they can be invited.": "De gebruiker kan niet uitgenodigd worden voordat diens ban teniet is gedaan.", "Unknown server error": "Onbekende serverfout", "Use a few words, avoid common phrases": "Gebruik enkele woorden - maar geen bekende uitdrukkingen", "No need for symbols, digits, or uppercase letters": "Hoofdletters, cijfers of speciale tekens hoeven niet, mogen wel", @@ -1107,7 +1107,7 @@ "Language and region": "Taal en regio", "Theme": "Thema", "Account management": "Accountbeheer", - "Deactivating your account is a permanent action - be careful!": "Pas op! Het sluiten van uw account kan niet ongedaan gemaakt worden!", + "Deactivating your account is a permanent action - be careful!": "Pas op! Het sluiten van uw account is onherroepelijk!", "General": "Algemeen", "Legal": "Wettelijk", "Credits": "Met dank aan", @@ -1356,7 +1356,7 @@ "Adds a custom widget by URL to the room": "Voegt met een URL een aangepaste widget toe aan het gesprek", "Please supply a https:// or http:// widget URL": "Voer een https://- of http://-widget-URL in", "You cannot modify widgets in this room.": "U kunt de widgets in dit gesprek niet aanpassen.", - "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s heeft de uitnodiging voor %(targetDisplayName)s om toe te treden tot het gesprek ingetrokken.", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s heeft de uitnodiging aan %(targetDisplayName)s toe te treden tot het gesprek ingetrokken.", "Upgrade this room to the recommended room version": "Werk dit gesprek bij tot de aanbevolen versie", "This room is running room version , which this homeserver has marked as unstable.": "Dit gesprek draait op groepsgespreksversie , die door deze thuisserver als onstabiel is gemarkeerd.", "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Bijwerken zal de huidige versie van dit gesprek sluiten, en onder dezelfde naam een bijgewerkte versie starten.", @@ -1477,7 +1477,7 @@ "Cannot reach homeserver": "Kan thuisserver niet bereiken", "Ensure you have a stable internet connection, or get in touch with the server admin": "Zorg dat u een stabiele internetverbinding heeft, of neem contact op met de systeembeheerder", "Your Riot is misconfigured": "Uw Riot is onjuist geconfigureerd", - "Ask your Riot admin to check your config for incorrect or duplicate entries.": "Vraag uw Riot-beheerder om uw configuratie na te kijken op onjuiste of duplicate items.", + "Ask your Riot admin to check your config for incorrect or duplicate entries.": "Vraag uw Riot-beheerder uw configuratie na te kijken op onjuiste of dubbele items.", "Unexpected error resolving identity server configuration": "Onverwachte fout bij het oplossen van de identiteitsserverconfiguratie", "Use lowercase letters, numbers, dashes and underscores only": "Gebruik enkel letters, cijfers, streepjes en underscores", "Cannot reach identity server": "Kan identiteitsserver niet bereiken", @@ -1572,8 +1572,8 @@ "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Gebruik een identiteitsserver om uit te nodigen via e-mail. Klik op ‘Doorgaan’ om de standaardidentiteitsserver (%(defaultIdentityServerName)s) te gebruiken, of beheer de server in de instellingen.", "Use an identity server to invite by email. Manage in Settings.": "Gebruik een identiteitsserver om uit te nodigen via e-mail. Beheer de server in de instellingen.", "Multiple integration managers": "Meerdere integratiebeheerders", - "Send read receipts for messages (requires compatible homeserver to disable)": "Verstuur leesbevestigingen voor berichten (vereist compatibele thuisserver om uit te schakelen)", - "Accept to continue:": "Aanvaard om verder te gaan:", + "Send read receipts for messages (requires compatible homeserver to disable)": "Verstuur leesbevestigingen voor berichten (uitschakelen vereist een compatibele thuisserver)", + "Accept to continue:": "Aanvaard om door te gaan:", "ID": "ID", "Public Name": "Openbare naam", "Change identity server": "Identiteitsserver wisselen", @@ -1599,11 +1599,11 @@ "No recent messages by %(user)s found": "Geen recente berichten door %(user)s gevonden", "Try scrolling up in the timeline to see if there are any earlier ones.": "Probeer omhoog te scrollen in de tijdslijn om te kijken of er eerdere zijn.", "Remove recent messages by %(user)s": "Recente berichten door %(user)s verwijderen", - "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "U staat op het punt %(count)s berichten door %(user)s te verwijderen. Dit kan niet ongedaan worden gemaakt. Wilt u doorgaan?", + "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "U staat op het punt %(count)s berichten door %(user)s te verwijderen. Dit is onherroepelijk. Wilt u doorgaan?", "For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.": "Bij een groot aantal berichten kan dit even duren. Herlaad uw cliënt niet gedurende deze tijd.", "Remove %(count)s messages|other": "%(count)s berichten verwijderen", "Deactivate user?": "Gebruiker deactiveren?", - "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Deze gebruiker deactiveren zal hem/haar afmelden en verhinderen dat hij/zij zich weer aanmeldt. Bovendien zal hij/zij alle gesprekken waaraan hij/zij deelneemt verlaten. Deze actie kan niet ongedaan worden gemaakt. Weet u zeker dat u deze gebruiker wilt deactiveren?", + "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Deze gebruiker deactiveren zal hem/haar afmelden en verhinderen dat hij/zij zich weer aanmeldt. Bovendien zal hij/zij alle gesprekken waaraan hij/zij deelneemt verlaten. Deze actie is onherroepelijk. Weet u zeker dat u deze gebruiker wilt deactiveren?", "Deactivate user": "Gebruiker deactiveren", "Remove recent messages": "Recente berichten verwijderen", "Bold": "Vet", @@ -1638,7 +1638,7 @@ "Explore rooms": "Gesprekken ontdekken", "Show previews/thumbnails for images": "Toon voorbeelden voor afbeeldingen", "Clear cache and reload": "Cache wissen en herladen", - "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|one": "U staat op het punt 1 bericht door %(user)s te verwijderen. Dit kan niet ongedaan gemaakt worden. Wilt u doorgaan?", + "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|one": "U staat op het punt 1 bericht door %(user)s te verwijderen. Dit is onherroepelijk. Wilt u doorgaan?", "Remove %(count)s messages|one": "1 bericht verwijderen", "%(count)s unread messages including mentions.|other": "%(count)s ongelezen berichten, inclusief vermeldingen.", "%(count)s unread messages.|other": "%(count)s ongelezen berichten.", @@ -1791,7 +1791,7 @@ "Compare unique emoji": "Vergelijk unieke emoji", "Compare a unique set of emoji if you don't have a camera on either device": "Vergelijk een unieke lijst met emoji als geen van beide apparaten een camera heeft", "Start": "Start", - "Securely cache encrypted messages locally for them to appear in search results.": "Sla versleutelde berichten beveiligd op om ze weer te geven in zoekresultaten.", + "Securely cache encrypted messages locally for them to appear in search results.": "Sla versleutelde berichten veilig lokaal op om ze doorzoekbaar te maken.", "Enable": "Inschakelen", "Connecting to integration manager...": "Verbinding maken met de integratiebeheerder…", "Cannot connect to integration manager": "Kan geen verbinding maken met de integratiebeheerder", @@ -1873,7 +1873,7 @@ "in secret storage": "in de sleutelopslag", "Secret storage public key:": "Sleutelopslag publieke sleutel:", "in account data": "in accountinformatie", - "Securely cache encrypted messages locally for them to appear in search results, using ": "Sla versleutelde berichten beveiligd op om ze weer te geven in de zoekresultaten, door gebruik te maken van ", + "Securely cache encrypted messages locally for them to appear in search results, using ": "Sla versleutelde berichten veilig lokaal op opdat ze doorzocht kunnen worden, middels ", " to store messages from ": " om berichten op te slaan van ", "Manage": "Beheren", "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Verbind deze sessie met de sleutelback-up voordat u zich afmeldt. Dit voorkomt dat u sleutels verliest die alleen op deze sessie voorkomen.", @@ -1905,8 +1905,8 @@ "Show rooms with unread notifications first": "Gesprekken met ongelezen meldingen eerst tonen", "Show shortcuts to recently viewed rooms above the room list": "Snelkoppelingen naar de gesprekken die u recent heeft bekeken bovenaan de gesprekslijst weergeven", "Cancelling…": "Bezig met annuleren…", - "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 beschikt niet over alle onderdelen die nodig zijn om versleutelde berichten veilig in het lokale cachegeheugen te bewaren. Als u deze functie wilt uittesten, kunt u een aangepaste versie van Riot Desktop compileren, waarbij de zoekonderdelen toegevoegd zijn.", - "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 kan versleutelde berichten niet veilig bewaren in het lokale cachegeheugen wanneer het uitgevoerd wordt in een webbrowser. Gebruik Riot Desktop om versleutelde berichten in de zoekresultaten te laten verschijnen.", + "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.": "In Riot ontbreken enige modulen vereist voor het veilig lokaal bewaren van versleutelde berichten. Wilt u deze functie uittesten, compileer dan een aangepaste versie van Riot Desktop die de zoekmodulen bevat.", + "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.": "Als Riot in een webbrowser draait kan het versleutelde berichten niet veilig lokaal bewaren. Gebruik Riot Desktop om versleutelde berichten doorzoekbaar te maken.", "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Deze sessie maakt geen back-ups van uw sleutels, maar u beschikt over een reeds bestaande back-up waaruit u kunt herstellen en waaraan u nieuwe sleutels vanaf nu kunt toevoegen.", "Backup key stored in secret storage, but this feature is not enabled on this session. Please enable cross-signing in Labs to modify key backup state.": "Er is een back-upsleutel opgeslagen in de geheime opslag, maar deze functie is niet ingeschakeld voor deze sessie. Schakel kruiselings ondertekenen in in de experimentele instellingen om de sleutelback-upstatus te wijzigen.", "Customise your experience with experimental labs features. Learn more.": "Personaliseer uw ervaring met experimentele functies. Klik hier voor meer informatie.", @@ -1952,7 +1952,7 @@ "Yours, or the other users’ session": "De sessie van uzelf of de andere gebruiker", "Not Trusted": "Niet vertrouwd", "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s%(userId)s heeft zich aangemeld bij een nieuwe sessie zonder deze te verifiëren:", - "Ask this user to verify their session, or manually verify it below.": "Vraag deze gebruiker om zijn/haar sessie te verifiëren, of verifieer deze hieronder handmatig.", + "Ask this user to verify their session, or manually verify it below.": "Vraag deze gebruiker haar/zijn sessie te verifiëren, of verifieer die hieronder handmatig.", "Done": "Klaar", "Manually Verify": "Handmatig verifiëren", "Trusted": "Vertrouwd", @@ -2028,10 +2028,10 @@ "More options": "Meer opties", "Language Dropdown": "Taalselectie", "Destroy cross-signing keys?": "Sleutels voor kruiselings ondertekenen verwijderen?", - "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Het verwijderen van sleutels voor kruiselings ondertekenen kan niet ongedaan gemaakt worden. Iedereen waarmee u geverifieerd heeft zal beveiligingswaarschuwingen te zien krijgen. U wilt dit hoogstwaarschijnlijk niet doen, tenzij u alle apparaten heeft verloren waarmee u kruiselings kon ondertekenen.", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.": "Het verwijderen van sleutels voor kruiselings ondertekenen is onherroepelijk. Iedereen waarmee u geverifieerd heeft zal beveiligingswaarschuwingen te zien krijgen. U wilt dit hoogstwaarschijnlijk niet doen, tenzij u alle apparaten heeft verloren waarmee u kruiselings kon ondertekenen.", "Clear cross-signing keys": "Sleutels voor kruiselings ondertekenen wissen", "Clear all data in this session?": "Alle gegevens in deze sessie verwijderen?", - "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Het verwijderen van alle gegevens in deze sessie kan niet ongedaan gemaakt worden. Versleutelde berichten zullen verloren gaan, tenzij u een back-up van hun sleutels heeft.", + "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Het verwijderen van alle gegevens in deze sessie is onherroepelijk. Versleutelde berichten zullen verloren gaan, tenzij u een back-up van hun sleutels heeft.", "Verify session": "Sessie verifiëren", "To verify that this session can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Controleer of de sleutel in de gebruikersinstellingen op het apparaat overeenkomt met onderstaande sleutel om te verifiëren dat de sessie vertrouwd kan worden:", "To verify that this session can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this session matches the key below:": "Neem contact op met de eigenaar op een andere manier (bv. onder vier ogen of met een telefoongesprek) en vraag of de sleutel in zijn/haar gebruikersinstellingen overeenkomt met onderstaande sleutel om te verifiëren dat de sessie vertrouwd kan worden:", @@ -2209,5 +2209,14 @@ "Verify the new login accessing your account: %(name)s": "Verifieer de nieuwe aanmelding op uw account: %(name)s", "Confirm your account deactivation by using Single Sign On to prove your identity.": "Bevestig uw intentie deze account te sluiten door met Single Sign On uw identiteit te bewijzen.", "Are you sure you want to deactivate your account? This is irreversible.": "Weet u zeker dat u uw account wil sluiten? Dit is onomkeerbaar.", - "Confirm account deactivation": "Bevestig accountsluiting" + "Confirm account deactivation": "Bevestig accountsluiting", + "Room name or address": "Gespreksnaam of -adres", + "Joins room with given address": "Treedt tot het gesprek met het opgegeven adres toe", + "Unrecognised room address:": "Gespreksadres niet herkend:", + "Help us improve Riot": "Help ons Riot nog beter te maken", + "Send anonymous usage data which helps us improve Riot. This will use a cookie.": "Stuur anonieme gebruiksinformatie waarmee we Riot kunnen verbeteren. Dit plaatst een cookie.", + "I want to help": "Ik wil helpen", + "Your homeserver has exceeded its user limit.": "Uw thuisserver heeft het maximaal aantal gebruikers overschreden.", + "Your homeserver has exceeded one of its resource limits.": "Uw thuisserver heeft een van zijn limieten overschreden.", + "Ok": "Oké" } From 0e6e76ad85fa70b3c56438ab83492c9634282136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 8 Jun 2020 15:31:40 +0000 Subject: [PATCH 088/181] Translated using Weblate (Estonian) Currently translated at 58.1% (1312 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 0fdcca40d1..5ffcd11f16 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1374,5 +1374,13 @@ "Sounds": "Helid", "Notification sound": "Teavitusheli", "Reset": "Taasta algolek", - "Set a new custom sound": "Seadista uus kohandatud heli" + "Set a new custom sound": "Seadista uus kohandatud heli", + "Jump to message": "Mine sõnumi juurde", + "were invited %(count)s times|other": "said kutse %(count)s korda", + "were invited %(count)s times|one": "said kutse", + "was invited %(count)s times|other": "sai kutse %(count)s korda", + "was invited %(count)s times|one": "sai kutse", + "%(severalUsers)schanged their name %(count)s times|other": "Mitu kasutajat %(severalUsers)s muutsid oma nime %(count)s korda", + "%(severalUsers)schanged their name %(count)s times|one": "Mitu kasutajat %(severalUsers)s muutsid oma nime", + "%(oneUser)schanged their name %(count)s times|other": "Kasutaja %(oneUser)s muutis oma nime %(count)s korda" } From 87d5988d53faaf650aa89eece7898762b4b050f3 Mon Sep 17 00:00:00 2001 From: Nils Haugen Date: Mon, 8 Jun 2020 15:52:42 +0000 Subject: [PATCH 089/181] Translated using Weblate (Norwegian Nynorsk) Currently translated at 58.5% (1322 of 2259 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nn/ --- src/i18n/strings/nn.json | 73 +++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/src/i18n/strings/nn.json b/src/i18n/strings/nn.json index 52b2c2e83d..8a5407967a 100644 --- a/src/i18n/strings/nn.json +++ b/src/i18n/strings/nn.json @@ -919,7 +919,7 @@ "Sign In": "Logg inn", "Explore rooms": "Utforsk romma", "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldinga di vart ikkje send, for denne heimetenaren har nådd grensa for maksimalt aktive brukarar pr. månad. Kontakt systemadministratoren for å vidare nytte denne tenesta.", - "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Denne meldingen vart ikkje sendt fordi heimeserveren har nådd grensa for tilgjengelege systemressursar. Kontakt systemadministratoren for å vidare nytte denne tenesten.", + "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Denne meldingen vart ikkje send fordi heimetenaren har nådd grensa for tilgjengelege systemressursar. Kontakt systemadministratoren for å vidare nytta denne tenesta.", "Add room": "Legg til rom", "You have %(count)s unread notifications in a prior version of this room.|other": "Du har %(count)s uleste varslingar i ein tidligare versjon av dette rommet.", "You have %(count)s unread notifications in a prior version of this room.|one": "Du har %(count)s ulest varsel i ein tidligare versjon av dette rommet.", @@ -947,7 +947,7 @@ "Failed to perform homeserver discovery": "Fekk ikkje til å utforska heimetenaren", "Sign in with single sign-on": "Logg på med Single-Sign-On", "Create account": "Lag konto", - "Registration has been disabled on this homeserver.": "Registrering er deaktivert på denne heimeserveren.", + "Registration has been disabled on this homeserver.": "Registrering er skrudd av for denne heimetenaren.", "Unable to query for supported registration methods.": "Klarte ikkje å spørre etter støtta registreringsmetodar.", "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Kontoen din %(newAccountId)s er no registrert, men du er frå tidligare logga på med ein annan konto (%(loggedInUserId)s).", "Continue with previous account": "Fortsett med tidligare konto", @@ -1007,7 +1007,7 @@ "Add Email Address": "Legg til e-postadresse", "Add Phone Number": "Legg til telefonnummer", "Call failed due to misconfigured server": "Kallet gjekk gale fordi tenaren er oppsatt feil", - "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Spør administratoren for din heimetenar%(homeserverDomain)s om å setje opp ein \"TURN-server\" slik at heimetenaren svarar korrekt.", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Spør administratoren for din heimetenar%(homeserverDomain)s om å setje opp ein \"TURN-server\" slik at talesamtalar fungerer på rett måte.", "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativt, kan du prøva å nytta den offentlege tenaren på turn.matrix.org, men det kan vera mindre stabilt og IP-adressa di vil bli delt med den tenaren. Du kan og endra på det under Innstillingar.", "Try using turn.matrix.org": "Prøv med å nytta turn.matrix.org", "A conference call could not be started because the integrations server is not available": "Ein konferansesamtale kunne ikkje starta fordi integrasjons-tenaren er utilgjengeleg", @@ -1063,12 +1063,12 @@ "Set up encryption": "Sett opp kryptering", "Unverified session": "Uverifisert sesjon", "Identity server has no terms of service": "Identitetstenaren manglar bruksvilkår", - "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Denne handlinga krev kommunikasjon mot (standard identitetsserver) for å verifisere e-post eller telefonnummer, men serveren manglar bruksvilkår.", - "Only continue if you trust the owner of the server.": "Gå vidare så lenge du har tillit til eigar av serveren.", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Denne handlinga krev kommunikasjon mot (standard identitetstenar) for å verifisere e-post eller telefonnummer, men tenaren manglar bruksvilkår.", + "Only continue if you trust the owner of the server.": "Gå vidare så lenge du har tillit til eigar av tenaren.", "Trust": "Tillat", "Custom (%(level)s)": "Tilpassa (%(level)s)", "Error upgrading room": "Feil ved oppgradering av rom", - "Double check that your server supports the room version chosen and try again.": "Sjekk at server støttar romversjon, og prøv på nytt.", + "Double check that your server supports the room version chosen and try again.": "Sjekk at tenar støttar denne romversjonen, og prøv på nytt.", "Verifies a user, session, and pubkey tuple": "Verifiser brukar, økt eller public-key objekt (pubkey tuple)", "Unknown (user, session) pair:": "Ukjent (brukar,økt) par:", "Session already verified!": "Sesjon er tidligare verifisert!", @@ -1183,7 +1183,7 @@ "You're previewing %(roomName)s. Want to join it?": "Du førehandsviser %(roomName)s. Ynskjer du å bli med ?", "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s kan ikkje førehandsvisast. Ynskjer du å bli med ?", "This room doesn't exist. Are you sure you're at the right place?": "Dette rommet eksisterar ikkje. Er du sikker på at du er på rett plass?", - "Try again later, or ask a room admin to check if you have access.": "Prøv igjen seinare, eller spør ein rom-administrator om du har tilgang.", + "Try again later, or ask a room admin to check if you have access.": "Prøv om att seinare, eller spør ein rom-administrator om du har tilgang.", "Never lose encrypted messages": "Aldri la krypterte meldingar gå tapt", "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Meldingane i rommet er sikra med ende-til-ende kryptering. Berre du og mottakarane har krypteringsnøklane for desse meldingane.", "Securely back up your keys to avoid losing them. Learn more.": "Kopier nøklane dine for å unngå i miste dei. Les meir.", @@ -1203,7 +1203,7 @@ "Mark all as read": "Merk alle som lesne", "Error updating main address": "Feil under oppdatering av hovedadresse", "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Det skjedde ein feil under oppdatering av hovudadressa for rommet. Det kan hende at dette er ein mellombels feil, eller at det ikkje er tillate på tenaren.", - "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Feil under oppdatering av sekundæradresse. Det kan hende at dette er midlertidig, eller at det ikkje er tillate på serveren.", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Feil under oppdatering av alternativ adresse. Det kan hende at dette er mellombels, eller at det ikkje er tillate på tenaren.", "Error creating alias": "Feil under oppretting av alias", "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "Det skjedde ein feil under oppretting av dette aliaset. Det kan hende at dette er midlertidig, eller at det ikkje er tillate på serveren.", "You don't have permission to delete the alias.": "Du har ikkje lov å slette aliaset.", @@ -1219,7 +1219,7 @@ "Local Addresses": "Lokale adresser", "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Sett adresse for dette rommet, slik at brukarar kan finne rommet på din heimetenar (%(localDomain)s)", "Error updating flair": "Oppdatering av etikett gjekk gale", - "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Feil under oppdatering av etikett for dette rommet. Dette kan vere deaktivert på server , eller så oppstod det ein feil.", + "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Feil under oppdatering av etikett for dette rommet. Dette kan vere deaktivert på tenaren, eller så oppstod det ein feil.", "Room Name": "Romnamn", "Room Topic": "Romemne", "Room avatar": "Rom-avatar", @@ -1339,7 +1339,7 @@ "Keyboard Shortcuts": "Tastatursnarvegar", "Ignored users": "Ignorerte brukarar", "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Legg til brukarar og tenarar du vil ignorera her. Bruk stjerne/wildcard (*) for at markere eitkvart teikn. Til dømes, @bot* vil ignorere alle brukarar med namn 'bot' på uansett tenar.", - "Server or user ID to ignore": "Server eller brukar-ID for å ignorere", + "Server or user ID to ignore": "Tenar eller brukar-ID for å ignorere", "If this isn't what you want, please use a different tool to ignore users.": "Om det ikkje var dette du ville, bruk eit anna verktøy til å ignorera brukarar.", "Enter the name of a new server you want to explore.": "Skriv inn namn på ny tenar du ynskjer å utforske.", "Matrix rooms": "Matrix-rom", @@ -1363,7 +1363,7 @@ "Help": "Hjelp", "Explore": "Utforsk", "%(creator)s created and configured the room.": "%(creator)s oppretta og konfiguerte dette rommet.", - "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot klarde ikkje å hente protokolllister frå heimeserveren. Det kan hende at serveren er for gammal til å støtte tredjeparts-nettverk", + "Riot failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "Riot klarde ikkje å hente protokolllister frå heimetenaren. Det kan hende at tenaren er for gammal til å støtte tredjeparts-nettverk", "The homeserver may be unavailable or overloaded.": "Heimetenaren kan vere overlasta eller utilgjengeleg.", "Preview": "Førehandsvis", "View": "Vis", @@ -1409,5 +1409,54 @@ "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s tok vekk den alternative adressa %(addresses)s for dette rommet.", "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s endre den alternative adressa for dette rommet.", "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s endra hovud- og alternativ-adressene for dette rommet.", - "%(senderName)s changed the addresses for this room.": "%(senderName)s endre adressene for dette rommet." + "%(senderName)s changed the addresses for this room.": "%(senderName)s endre adressene for dette rommet.", + "Review where you’re logged in": "Sjå over kvar du er logga inn", + "Later": "Seinare", + "Allow Peer-to-Peer for 1:1 calls": "Tillat peer-to-peer (P2P) for ein-til-ein samtalar", + "Never send encrypted messages to unverified sessions from this session": "Aldri send krypterte meldingar til ikkje-verifiserte sesjonar frå denne sesjonen", + "Never send encrypted messages to unverified sessions in this room from this session": "Aldri send krypterte meldingar i dette rommet til ikkje-verifiserte sesjonar frå denne sesjonen", + "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Tillat å bruke assistansetenaren turn.matrix.org for talesamtalar viss heimetenaren din ikkje tilbyr dette (IP-adressa di vil bli delt under talesamtalen)", + "Enable message search in encrypted rooms": "Aktiver søk etter meldingar i krypterte rom", + "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Sikre meldingar med denne brukaren er ende-til-ende krypterte og kan ikkje lesast av tredjepart.", + "Public Name": "Offentleg namn", + "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Er du sikker? Alle dine krypterte meldingar vil gå tapt viss nøklane dine ikkje er sikkerheitskopierte.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Krypterte meldingar er sikra med ende-til-ende kryptering. Berre du og mottakar(ane) har nøklane for å lese desse meldingane.", + "wait and try again later": "vent og prøv om att seinare", + "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.": "For å rapportere eit Matrix-relatert sikkerheitsproblem, les Matrix.org sin Security Disclosure Policy.", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Tenaradministratoren din har deaktivert ende-til-ende kryptering som standard i direktemeldingar og private rom.", + "Where you’re logged in": "Stader du er innlogga", + "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Handter namn på eller logg ut av sesjonane dine nedanfor eller verifiser dei under brukarprofilen din.", + "A session's public name is visible to people you communicate with": "Ein sesjon sitt offentlege namn, er synleg for andre du kommuniserar med", + "Reset": "Nullstill", + "Set a new custom sound": "Set ein ny tilpassa lyd", + "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "Når kryptering er aktivert for eit rom, kan ein ikkje deaktivere det. Meldingar som blir sende i eit kryptert rom kan ikkje bli lesne av tenaren, men berre av deltakarane i rommet. Aktivering av kryptering kan hindre mange botar og bruer frå å fungera på rett måte. Les meir om kryptering her.", + "Encrypted": "Kryptert", + "This room is end-to-end encrypted": "Dette rommet er ende-til-ende kryptert", + "Encrypted by an unverified session": "Kryptert av ein ikkje-verifisert sesjon", + "Encrypted by a deleted session": "Kryptert av ein sletta sesjon", + "Create room": "Lag rom", + "Messages in this room are end-to-end encrypted.": "Meldingar i dette rommet er ende-til-ende kryptert.", + "Messages in this room are not end-to-end encrypted.": "Meldingar i dette rommet er ikkje ende-til-ende kryptert.", + "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.": "Når du nyttar krypterte rom er meldingane din sikra. Berre du og mottakaren har unike nøklar som kan gjere meldingane lesbare.", + "This client does not support end-to-end encryption.": "Denne klienten støttar ikkje ende-til-ende kryptering.", + "In encrypted rooms, verify all users to ensure it’s secure.": "Når du nyttar krypterte rom, verifiser alle brukarar for å vere trygg på at det er sikkert.", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Meldingar i dette rommet er ende-til-ende krypterte. Meir om dette, samt verifisering av denne brukaren finn du under deira brukarprofil.", + "Join": "Bli med", + "Remove server": "Ta vekk tenar", + "Matrix": "Matrix", + "Add a new server": "Legg til ein ny tenar", + "Add a new server...": "Legg til ein ny tenar", + "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Tømming av data frå denne sesjonen er permanent. Krypterte meldingar vil gå tapt med mindre krypteringsnøklane har blitt sikkerheitskopierte.", + "This room is private, and can only be joined by invitation.": "Dette rommet er privat, brukarar kan berre bli med viss dei har ein invitasjon", + "You can’t disable this later. Bridges & most bots won’t work yet.": "Du kan ikkje skru av dette seinare. Bruer og dei fleste botar vil ikkje fungere enno.", + "Enable end-to-end encryption": "Skru på ende-til-ende kryptering", + "Create a private room": "Lag eit privat rom", + "Make this room public": "Gjer dette rommet offentleg", + "Hide advanced": "Gøym avanserte alternativ", + "Show advanced": "Vis avanserte alternativ", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Utesteng brukarar på andre Matrix heimetenarar frå å koma inn i rommet (Dette kan endrast seinare!)", + "I don't want my encrypted messages": "Eg treng ikkje mine krypterte meldingar", + "You'll lose access to your encrypted messages": "Du vil miste tilgangen til dine krypterte meldingar", + "No identity server is configured so you cannot add an email address in order to reset your password in the future.": "Ingen identitetstenar er konfiguert, så i framtida kan ikkje legge til ei e-postadresse for å nullstille passordet.", + "Join millions for free on the largest public server": "Kom ihop med millionar av andre på den største offentlege tenaren" } From eff97e6c205ecdcce0bc40714fe51ca6f4e6d960 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 18:18:34 -0600 Subject: [PATCH 090/181] Fix the tests --- src/stores/BreadcrumbsStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index 783b38e62f..5944091d00 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -113,7 +113,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } private async appendRoom(room: Room) { - const rooms = this.state.rooms.slice(); // cheap clone + const rooms = (this.state.rooms || []).slice(); // cheap clone // If the room is upgraded, use that room instead. We'll also splice out // any children of the room. From 5083811deb8a2a1fc53acd331c667a0926bc5bce Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 18:26:43 -0600 Subject: [PATCH 091/181] Appease the tests --- src/stores/BreadcrumbsStore.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index 5944091d00..f0f2dad91b 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -68,7 +68,9 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { // waiting for a room join to complete. this.waitingRooms.push({roomId: payload.room_id, addedTs: Date.now()}); } else { - await this.appendRoom(this.matrixClient.getRoom(payload.room_id)); + // The tests might not result in a valid room object. + const room = this.matrixClient.getRoom(payload.room_id); + if (room) await this.appendRoom(room); } } } From 708c65cd965f9da3fd8ada635a71a116d710b292 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 19:08:18 -0600 Subject: [PATCH 092/181] Disable new breadcrumb store when old room list is in use --- src/stores/BreadcrumbsStore.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index f0f2dad91b..332fa7fe2e 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -20,6 +20,7 @@ import { ActionPayload } from "../dispatcher/payloads"; import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; import defaultDispatcher from "../dispatcher/dispatcher"; import { arrayHasDiff } from "../utils/arrays"; +import { RoomListStoreTempProxy } from "./room-list/RoomListStoreTempProxy"; const MAX_ROOMS = 20; // arbitrary const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up @@ -56,6 +57,9 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { protected async onAction(payload: ActionPayload) { if (!this.matrixClient) return; + // TODO: Remove when new room list is made the default + if (!RoomListStoreTempProxy.isUsingNewStore()) return; + if (payload.action === 'setting_updated') { if (payload.settingName === 'breadcrumb_rooms') { await this.updateRooms(); @@ -76,6 +80,9 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } protected async onReady() { + // TODO: Remove when new room list is made the default + if (!RoomListStoreTempProxy.isUsingNewStore()) return; + await this.updateRooms(); await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)}); @@ -84,6 +91,9 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } protected async onNotReady() { + // TODO: Remove when new room list is made the default + if (!RoomListStoreTempProxy.isUsingNewStore()) return; + this.matrixClient.removeListener("Room.myMembership", this.onMyMembership); this.matrixClient.removeListener("Room", this.onRoom); } From 5114c37b82374429c7a7cb1f5889efbd2b1651d7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Jun 2020 20:33:21 -0600 Subject: [PATCH 093/181] Add filtering and exploring to the new room list This is per the designs. Animation doesn't feel required here. Like the rest of this series, this rewrites a component to be more purpose-built to help match the designs and to solve the smallest possible problem. --- res/css/_components.scss | 1 + res/css/structures/_LeftPanel2.scss | 39 ++++- res/css/structures/_RoomSearch.scss | 70 +++++++++ res/img/feather-customised/compass.svg | 1 + src/components/structures/HomePage.tsx | 3 +- src/components/structures/LeftPanel.js | 2 +- src/components/structures/LeftPanel2.tsx | 61 +++----- src/components/structures/MatrixChat.tsx | 6 +- src/components/structures/RoomSearch.tsx | 143 ++++++++++++++++++ src/components/structures/RoomView.js | 4 +- .../views/elements/RoomDirectoryButton.js | 3 +- src/dispatcher/actions.ts | 5 + 12 files changed, 287 insertions(+), 51 deletions(-) create mode 100644 res/css/structures/_RoomSearch.scss create mode 100644 res/img/feather-customised/compass.svg create mode 100644 src/components/structures/RoomSearch.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index de4c1c677c..66af2ba00f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -19,6 +19,7 @@ @import "./structures/_NotificationPanel.scss"; @import "./structures/_RightPanel.scss"; @import "./structures/_RoomDirectory.scss"; +@import "./structures/_RoomSearch.scss"; @import "./structures/_RoomStatusBar.scss"; @import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index d335df305f..d9a2b1dd5c 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -88,7 +88,44 @@ $roomListMinimizedWidth: 50px; } .mx_LeftPanel2_filterContainer { - // TODO: Improve CSS for filtering and its input + margin-left: 12px; + margin-right: 12px; + + // Create a flexbox to organize the inputs + display: flex; + align-items: center; + + .mx_RoomSearch_expanded + .mx_LeftPanel2_exploreButton { + // Cheaty way to return the occupied space to the filter input + margin: 0; + width: 0; + + // Don't forget to hide the masked ::before icon + visibility: hidden; + } + + .mx_LeftPanel2_exploreButton { + width: 28px; + height: 28px; + border-radius: 20px; + background-color: #fff; // TODO: Variable and theme + position: relative; + margin-left: 8px; + + &::before { + content: ''; + position: absolute; + top: 6px; + left: 6px; + width: 16px; + height: 16px; + mask-image: url('$(res)/img/feather-customised/compass.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } } .mx_LeftPanel2_actualRoomListContainer { diff --git a/res/css/structures/_RoomSearch.scss b/res/css/structures/_RoomSearch.scss new file mode 100644 index 0000000000..d078031090 --- /dev/null +++ b/res/css/structures/_RoomSearch.scss @@ -0,0 +1,70 @@ +/* +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. +*/ + +// Note: this component expects to be contained within a flexbox +.mx_RoomSearch { + flex: 1; + border-radius: 20px; + background-color: #fff; // TODO: Variable & theme + height: 26px; + padding: 2px; + + // Create a flexbox for the icons (easier to manage) + display: flex; + align-items: center; + + .mx_RoomSearch_icon { + width: 16px; + height: 16px; + mask: url('$(res)/img/feather-customised/search-input.svg'); + mask-repeat: no-repeat; + background: $primary-fg-color; + margin-left: 7px; + } + + .mx_RoomSearch_input { + border: none !important; // !important to override default app-wide styles + flex: 1 !important; // !important to override default app-wide styles + color: $primary-fg-color !important; // !important to override default app-wide styles + padding: 0; + height: 100%; + width: 100%; + font-size: $font-12px; + line-height: $font-16px; + + &:not(.mx_RoomSearch_inputExpanded)::placeholder { + color: $primary-fg-color !important; // !important to override default app-wide styles + } + } + + &.mx_RoomSearch_expanded { + .mx_RoomSearch_clearButton { + width: 16px; + height: 16px; + mask-image: url('$(res)/img/feather-customised/x.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + margin-right: 8px; + } + } + + .mx_RoomSearch_clearButton { + width: 0; + height: 0; + } +} diff --git a/res/img/feather-customised/compass.svg b/res/img/feather-customised/compass.svg new file mode 100644 index 0000000000..3296260803 --- /dev/null +++ b/res/img/feather-customised/compass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index ff8d35a114..209f219598 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -22,9 +22,10 @@ import { _t } from "../../languageHandler"; import SdkConfig from "../../SdkConfig"; import * as sdk from "../../index"; import dis from "../../dispatcher/dispatcher"; +import { Action } from "../../dispatcher/actions"; const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'}); -const onClickExplore = () => dis.dispatch({action: 'view_room_directory'}); +const onClickExplore = () => dis.fire(Action.ViewRoomDirectory); const onClickNewRoom = () => dis.dispatch({action: 'view_create_room'}); const HomePage = () => { diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index 05cd97df2a..bae69b5631 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -252,7 +252,7 @@ const LeftPanel = createReactClass({ if (!this.props.collapsed) { exploreButton = (
    - dis.dispatch({action: 'view_room_directory'})}>{_t("Explore")} + dis.fire(Action.ViewRoomDirectory)}>{_t("Explore")}
    ); } diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 2fd8612ff5..f417dc99b1 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -18,16 +18,16 @@ import * as React from "react"; import TagPanel from "./TagPanel"; import classNames from "classnames"; import dis from "../../dispatcher/dispatcher"; -import AccessibleButton from "../views/elements/AccessibleButton"; import { _t } from "../../languageHandler"; import SearchBox from "./SearchBox"; import RoomList2 from "../views/rooms/RoomList2"; -import TopLeftMenuButton from "./TopLeftMenuButton"; import { Action } from "../../dispatcher/actions"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import BaseAvatar from '../views/avatars/BaseAvatar'; import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; import UserMenuButton from "./UserMenuButton"; +import RoomSearch from "./RoomSearch"; +import AccessibleButton from "../views/elements/AccessibleButton"; /******************************************************************* * CAUTION * @@ -42,7 +42,6 @@ interface IProps { } interface IState { - searchExpanded: boolean; searchFilter: string; // TODO: Move search into room list? } @@ -58,7 +57,6 @@ export default class LeftPanel2 extends React.Component { super(props); this.state = { - searchExpanded: false, searchFilter: "", }; } @@ -67,24 +65,10 @@ export default class LeftPanel2 extends React.Component { this.setState({searchFilter: term}); }; - private onSearchCleared = (source: string): void => { - if (source === "keyboard") { - dis.fire(Action.FocusComposer); - } - this.setState({searchExpanded: false}); - } - - private onSearchFocus = (): void => { - this.setState({searchExpanded: true}); + private onExplore = () => { + dis.fire(Action.ViewRoomDirectory); }; - private onSearchBlur = (event: FocusEvent): void => { - const target = event.target as HTMLInputElement; - if (target.value.length === 0) { - this.setState({searchExpanded: false}); - } - } - private renderHeader(): React.ReactNode { // TODO: Update when profile info changes // TODO: Presence @@ -126,6 +110,22 @@ export default class LeftPanel2 extends React.Component { ); } + private renderSearchExplore(): React.ReactNode { + // TODO: Collapsed support + + return ( +
    + + +
    + ); + } + public render(): React.ReactNode { const tagPanel = (
    @@ -133,18 +133,6 @@ export default class LeftPanel2 extends React.Component {
    ); - const searchBox = ( {/*TODO*/}} - onSearch={this.onSearch} - onCleared={this.onSearchCleared} - onFocus={this.onSearchFocus} - onBlur={this.onSearchBlur} - collapsed={false}/>); // TODO: Collapsed support - // TODO: Improve props for RoomList2 const roomList = {/*TODO*/}} @@ -167,14 +155,7 @@ export default class LeftPanel2 extends React.Component { {tagPanel}