From 344185a3758eee109e9725581291bdaa78993a97 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 16:34:48 +0530 Subject: [PATCH 001/136] Translate right panel stuff to ts Add actions for right panel --- .../{HeaderButton.js => HeaderButton.tsx} | 41 +++--- .../{HeaderButtons.js => HeaderButtons.tsx} | 52 +++++--- src/dispatcher/actions.ts | 15 +++ .../AfterRightPanelPhaseChangePayload.ts | 28 ++++ .../payloads/SetRightPanelPhasePayload.ts | 36 ++++++ .../payloads/ToggleRightPanelPayload.ts | 27 ++++ ...{RightPanelStore.js => RightPanelStore.ts} | 121 ++++++++++-------- ...torePhases.js => RightPanelStorePhases.ts} | 34 ++--- 8 files changed, 247 insertions(+), 107 deletions(-) rename src/components/views/right_panel/{HeaderButton.js => HeaderButton.tsx} (79%) rename src/components/views/right_panel/{HeaderButtons.js => HeaderButtons.tsx} (57%) create mode 100644 src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts create mode 100644 src/dispatcher/payloads/SetRightPanelPhasePayload.ts create mode 100644 src/dispatcher/payloads/ToggleRightPanelPayload.ts rename src/stores/{RightPanelStore.js => RightPanelStore.ts} (59%) rename src/stores/{RightPanelStorePhases.js => RightPanelStorePhases.ts} (57%) diff --git a/src/components/views/right_panel/HeaderButton.js b/src/components/views/right_panel/HeaderButton.tsx similarity index 79% rename from src/components/views/right_panel/HeaderButton.js rename to src/components/views/right_panel/HeaderButton.tsx index 2cfc060bba..a64ed0a980 100644 --- a/src/components/views/right_panel/HeaderButton.js +++ b/src/components/views/right_panel/HeaderButton.tsx @@ -19,18 +19,33 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import Analytics from '../../../Analytics'; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; -export default class HeaderButton extends React.Component { - constructor() { - super(); +interface IProps { + // Whether this button is highlighted + isHighlighted: boolean; + // click handler + onClick: () => void; + // The badge to display above the icon + badge: React.ReactNode; + // The parameters to track the click event + analytics: string[]; + + // Button name + name: string; + // Button title + title: string; +}; + +export default class HeaderButton extends React.Component { + constructor(props: IProps) { + super(props); this.onClick = this.onClick.bind(this); } - onClick(ev) { + onClick(_ev: React.KeyboardEvent) { Analytics.trackEvent(...this.props.analytics); this.props.onClick(); } @@ -51,19 +66,3 @@ export default class HeaderButton extends React.Component { />; } } - -HeaderButton.propTypes = { - // Whether this button is highlighted - isHighlighted: PropTypes.bool.isRequired, - // click handler - onClick: PropTypes.func.isRequired, - // The badge to display above the icon - badge: PropTypes.node, - // The parameters to track the click event - analytics: PropTypes.arrayOf(PropTypes.string).isRequired, - - // Button name - name: PropTypes.string.isRequired, - // Button title - title: PropTypes.string.isRequired, -}; diff --git a/src/components/views/right_panel/HeaderButtons.js b/src/components/views/right_panel/HeaderButtons.tsx similarity index 57% rename from src/components/views/right_panel/HeaderButtons.js rename to src/components/views/right_panel/HeaderButtons.tsx index 1c66fe5828..099e785ada 100644 --- a/src/components/views/right_panel/HeaderButtons.js +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -21,42 +21,52 @@ limitations under the License. import React from 'react'; import dis from '../../../dispatcher/dispatcher'; import RightPanelStore from "../../../stores/RightPanelStore"; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; +import {Action} from '../../../dispatcher/actions'; -export const HEADER_KIND_ROOM = "room"; -export const HEADER_KIND_GROUP = "group"; +export enum HeaderKind { + Room = "room", + Group = "group", +} -const HEADER_KINDS = [HEADER_KIND_GROUP, HEADER_KIND_ROOM]; +interface IState { + headerKind: HeaderKind; + phase: RightPanelPhases; +} -export default class HeaderButtons extends React.Component { - constructor(props, kind) { +interface IProps {} + +export default class HeaderButtons extends React.Component { + private storeToken: ReturnType; + private dispatcherRef: string; + + constructor(props: IProps, kind: HeaderKind) { super(props); - if (!HEADER_KINDS.includes(kind)) throw new Error(`Invalid header kind: ${kind}`); - const rps = RightPanelStore.getSharedInstance(); this.state = { headerKind: kind, - phase: kind === HEADER_KIND_ROOM ? rps.visibleRoomPanelPhase : rps.visibleGroupPanelPhase, + phase: kind === HeaderKind.Room ? rps.visibleRoomPanelPhase : rps.visibleGroupPanelPhase, }; } componentDidMount() { - this._storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this)); - this._dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses + this.storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this)); + this.dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses } componentWillUnmount() { - if (this._storeToken) this._storeToken.remove(); - if (this._dispatcherRef) dis.unregister(this._dispatcherRef); + if (this.storeToken) this.storeToken.remove(); + if (this.dispatcherRef) dis.unregister(this.dispatcherRef); } onAction(payload) { // Ignore - intended to be overridden by subclasses } - setPhase(phase, extras) { + setPhase(phase: RightPanelPhases, extras) { dis.dispatch({ - action: 'set_right_panel_phase', + action: Action.SetRightPanelPhase, phase: phase, refireParams: extras, }); @@ -72,13 +82,23 @@ export default class HeaderButtons extends React.Component { onRightPanelUpdate() { const rps = RightPanelStore.getSharedInstance(); - if (this.state.headerKind === HEADER_KIND_ROOM) { + if (this.state.headerKind === HeaderKind.Room) { this.setState({phase: rps.visibleRoomPanelPhase}); - } else if (this.state.headerKind === HEADER_KIND_GROUP) { + } else if (this.state.headerKind === HeaderKind.Group) { this.setState({phase: rps.visibleGroupPanelPhase}); } } + // XXX: Make renderButtons a prop + renderButtons(): JSX.Element[] { + // Ignore - intended to be overridden by subclasses + // Return empty fragment to satisfy the type + return [ + + + ]; + } + render() { // inline style as this will be swapped around in future commits return
diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 519a799e67..6fb71df30d 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -79,4 +79,19 @@ export enum Action { * Changes room based on room list order and payload parameters. Should be used with ViewRoomDeltaPayload. */ ViewRoomDelta = "view_room_delta", + + /** + * Sets the phase for the right panel. Should be used with SetRightPanelPhasePayload. + */ + SetRightPanelPhase = "set_right_panel_phase", + + /** + * Toggles the right panel. Should be used with ToggleRightPanelPayload. + */ + ToggleRightPanel = "toggle_right_panel", + + /** + * Trigged after the phase of the right panel is set. Should be used with AfterRightPanelPhaseChangePayload. + */ + AfterRightPanelPhaseChange = "after_right_panel_phase_change", } diff --git a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts new file mode 100644 index 0000000000..3193f9043b --- /dev/null +++ b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts @@ -0,0 +1,28 @@ +/* +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 { RightPanelPhases } from "../../stores/RightPanelStorePhases"; +import { SetRightPanelPhaseRefireParams } from "./SetRightPanelPhasePayload"; +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +interface AfterRightPanelPhaseChangeAction extends ActionPayload { + action: Action.AfterRightPanelPhaseChange; + phase: RightPanelPhases; +} + +export type AfterRightPanelPhaseChangePayload + = AfterRightPanelPhaseChangeAction & SetRightPanelPhaseRefireParams; diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts new file mode 100644 index 0000000000..8d8eca762c --- /dev/null +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -0,0 +1,36 @@ +/* +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 {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +export interface SetRightPanelPhasePayload extends ActionPayload { + action: Action.SetRightPanelPhase; + + phase: RightPanelPhases; + refireParams?: SetRightPanelPhaseRefireParams; +} + +export interface SetRightPanelPhaseRefireParams { + // XXX: Fix after the types are defiend in matrix-js-sdk + // No appropriate types exist yet for the fields + members: any; + verificationRequest: typeof VerificationRequest; + groudId: string; + groupRoomId: string; +} diff --git a/src/dispatcher/payloads/ToggleRightPanelPayload.ts b/src/dispatcher/payloads/ToggleRightPanelPayload.ts new file mode 100644 index 0000000000..0635194890 --- /dev/null +++ b/src/dispatcher/payloads/ToggleRightPanelPayload.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 ToggleRightPanelPayload extends ActionPayload { + action: Action.ToggleRightPanel; + + /** + * The type of room that the panel is toggled in. + */ + type: "group" | "room"; +} diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.ts similarity index 59% rename from src/stores/RightPanelStore.js rename to src/stores/RightPanelStore.ts index a73f3befbb..6ed5d7fe9a 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.ts @@ -15,31 +15,45 @@ limitations under the License. */ import dis from '../dispatcher/dispatcher'; +import {Action} from '../dispatcher/actions'; import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; -import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases"; +import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases"; -const INITIAL_STATE = { +interface RightPanelStoreState { // Whether or not to show the right panel at all. We split out rooms and groups // because they're different flows for the user to follow. - showRoomPanel: SettingsStore.getValue("showRightPanelInRoom"), - showGroupPanel: SettingsStore.getValue("showRightPanelInGroup"), + showRoomPanel: boolean; + showGroupPanel: boolean; // The last phase (screen) the right panel was showing - lastRoomPhase: SettingsStore.getValue("lastRightPanelPhaseForRoom"), - lastGroupPhase: SettingsStore.getValue("lastRightPanelPhaseForGroup"), + lastRoomPhase: RightPanelPhases; + lastGroupPhase: RightPanelPhases; // Extra information about the last phase + lastRoomPhaseParams: {[key: string]: any}; +} + +const INITIAL_STATE: RightPanelStoreState = { + showRoomPanel: SettingsStore.getValue("showRightPanelInRoom"), + showGroupPanel: SettingsStore.getValue("showRightPanelInGroup"), + lastRoomPhase: SettingsStore.getValue("lastRightPanelPhaseForRoom"), + lastGroupPhase: SettingsStore.getValue("lastRightPanelPhaseForGroup"), lastRoomPhaseParams: {}, }; -const GROUP_PHASES = Object.keys(RIGHT_PANEL_PHASES).filter(k => k.startsWith("Group")); +const GROUP_PHASES = [ + RightPanelPhases.GroupMemberList, + RightPanelPhases.GroupRoomList, + RightPanelPhases.GroupRoomInfo, + RightPanelPhases.GroupMemberInfo, +]; const MEMBER_INFO_PHASES = [ - RIGHT_PANEL_PHASES.RoomMemberInfo, - RIGHT_PANEL_PHASES.Room3pidMemberInfo, - RIGHT_PANEL_PHASES.EncryptionPanel, + RightPanelPhases.RoomMemberInfo, + RightPanelPhases.Room3pidMemberInfo, + RightPanelPhases.EncryptionPanel, ]; /** @@ -47,132 +61,133 @@ const MEMBER_INFO_PHASES = [ * sessions. */ export default class RightPanelStore extends Store { - static _instance; + private static instance: RightPanelStore; + private state: RightPanelStoreState; constructor() { super(dis); // Initialise state - this._state = INITIAL_STATE; + this.state = INITIAL_STATE; } get isOpenForRoom(): boolean { - return this._state.showRoomPanel; + return this.state.showRoomPanel; } get isOpenForGroup(): boolean { - return this._state.showGroupPanel; + return this.state.showGroupPanel; } - get roomPanelPhase(): string { - return this._state.lastRoomPhase; + get roomPanelPhase(): RightPanelPhases { + return this.state.lastRoomPhase; } - get groupPanelPhase(): string { - return this._state.lastGroupPhase; + get groupPanelPhase(): RightPanelPhases { + return this.state.lastGroupPhase; } - get visibleRoomPanelPhase(): string { + get visibleRoomPanelPhase(): RightPanelPhases { return this.isOpenForRoom ? this.roomPanelPhase : null; } - get visibleGroupPanelPhase(): string { + get visibleGroupPanelPhase(): RightPanelPhases { return this.isOpenForGroup ? this.groupPanelPhase : null; } get roomPanelPhaseParams(): any { - return this._state.lastRoomPhaseParams || {}; + return this.state.lastRoomPhaseParams || {}; } - _setState(newState) { - this._state = Object.assign(this._state, newState); + private setState(newState: Partial) { + this.state = Object.assign(this.state, newState); SettingsStore.setValue( "showRightPanelInRoom", null, SettingLevel.DEVICE, - this._state.showRoomPanel, + this.state.showRoomPanel, ); SettingsStore.setValue( "showRightPanelInGroup", null, SettingLevel.DEVICE, - this._state.showGroupPanel, + this.state.showGroupPanel, ); - if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this._state.lastRoomPhase)) { + if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this.state.lastRoomPhase)) { SettingsStore.setValue( "lastRightPanelPhaseForRoom", null, SettingLevel.DEVICE, - this._state.lastRoomPhase, + this.state.lastRoomPhase, ); } - if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this._state.lastGroupPhase)) { + if (RIGHT_PANEL_PHASES_NO_ARGS.includes(this.state.lastGroupPhase)) { SettingsStore.setValue( "lastRightPanelPhaseForGroup", null, SettingLevel.DEVICE, - this._state.lastGroupPhase, + this.state.lastGroupPhase, ); } this.__emitChange(); } - __onDispatch(payload) { + __onDispatch(payload: ActionPayload) { switch (payload.action) { case 'view_room': case 'view_group': // Reset to the member list if we're viewing member info - if (MEMBER_INFO_PHASES.includes(this._state.lastRoomPhase)) { - this._setState({lastRoomPhase: RIGHT_PANEL_PHASES.RoomMemberList, lastRoomPhaseParams: {}}); + if (MEMBER_INFO_PHASES.includes(this.state.lastRoomPhase)) { + this.setState({lastRoomPhase: RightPanelPhases.RoomMemberList, lastRoomPhaseParams: {}}); } // Do the same for groups - if (this._state.lastGroupPhase === RIGHT_PANEL_PHASES.GroupMemberInfo) { - this._setState({lastGroupPhase: RIGHT_PANEL_PHASES.GroupMemberList}); + if (this.state.lastGroupPhase === RightPanelPhases.GroupMemberInfo) { + this.setState({lastGroupPhase: RightPanelPhases.GroupMemberList}); } break; - case 'set_right_panel_phase': { + case Action.SetRightPanelPhase: { let targetPhase = payload.phase; let refireParams = payload.refireParams; // redirect to EncryptionPanel if there is an ongoing verification request - if (targetPhase === RIGHT_PANEL_PHASES.RoomMemberInfo && payload.refireParams) { + if (targetPhase === RightPanelPhases.RoomMemberInfo && payload.refireParams) { const {member} = payload.refireParams; const pendingRequest = pendingVerificationRequestForUser(member); if (pendingRequest) { - targetPhase = RIGHT_PANEL_PHASES.EncryptionPanel; + targetPhase = RightPanelPhases.EncryptionPanel; refireParams = { verificationRequest: pendingRequest, member, }; } } - if (!RIGHT_PANEL_PHASES[targetPhase]) { + if (!RightPanelPhases[targetPhase]) { console.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`); return; } if (GROUP_PHASES.includes(targetPhase)) { - if (targetPhase === this._state.lastGroupPhase) { - this._setState({ - showGroupPanel: !this._state.showGroupPanel, + if (targetPhase === this.state.lastGroupPhase) { + this.setState({ + showGroupPanel: !this.state.showGroupPanel, }); } else { - this._setState({ + this.setState({ lastGroupPhase: targetPhase, showGroupPanel: true, }); } } else { - if (targetPhase === this._state.lastRoomPhase && !refireParams) { - this._setState({ - showRoomPanel: !this._state.showRoomPanel, + if (targetPhase === this.state.lastRoomPhase && !refireParams) { + this.setState({ + showRoomPanel: !this.state.showRoomPanel, }); } else { - this._setState({ + this.setState({ lastRoomPhase: targetPhase, showRoomPanel: true, lastRoomPhaseParams: refireParams || {}, @@ -182,27 +197,27 @@ export default class RightPanelStore extends Store { // Let things like the member info panel actually open to the right member. dis.dispatch({ - action: 'after_right_panel_phase_change', + action: Action.AfterRightPanelPhaseChange, phase: targetPhase, ...(refireParams || {}), }); break; } - case 'toggle_right_panel': + case Action.ToggleRightPanel: if (payload.type === "room") { - this._setState({ showRoomPanel: !this._state.showRoomPanel }); + this.setState({ showRoomPanel: !this.state.showRoomPanel }); } else { // group - this._setState({ showGroupPanel: !this._state.showGroupPanel }); + this.setState({ showGroupPanel: !this.state.showGroupPanel }); } break; } } static getSharedInstance(): RightPanelStore { - if (!RightPanelStore._instance) { - RightPanelStore._instance = new RightPanelStore(); + if (!RightPanelStore.instance) { + RightPanelStore.instance = new RightPanelStore(); } - return RightPanelStore._instance; + return RightPanelStore.instance; } } diff --git a/src/stores/RightPanelStorePhases.js b/src/stores/RightPanelStorePhases.ts similarity index 57% rename from src/stores/RightPanelStorePhases.js rename to src/stores/RightPanelStorePhases.ts index d9af320233..4d05738425 100644 --- a/src/stores/RightPanelStorePhases.js +++ b/src/stores/RightPanelStorePhases.ts @@ -15,28 +15,28 @@ limitations under the License. */ // These are in their own file because of circular imports being a problem. -export const RIGHT_PANEL_PHASES = Object.freeze({ +export enum RightPanelPhases { // Room stuff - RoomMemberList: 'RoomMemberList', - FilePanel: 'FilePanel', - NotificationPanel: 'NotificationPanel', - RoomMemberInfo: 'RoomMemberInfo', - EncryptionPanel: 'EncryptionPanel', + RoomMemberList = 'RoomMemberList', + FilePanel = 'FilePanel', + NotificationPanel = 'NotificationPanel', + RoomMemberInfo = 'RoomMemberInfo', + EncryptionPanel = 'EncryptionPanel', - Room3pidMemberInfo: 'Room3pidMemberInfo', + Room3pidMemberInfo = 'Room3pidMemberInfo', // Group stuff - GroupMemberList: 'GroupMemberList', - GroupRoomList: 'GroupRoomList', - GroupRoomInfo: 'GroupRoomInfo', - GroupMemberInfo: 'GroupMemberInfo', -}); + GroupMemberList = 'GroupMemberList', + GroupRoomList = 'GroupRoomList', + GroupRoomInfo = 'GroupRoomInfo', + GroupMemberInfo = 'GroupMemberInfo', +}; // These are the phases that are safe to persist (the ones that don't require additional // arguments). export const RIGHT_PANEL_PHASES_NO_ARGS = [ - RIGHT_PANEL_PHASES.NotificationPanel, - RIGHT_PANEL_PHASES.FilePanel, - RIGHT_PANEL_PHASES.RoomMemberList, - RIGHT_PANEL_PHASES.GroupMemberList, - RIGHT_PANEL_PHASES.GroupRoomList, + RightPanelPhases.NotificationPanel, + RightPanelPhases.FilePanel, + RightPanelPhases.RoomMemberList, + RightPanelPhases.GroupMemberList, + RightPanelPhases.GroupRoomList, ]; From 1d3635e1c8c9758c9aa9720e4439749d7744c5bd Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 16:38:20 +0530 Subject: [PATCH 002/136] Replaced string actions with their corresponding types --- src/components/structures/LoggedInView.tsx | 5 ++- src/components/structures/RightPanel.js | 42 +++++++++---------- .../views/messages/MKeyVerificationRequest.js | 7 ++-- .../views/right_panel/GroupHeaderButtons.js | 36 ++++++++-------- .../views/right_panel/HeaderButtons.tsx | 3 +- .../views/right_panel/RoomHeaderButtons.js | 36 ++++++++-------- src/components/views/right_panel/UserInfo.js | 10 ++--- src/components/views/rooms/Stickerpicker.js | 3 +- .../views/toasts/VerificationRequestToast.tsx | 7 ++-- src/settings/Settings.js | 6 +-- src/stores/RightPanelStore.ts | 5 ++- src/verification.js | 15 +++---- 12 files changed, 91 insertions(+), 84 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1f561e68ef..760ea52855 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -54,6 +54,7 @@ import LeftPanel from "./LeftPanel"; import CallContainer from '../views/voip/CallContainer'; import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload"; import RoomListStore from "../../stores/room-list/RoomListStore"; +import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload"; // 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. @@ -472,8 +473,8 @@ class LoggedInView extends React.Component { case Key.PERIOD: if (ctrlCmdOnly && (this.props.page_type === "room_view" || this.props.page_type === "group_view")) { - dis.dispatch({ - action: 'toggle_right_panel', + dis.dispatch({ + action: Action.ToggleRightPanel, type: this.props.page_type === "room_view" ? "room" : "group", }); handled = true; diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 776130e709..a4e3254e4c 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -26,7 +26,7 @@ import dis from '../../dispatcher/dispatcher'; import RateLimitedFunc from '../../ratelimitedfunc'; import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker'; import GroupStore from '../../stores/GroupStore'; -import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases"; +import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases"; import RightPanelStore from "../../stores/RightPanelStore"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import {Action} from "../../dispatcher/actions"; @@ -75,8 +75,8 @@ export default class RightPanel extends React.Component { const userForPanel = this._getUserForPanel(); if (this.props.groupId) { if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.groupPanelPhase)) { - dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.GroupMemberList}); - return RIGHT_PANEL_PHASES.GroupMemberList; + dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.GroupMemberList}); + return RightPanelPhases.GroupMemberList; } return rps.groupPanelPhase; } else if (userForPanel) { @@ -98,11 +98,11 @@ export default class RightPanel extends React.Component { ) { return rps.roomPanelPhase; } - return RIGHT_PANEL_PHASES.RoomMemberInfo; + return RightPanelPhases.RoomMemberInfo; } else { if (!RIGHT_PANEL_PHASES_NO_ARGS.includes(rps.roomPanelPhase)) { - dis.dispatch({action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.RoomMemberList}); - return RIGHT_PANEL_PHASES.RoomMemberList; + dis.dispatch({action: Action.SetRightPanelPhase, phase: RightPanelPhases.RoomMemberList}); + return RightPanelPhases.RoomMemberList; } return rps.roomPanelPhase; } @@ -149,7 +149,7 @@ export default class RightPanel extends React.Component { onInviteToGroupButtonClick() { showGroupInviteDialog(this.props.groupId).then(() => { this.setState({ - phase: RIGHT_PANEL_PHASES.GroupMemberList, + phase: RightPanelPhases.GroupMemberList, }); }); } @@ -165,9 +165,9 @@ export default class RightPanel extends React.Component { return; } // redraw the badge on the membership list - if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberList && member.roomId === this.props.roomId) { + if (this.state.phase === RightPanelPhases.RoomMemberList && member.roomId === this.props.roomId) { this._delayedUpdate(); - } else if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo && member.roomId === this.props.roomId && + } else if (this.state.phase === RightPanelPhases.RoomMemberInfo && member.roomId === this.props.roomId && member.userId === this.state.member.userId) { // refresh the member info (e.g. new power level) this._delayedUpdate(); @@ -175,7 +175,7 @@ export default class RightPanel extends React.Component { } onAction(payload) { - if (payload.action === "after_right_panel_phase_change") { + if (payload.action === Action.AfterRightPanelPhaseChange) { this.setState({ phase: payload.phase, groupRoomId: payload.groupRoomId, @@ -206,7 +206,7 @@ export default class RightPanel extends React.Component { // or the member list if we were in the member panel... phew. dis.dispatch({ action: Action.ViewUser, - member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ? this.state.member : null, + member: this.state.phase === RightPanelPhases.EncryptionPanel ? this.state.member : null, }); } }; @@ -225,21 +225,21 @@ export default class RightPanel extends React.Component { let panel =
; switch (this.state.phase) { - case RIGHT_PANEL_PHASES.RoomMemberList: + case RightPanelPhases.RoomMemberList: if (this.props.roomId) { panel = ; } break; - case RIGHT_PANEL_PHASES.GroupMemberList: + case RightPanelPhases.GroupMemberList: if (this.props.groupId) { panel = ; } break; - case RIGHT_PANEL_PHASES.GroupRoomList: + case RightPanelPhases.GroupRoomList: panel = ; break; - case RIGHT_PANEL_PHASES.RoomMemberInfo: - case RIGHT_PANEL_PHASES.EncryptionPanel: + case RightPanelPhases.RoomMemberInfo: + case RightPanelPhases.EncryptionPanel: panel = ; break; - case RIGHT_PANEL_PHASES.Room3pidMemberInfo: + case RightPanelPhases.Room3pidMemberInfo: panel = ; break; - case RIGHT_PANEL_PHASES.GroupMemberInfo: + case RightPanelPhases.GroupMemberInfo: panel = ; break; - case RIGHT_PANEL_PHASES.GroupRoomInfo: + case RightPanelPhases.GroupRoomInfo: panel = ; break; - case RIGHT_PANEL_PHASES.NotificationPanel: + case RightPanelPhases.NotificationPanel: panel = ; break; - case RIGHT_PANEL_PHASES.FilePanel: + case RightPanelPhases.FilePanel: panel = ; break; } diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index a5b1ae26bb..01a5c2663e 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -22,7 +22,8 @@ import { _t } from '../../../languageHandler'; import {getNameForEventRoom, userLabelForEventRoom} from '../../../utils/KeyVerificationStateObserver'; import dis from "../../../dispatcher/dispatcher"; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; +import {Action} from "../../../dispatcher/actions"; export default class MKeyVerificationRequest extends React.Component { constructor(props) { @@ -48,8 +49,8 @@ export default class MKeyVerificationRequest extends React.Component { const {verificationRequest} = this.props.mxEvent; const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId); dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.EncryptionPanel, refireParams: {verificationRequest, member}, }); }; diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.js index 33d9325433..fb589b1a0f 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.js +++ b/src/components/views/right_panel/GroupHeaderButtons.js @@ -21,23 +21,23 @@ limitations under the License. import React from 'react'; import { _t } from '../../../languageHandler'; import HeaderButton from './HeaderButton'; -import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons'; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import HeaderButtons, {HeaderKind} from './HeaderButtons'; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from "../../../dispatcher/actions"; import {ActionPayload} from "../../../dispatcher/payloads"; const GROUP_PHASES = [ - RIGHT_PANEL_PHASES.GroupMemberInfo, - RIGHT_PANEL_PHASES.GroupMemberList, + RightPanelPhases.GroupMemberInfo, + RightPanelPhases.GroupMemberList, ]; const ROOM_PHASES = [ - RIGHT_PANEL_PHASES.GroupRoomList, - RIGHT_PANEL_PHASES.GroupRoomInfo, + RightPanelPhases.GroupRoomList, + RightPanelPhases.GroupRoomInfo, ]; export default class GroupHeaderButtons extends HeaderButtons { constructor(props) { - super(props, HEADER_KIND_GROUP); + super(props, HeaderKind.Group); this._onMembersClicked = this._onMembersClicked.bind(this); this._onRoomsClicked = this._onRoomsClicked.bind(this); } @@ -47,39 +47,39 @@ export default class GroupHeaderButtons extends HeaderButtons { if (payload.action === Action.ViewUser) { if (payload.member) { - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member}); + this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); } else { - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList); + this.setPhase(RightPanelPhases.GroupMemberList); } } else if (payload.action === "view_group") { - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList); + this.setPhase(RightPanelPhases.GroupMemberList); } else if (payload.action === "view_group_room") { this.setPhase( - RIGHT_PANEL_PHASES.GroupRoomInfo, + RightPanelPhases.GroupRoomInfo, {groupRoomId: payload.groupRoomId, groupId: payload.groupId}, ); } else if (payload.action === "view_group_room_list") { - this.setPhase(RIGHT_PANEL_PHASES.GroupRoomList); + this.setPhase(RightPanelPhases.GroupRoomList); } else if (payload.action === "view_group_member_list") { - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList); + this.setPhase(RightPanelPhases.GroupMemberList); } else if (payload.action === "view_group_user") { - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo, {member: payload.member}); + this.setPhase(RightPanelPhases.GroupMemberInfo, {member: payload.member}); } } _onMembersClicked() { - if (this.state.phase === RIGHT_PANEL_PHASES.GroupMemberInfo) { + if (this.state.phase === RightPanelPhases.GroupMemberInfo) { // send the active phase to trigger a toggle - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberInfo); + this.setPhase(RightPanelPhases.GroupMemberInfo); } else { // This toggles for us, if needed - this.setPhase(RIGHT_PANEL_PHASES.GroupMemberList); + this.setPhase(RightPanelPhases.GroupMemberList); } } _onRoomsClicked() { // This toggles for us, if needed - this.setPhase(RIGHT_PANEL_PHASES.GroupRoomList); + this.setPhase(RightPanelPhases.GroupRoomList); } renderButtons() { diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 099e785ada..9e57eab5eb 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -23,6 +23,7 @@ import dis from '../../../dispatcher/dispatcher'; import RightPanelStore from "../../../stores/RightPanelStore"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from '../../../dispatcher/actions'; +import {SetRightPanelPhasePayload} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; export enum HeaderKind { Room = "room", @@ -65,7 +66,7 @@ export default class HeaderButtons extends React.Component { } setPhase(phase: RightPanelPhases, extras) { - dis.dispatch({ + dis.dispatch({ action: Action.SetRightPanelPhase, phase: phase, refireParams: extras, diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.js index 838727981d..8620d5b485 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.js +++ b/src/components/views/right_panel/RoomHeaderButtons.js @@ -21,21 +21,21 @@ limitations under the License. import React from 'react'; import { _t } from '../../../languageHandler'; import HeaderButton from './HeaderButton'; -import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons'; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import HeaderButtons, {HeaderKind} from './HeaderButtons'; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from "../../../dispatcher/actions"; import {ActionPayload} from "../../../dispatcher/payloads"; const MEMBER_PHASES = [ - RIGHT_PANEL_PHASES.RoomMemberList, - RIGHT_PANEL_PHASES.RoomMemberInfo, - RIGHT_PANEL_PHASES.EncryptionPanel, - RIGHT_PANEL_PHASES.Room3pidMemberInfo, + RightPanelPhases.RoomMemberList, + RightPanelPhases.RoomMemberInfo, + RightPanelPhases.EncryptionPanel, + RightPanelPhases.Room3pidMemberInfo, ]; export default class RoomHeaderButtons extends HeaderButtons { constructor(props) { - super(props, HEADER_KIND_ROOM); + super(props, HeaderKind.Room); this._onMembersClicked = this._onMembersClicked.bind(this); this._onFilesClicked = this._onFilesClicked.bind(this); this._onNotificationsClicked = this._onNotificationsClicked.bind(this); @@ -45,38 +45,38 @@ export default class RoomHeaderButtons extends HeaderButtons { super.onAction(payload); if (payload.action === Action.ViewUser) { if (payload.member) { - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member}); + this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); } else { - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList); + this.setPhase(RightPanelPhases.RoomMemberList); } } else if (payload.action === "view_3pid_invite") { if (payload.event) { - this.setPhase(RIGHT_PANEL_PHASES.Room3pidMemberInfo, {event: payload.event}); + this.setPhase(RightPanelPhases.Room3pidMemberInfo, {event: payload.event}); } else { - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList); + this.setPhase(RightPanelPhases.RoomMemberList); } } } _onMembersClicked() { - if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo) { + if (this.state.phase === RightPanelPhases.RoomMemberInfo) { // send the active phase to trigger a toggle // XXX: we should pass refireParams here but then it won't collapse as we desire it to - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo); + this.setPhase(RightPanelPhases.RoomMemberInfo); } else { // This toggles for us, if needed - this.setPhase(RIGHT_PANEL_PHASES.RoomMemberList); + this.setPhase(RightPanelPhases.RoomMemberList); } } _onFilesClicked() { // This toggles for us, if needed - this.setPhase(RIGHT_PANEL_PHASES.FilePanel); + this.setPhase(RightPanelPhases.FilePanel); } _onNotificationsClicked() { // This toggles for us, if needed - this.setPhase(RIGHT_PANEL_PHASES.NotificationPanel); + this.setPhase(RightPanelPhases.NotificationPanel); } renderButtons() { @@ -89,13 +89,13 @@ export default class RoomHeaderButtons extends HeaderButtons { />, , , diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 719a64063d..20168faede 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -40,7 +40,7 @@ import E2EIcon from "../rooms/E2EIcon"; import {useEventEmitter} from "../../../hooks/useEventEmitter"; import {textualPowerLevel} from '../../../Roles'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import EncryptionPanel from "./EncryptionPanel"; import { useAsyncMemo } from '../../../hooks/useAsyncMemo'; import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification'; @@ -1480,7 +1480,7 @@ const UserInfoHeader = ({onClose, member, e2eStatus}) => { ; }; -const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.RoomMemberInfo, ...props}) => { +const UserInfo = ({user, groupId, roomId, onClose, phase=RightPanelPhases.RoomMemberInfo, ...props}) => { const cli = useContext(MatrixClientContext); // Load room if we are given a room id and memoize it @@ -1500,8 +1500,8 @@ const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.Room let content; switch (phase) { - case RIGHT_PANEL_PHASES.RoomMemberInfo: - case RIGHT_PANEL_PHASES.GroupMemberInfo: + case RightPanelPhases.RoomMemberInfo: + case RightPanelPhases.GroupMemberInfo: content = ( ); break; - case RIGHT_PANEL_PHASES.EncryptionPanel: + case RightPanelPhases.EncryptionPanel: classes.push("mx_UserInfo_smallAvatar"); content = ( diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index 2e56e49be1..b73f03a9c5 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -28,6 +28,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import {ContextMenu} from "../../structures/ContextMenu"; import {WidgetType} from "../../../widgets/WidgetType"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; +import {Action} from "../../../dispatcher/actions"; // This should be below the dialog level (4000), but above the rest of the UI (1000-2000). // We sit in a context menu, so this should be given to the context menu. @@ -181,7 +182,7 @@ export default class Stickerpicker extends React.Component { case "stickerpicker_close": this.setState({showStickers: false}); break; - case "after_right_panel_phase_change": + case Action.AfterRightPanelPhaseChange: case "show_left_panel": case "hide_left_panel": this.setState({showStickers: false}); diff --git a/src/components/views/toasts/VerificationRequestToast.tsx b/src/components/views/toasts/VerificationRequestToast.tsx index cc41e81b33..89c826eac4 100644 --- a/src/components/views/toasts/VerificationRequestToast.tsx +++ b/src/components/views/toasts/VerificationRequestToast.tsx @@ -19,7 +19,7 @@ import React from "react"; import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; import dis from "../../../dispatcher/dispatcher"; import ToastStore from "../../../stores/ToastStore"; @@ -27,6 +27,7 @@ import Modal from "../../../Modal"; import GenericToast from "./GenericToast"; import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import {DeviceInfo} from "matrix-js-sdk/src/crypto/deviceinfo"; +import {Action} from "../../../dispatcher/actions"; interface IProps { toastKey: string; @@ -105,8 +106,8 @@ export default class VerificationRequestToast extends React.PureComponent { private static instance: RightPanelStore; private state: RightPanelStoreState; diff --git a/src/verification.js b/src/verification.js index 1dccb7dc28..36fb8b0e4f 100644 --- a/src/verification.js +++ b/src/verification.js @@ -19,10 +19,11 @@ import dis from "./dispatcher/dispatcher"; import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; -import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases"; +import {RightPanelPhases} from "./stores/RightPanelStorePhases"; import {findDMForUser} from './createRoom'; import {accessSecretStorage} from './CrossSigningManager'; import {verificationMethods} from 'matrix-js-sdk/src/crypto'; +import {Action} from './dispatcher/actions'; async function enable4SIfNeeded() { const cli = MatrixClientPeg.get(); @@ -91,8 +92,8 @@ export async function verifyDevice(user, device) { verificationMethods.SAS, ); dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.EncryptionPanel, refireParams: {member: user, verificationRequestPromise}, }); } else if (action === "legacy") { @@ -120,8 +121,8 @@ export async function legacyVerifyUser(user) { } const verificationRequestPromise = cli.requestVerification(user.userId); dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.EncryptionPanel, refireParams: {member: user, verificationRequestPromise}, }); } @@ -132,8 +133,8 @@ export async function verifyUser(user) { } const existingRequest = pendingVerificationRequestForUser(user); dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.EncryptionPanel, refireParams: { member: user, verificationRequest: existingRequest, From 2c8b5b49ed1d26ffb0f1c35b5f0346788c579aa1 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 16:41:57 +0530 Subject: [PATCH 003/136] lint --- src/components/views/right_panel/HeaderButton.tsx | 2 +- src/stores/RightPanelStorePhases.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/HeaderButton.tsx b/src/components/views/right_panel/HeaderButton.tsx index a64ed0a980..563af90432 100644 --- a/src/components/views/right_panel/HeaderButton.tsx +++ b/src/components/views/right_panel/HeaderButton.tsx @@ -37,7 +37,7 @@ interface IProps { name: string; // Button title title: string; -}; +} export default class HeaderButton extends React.Component { constructor(props: IProps) { diff --git a/src/stores/RightPanelStorePhases.ts b/src/stores/RightPanelStorePhases.ts index 4d05738425..9045b17193 100644 --- a/src/stores/RightPanelStorePhases.ts +++ b/src/stores/RightPanelStorePhases.ts @@ -29,7 +29,7 @@ export enum RightPanelPhases { GroupRoomList = 'GroupRoomList', GroupRoomInfo = 'GroupRoomInfo', GroupMemberInfo = 'GroupMemberInfo', -}; +} // These are the phases that are safe to persist (the ones that don't require additional // arguments). From 1c913b85e2217813402b013d76fc4267085f11fc Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 16:47:07 +0530 Subject: [PATCH 004/136] Replace strings action with correspoding type --- src/components/views/groups/GroupMemberList.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 7b643c7346..31c6fe5d07 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -25,7 +25,10 @@ import PropTypes from 'prop-types'; import { showGroupInviteDialog } from '../../../GroupAddressPicker'; import AccessibleButton from '../elements/AccessibleButton'; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import TintableSvg from '../elements/TintableSvg'; +import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; +import {Action} from "../../../dispatcher/actions"; const INITIAL_LOAD_NUM_MEMBERS = 30; @@ -164,8 +167,8 @@ export default createReactClass({ onInviteToGroupButtonClick() { showGroupInviteDialog(this.props.groupId).then(() => { dis.dispatch({ - action: 'set_right_panel_phase', - phase: RIGHT_PANEL_PHASES.GroupMemberList, + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.GroupMemberList, groupId: this.props.groupId, }); }); From 0f59e34a3a12d08413e57b4e3031016cb373228e Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 16:47:44 +0530 Subject: [PATCH 005/136] Nest groupId in refireParams to match types --- src/components/views/groups/GroupMemberList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 31c6fe5d07..e7143f0dd2 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -169,7 +169,7 @@ export default createReactClass({ dis.dispatch({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.GroupMemberList, - groupId: this.props.groupId, + refireParams: { groupId: this.props.groupId }, }); }); }, From 23e4b67a69a61cd406012ee4b1c4344734e99664 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 17:14:20 +0530 Subject: [PATCH 006/136] Convert HeaderButton to TS --- src/components/views/right_panel/HeaderButton.tsx | 2 +- src/components/views/right_panel/HeaderButtons.tsx | 4 ++-- .../{RoomHeaderButtons.js => RoomHeaderButtons.tsx} | 0 src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) rename src/components/views/right_panel/{RoomHeaderButtons.js => RoomHeaderButtons.tsx} (100%) diff --git a/src/components/views/right_panel/HeaderButton.tsx b/src/components/views/right_panel/HeaderButton.tsx index 563af90432..022a993c65 100644 --- a/src/components/views/right_panel/HeaderButton.tsx +++ b/src/components/views/right_panel/HeaderButton.tsx @@ -29,7 +29,7 @@ interface IProps { // click handler onClick: () => void; // The badge to display above the icon - badge: React.ReactNode; + badge?: React.ReactNode; // The parameters to track the click event analytics: string[]; diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 9e57eab5eb..8141e8a9cc 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -23,7 +23,7 @@ import dis from '../../../dispatcher/dispatcher'; import RightPanelStore from "../../../stores/RightPanelStore"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from '../../../dispatcher/actions'; -import {SetRightPanelPhasePayload} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; +import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; export enum HeaderKind { Room = "room", @@ -65,7 +65,7 @@ export default class HeaderButtons extends React.Component { // Ignore - intended to be overridden by subclasses } - setPhase(phase: RightPanelPhases, extras) { + setPhase(phase: RightPanelPhases, extras?: Partial) { dis.dispatch({ action: Action.SetRightPanelPhase, phase: phase, diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.tsx similarity index 100% rename from src/components/views/right_panel/RoomHeaderButtons.js rename to src/components/views/right_panel/RoomHeaderButtons.tsx diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 8d8eca762c..94e38b7ce6 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -29,8 +29,10 @@ export interface SetRightPanelPhasePayload extends ActionPayload { export interface SetRightPanelPhaseRefireParams { // XXX: Fix after the types are defiend in matrix-js-sdk // No appropriate types exist yet for the fields - members: any; - verificationRequest: typeof VerificationRequest; - groudId: string; - groupRoomId: string; + members?: any; + verificationRequest?: typeof VerificationRequest; + groudId?: string; + groupRoomId?: string; + // XXX: 'view_3pid_invite' action's payload + event?: any; } From 887d507772af9c464e193de10cc07c3d04e4cb3c Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 17:15:12 +0530 Subject: [PATCH 007/136] Fix key to match SetRightPanelPhasePayload's key member -> members --- src/components/views/right_panel/RoomHeaderButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 8620d5b485..74bd2c51fc 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -45,7 +45,7 @@ export default class RoomHeaderButtons extends HeaderButtons { super.onAction(payload); if (payload.action === Action.ViewUser) { if (payload.member) { - this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); + this.setPhase(RightPanelPhases.RoomMemberInfo, {members: payload.member}); } else { this.setPhase(RightPanelPhases.RoomMemberList); } From a4959f43d2882cf42bdd06385657e0f11db7c55a Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 17:56:53 +0530 Subject: [PATCH 008/136] Convert GroupHeaderButtons to TS Fix typo in SetRightPanelPhasePayload --- .../{GroupHeaderButtons.js => GroupHeaderButtons.tsx} | 11 +++++++---- src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) rename src/components/views/right_panel/{GroupHeaderButtons.js => GroupHeaderButtons.tsx} (93%) diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.tsx similarity index 93% rename from src/components/views/right_panel/GroupHeaderButtons.js rename to src/components/views/right_panel/GroupHeaderButtons.tsx index fb589b1a0f..7bd6d90038 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.js +++ b/src/components/views/right_panel/GroupHeaderButtons.tsx @@ -25,6 +25,7 @@ import HeaderButtons, {HeaderKind} from './HeaderButtons'; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from "../../../dispatcher/actions"; import {ActionPayload} from "../../../dispatcher/payloads"; +import {ViewUserPayload} from "../../../dispatcher/payloads/ViewUserPayload"; const GROUP_PHASES = [ RightPanelPhases.GroupMemberInfo, @@ -35,8 +36,10 @@ const ROOM_PHASES = [ RightPanelPhases.GroupRoomInfo, ]; +interface IProps {} + export default class GroupHeaderButtons extends HeaderButtons { - constructor(props) { + constructor(props: IProps) { super(props, HeaderKind.Group); this._onMembersClicked = this._onMembersClicked.bind(this); this._onRoomsClicked = this._onRoomsClicked.bind(this); @@ -46,8 +49,8 @@ export default class GroupHeaderButtons extends HeaderButtons { super.onAction(payload); if (payload.action === Action.ViewUser) { - if (payload.member) { - this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); + if ((payload as ViewUserPayload).member) { + this.setPhase(RightPanelPhases.RoomMemberInfo, {members: payload.member}); } else { this.setPhase(RightPanelPhases.GroupMemberList); } @@ -63,7 +66,7 @@ export default class GroupHeaderButtons extends HeaderButtons { } else if (payload.action === "view_group_member_list") { this.setPhase(RightPanelPhases.GroupMemberList); } else if (payload.action === "view_group_user") { - this.setPhase(RightPanelPhases.GroupMemberInfo, {member: payload.member}); + this.setPhase(RightPanelPhases.GroupMemberInfo, {members: payload.member}); } } diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 94e38b7ce6..4b360d294a 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -31,7 +31,7 @@ export interface SetRightPanelPhaseRefireParams { // No appropriate types exist yet for the fields members?: any; verificationRequest?: typeof VerificationRequest; - groudId?: string; + groupId?: string; groupRoomId?: string; // XXX: 'view_3pid_invite' action's payload event?: any; From dac19cffce4ffc4a329aad424900187755a3cddc Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 22:44:56 +0530 Subject: [PATCH 009/136] Convert EncryptionInfo to TS --- .../{EncryptionInfo.js => EncryptionInfo.tsx} | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) rename src/components/views/right_panel/{EncryptionInfo.js => EncryptionInfo.tsx} (87%) diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.tsx similarity index 87% rename from src/components/views/right_panel/EncryptionInfo.js rename to src/components/views/right_panel/EncryptionInfo.tsx index 007e2831ce..2d0e3f7d46 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.tsx @@ -15,10 +15,10 @@ limitations under the License. */ import React from "react"; -import PropTypes from "prop-types"; import * as sdk from "../../../index"; import {_t} from "../../../languageHandler"; +import {RoomMember} from "matrix-js-sdk/src/models/room-member" export const PendingActionSpinner = ({text}) => { const Spinner = sdk.getComponent('elements.Spinner'); @@ -28,7 +28,17 @@ export const PendingActionSpinner = ({text}) => {
; }; -const EncryptionInfo = ({ +interface IProps { + waitingForOtherParty: boolean; + waitingForNetwork: boolean; + member: RoomMember; + onStartVerification: () => Promise; + isRoomEncrypted: boolean; + inDialog: boolean; + isSelfVerification: boolean; +} + +const EncryptionInfo: React.FC = ({ waitingForOtherParty, waitingForNetwork, member, @@ -36,10 +46,10 @@ const EncryptionInfo = ({ isRoomEncrypted, inDialog, isSelfVerification, -}) => { - let content; +}: IProps) => { + let content: JSX.Element; if (waitingForOtherParty || waitingForNetwork) { - let text; + let text: string; if (waitingForOtherParty) { if (isSelfVerification) { text = _t("Waiting for you to accept on your other session…"); @@ -61,7 +71,7 @@ const EncryptionInfo = ({ ); } - let description; + let description: JSX.Element; if (isRoomEncrypted) { description = (
@@ -97,10 +107,5 @@ const EncryptionInfo = ({
; }; -EncryptionInfo.propTypes = { - member: PropTypes.object.isRequired, - onStartVerification: PropTypes.func.isRequired, - request: PropTypes.object, -}; export default EncryptionInfo; From 2f0caab8514261f28ef58a9a2a70668bcf61ffc8 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 22:45:22 +0530 Subject: [PATCH 010/136] "fix" type for members in SetRightPanelPhasePayload --- src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 4b360d294a..49e3032d4f 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; @@ -29,7 +30,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload { export interface SetRightPanelPhaseRefireParams { // XXX: Fix after the types are defiend in matrix-js-sdk // No appropriate types exist yet for the fields - members?: any; + members?: RoomMember; verificationRequest?: typeof VerificationRequest; groupId?: string; groupRoomId?: string; From 36974c423162eb25b52c7d0c572a8582f72bebce Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 23:09:11 +0530 Subject: [PATCH 011/136] Convert EncryptionPanel to TS --- ...EncryptionPanel.js => EncryptionPanel.tsx} | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) rename src/components/views/right_panel/{EncryptionPanel.js => EncryptionPanel.tsx} (91%) diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.tsx similarity index 91% rename from src/components/views/right_panel/EncryptionPanel.js rename to src/components/views/right_panel/EncryptionPanel.tsx index e9f94729fa..77af180db1 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -15,7 +15,6 @@ limitations under the License. */ import React, {useCallback, useEffect, useState} from "react"; -import PropTypes from "prop-types"; import EncryptionInfo from "./EncryptionInfo"; import VerificationPanel from "./VerificationPanel"; @@ -26,11 +25,23 @@ import Modal from "../../../Modal"; import {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import * as sdk from "../../../index"; import {_t} from "../../../languageHandler"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; // cancellation codes which constitute a key mismatch const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"]; -const EncryptionPanel = (props) => { +interface IProps { + member: RoomMember; + onClose: () => void; + verificationRequest: VerificationRequest; + verificationRequestPromise: Promise; + layout: string; + inDialog: boolean; + isRoomEncrypted: boolean; +} + +const EncryptionPanel: React.FC = (props: IProps) => { const {verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted} = props; const [request, setRequest] = useState(verificationRequest); // state to show a spinner immediately after clicking "start verification", @@ -90,7 +101,7 @@ const EncryptionPanel = (props) => { } }, [request]); - let cancelButton; + let cancelButton: JSX.Element; if (layout !== "dialog" && request && request.pending) { const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); cancelButton = ( { ); } }; -EncryptionPanel.propTypes = { - member: PropTypes.object.isRequired, - onClose: PropTypes.func.isRequired, - verificationRequest: PropTypes.object, - layout: PropTypes.string, - inDialog: PropTypes.bool, -}; export default EncryptionPanel; From 3e64ec11c01c06668e5d63f941e551a1f18d3ebf Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 23:09:24 +0530 Subject: [PATCH 012/136] lint --- src/components/views/right_panel/EncryptionInfo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/right_panel/EncryptionInfo.tsx b/src/components/views/right_panel/EncryptionInfo.tsx index 2d0e3f7d46..f62af65543 100644 --- a/src/components/views/right_panel/EncryptionInfo.tsx +++ b/src/components/views/right_panel/EncryptionInfo.tsx @@ -18,7 +18,7 @@ import React from "react"; import * as sdk from "../../../index"; import {_t} from "../../../languageHandler"; -import {RoomMember} from "matrix-js-sdk/src/models/room-member" +import {RoomMember} from "matrix-js-sdk/src/models/room-member"; export const PendingActionSpinner = ({text}) => { const Spinner = sdk.getComponent('elements.Spinner'); From 8fac7a81672a7ee5aa4612abca4c0dc2f4bd580c Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 23:51:46 +0530 Subject: [PATCH 013/136] Convert VerificationPanel to TS --- ...ficationPanel.js => VerificationPanel.tsx} | 153 +++++++++++------- 1 file changed, 93 insertions(+), 60 deletions(-) rename src/components/views/right_panel/{VerificationPanel.js => VerificationPanel.tsx} (79%) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.tsx similarity index 79% rename from src/components/views/right_panel/VerificationPanel.js rename to src/components/views/right_panel/VerificationPanel.tsx index 0b6790eac8..c2c065c022 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -15,12 +15,15 @@ limitations under the License. */ import React from "react"; -import PropTypes from "prop-types"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import * as sdk from '../../../index'; import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode"; +import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import {RoomMember} from "matrix-js-sdk/src/models/room-member"; +import {ReciprocateQRCode} from "matrix-js-sdk/src/crypto/verification/QRCode"; +import {SAS} from "matrix-js-sdk/src/crypto/verification/SAS"; import VerificationQRCode from "../elements/crypto/VerificationQRCode"; import {_t} from "../../../languageHandler"; @@ -36,37 +39,67 @@ import { } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import Spinner from "../elements/Spinner"; -export default class VerificationPanel extends React.PureComponent { - static propTypes = { - layout: PropTypes.string, - request: PropTypes.object.isRequired, - member: PropTypes.object.isRequired, - phase: PropTypes.oneOf([ - PHASE_UNSENT, - PHASE_REQUESTED, - PHASE_READY, - PHASE_STARTED, - PHASE_CANCELLED, - PHASE_DONE, - ]).isRequired, - onClose: PropTypes.func.isRequired, - isRoomEncrypted: PropTypes.bool, - }; +// XXX: Should be defined in matrix-js-sdk +enum VerificationPhase { + PHASE_UNSENT, + PHASE_REQUESTED, + PHASE_READY, + PHASE_DONE, + PHASE_STARTED, + PHASE_CANCELLED, +} - constructor(props) { +interface IProps { + layout: string; + request: VerificationRequest; + member: RoomMember; + phase: VerificationPhase; + onClose: () => void; + isRoomEncrypted: boolean; + inDialog: boolean; + key: any; +} + +interface IState { + sasEvent?: SAS; + emojiButtonClicked?: boolean; + reciprocateButtonClicked?: boolean; + reciprocateQREvent?: ReciprocateQRCode; +} + +export default class VerificationPanel extends React.PureComponent { + /* static propTypes = { */ + /* layout: PropTypes.string, */ + /* request: PropTypes.object.isRequired, */ + /* member: PropTypes.object.isRequired, */ + /* phase: PropTypes.oneOf([ */ + /* PHASE_UNSENT, */ + /* PHASE_REQUESTED, */ + /* PHASE_READY, */ + /* PHASE_STARTED, */ + /* PHASE_CANCELLED, */ + /* PHASE_DONE, */ + /* ]).isRequired, */ + /* onClose: PropTypes.func.isRequired, */ + /* isRoomEncrypted: PropTypes.bool, */ + /* }; */ + + private hasVerifier: boolean; + + constructor(props: IProps) { super(props); this.state = {}; - this._hasVerifier = false; + this.hasVerifier = false; } renderQRPhase() { const {member, request} = this.props; - const showSAS = request.otherPartySupportsMethod(verificationMethods.SAS); - const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD); + const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS); + const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const brand = SdkConfig.get().brand; - const noCommonMethodError = !showSAS && !showQR ? + const noCommonMethodError: JSX.Element = !showSAS && !showQR ?

{_t( "The session you are trying to verify doesn't support scanning a " + "QR code or emoji verification, which is what %(brand)s supports. Try " + @@ -77,8 +110,8 @@ export default class VerificationPanel extends React.PureComponent { if (this.props.layout === 'dialog') { // HACK: This is a terrible idea. - let qrBlock; - let sasBlock; + let qrBlock: JSX.Element; + let sasBlock: JSX.Element; if (showQR) { qrBlock =

@@ -91,7 +124,7 @@ export default class VerificationPanel extends React.PureComponent {

{_t("Compare unique emoji")}

{_t("Compare a unique set of emoji if you don't have a camera on either device")} - + {_t("Start")}
; @@ -111,7 +144,7 @@ export default class VerificationPanel extends React.PureComponent { ); } - let qrBlock; + let qrBlock: JSX.Element; if (showQR) { qrBlock =

{_t("Verify by scanning")}

@@ -125,7 +158,7 @@ export default class VerificationPanel extends React.PureComponent {
; } - let sasBlock; + let sasBlock: JSX.Element; if (showSAS) { const disabled = this.state.emojiButtonClicked; const sasLabel = showQR ? @@ -140,7 +173,7 @@ export default class VerificationPanel extends React.PureComponent { disabled={disabled} kind="primary" className="mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton" - onClick={this._startSAS} + onClick={this.startSAS} > {_t("Verify by emoji")} @@ -159,17 +192,17 @@ export default class VerificationPanel extends React.PureComponent { ; } - _onReciprocateYesClick = () => { + private onReciprocateYesClick = () => { this.setState({reciprocateButtonClicked: true}); this.state.reciprocateQREvent.confirm(); }; - _onReciprocateNoClick = () => { + private onReciprocateNoClick = () => { this.setState({reciprocateButtonClicked: true}); this.state.reciprocateQREvent.cancel(); }; - _getDevice() { + private getDevice() { const deviceId = this.props.request && this.props.request.channel.deviceId; return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId); } @@ -189,7 +222,7 @@ export default class VerificationPanel extends React.PureComponent { _t("Almost there! Is %(displayName)s showing the same shield?", { displayName: member.displayName || member.name || member.userId, }); - let body; + let body: JSX.Element; if (this.state.reciprocateQREvent) { // riot web doesn't support scanning yet, so assume here we're the client being scanned. // @@ -202,11 +235,11 @@ export default class VerificationPanel extends React.PureComponent { + onClick={this.onReciprocateNoClick}>{_t("No")} + onClick={this.onReciprocateYesClick}>{_t("Yes")}
; } else { @@ -221,7 +254,7 @@ export default class VerificationPanel extends React.PureComponent { renderVerifiedPhase() { const {member, request} = this.props; - let text; + let text: string; if (!request.isSelfVerification) { if (this.props.isRoomEncrypted) { text = _t("Verify all users in a room to ensure it's secure."); @@ -230,9 +263,9 @@ export default class VerificationPanel extends React.PureComponent { } } - let description; + let description: string; if (request.isSelfVerification) { - const device = this._getDevice(); + const device = this.getDevice(); if (!device) { // This can happen if the device is logged out while we're still showing verification // UI for it. @@ -269,14 +302,14 @@ export default class VerificationPanel extends React.PureComponent { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - let startAgainInstruction; + let startAgainInstruction: string; if (request.isSelfVerification) { startAgainInstruction = _t("Start verification again from the notification."); } else { startAgainInstruction = _t("Start verification again from their profile."); } - let text; + let text: string; if (request.cancellationCode === "m.timeout") { text = _t("Verification timed out.") + ` ${startAgainInstruction}`; } else if (request.cancellingUserId === request.otherUserId) { @@ -321,10 +354,10 @@ export default class VerificationPanel extends React.PureComponent { const emojis = this.state.sasEvent ? : ; @@ -345,7 +378,7 @@ export default class VerificationPanel extends React.PureComponent { return null; } - _startSAS = async () => { + private startSAS = async () => { this.setState({emojiButtonClicked: true}); const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS); try { @@ -355,31 +388,31 @@ export default class VerificationPanel extends React.PureComponent { } }; - _onSasMatchesClick = () => { + private onSasMatchesClick = () => { this.state.sasEvent.confirm(); }; - _onSasMismatchesClick = () => { + private onSasMismatchesClick = () => { this.state.sasEvent.mismatch(); }; - _updateVerifierState = () => { + private updateVerifierState = () => { const {request} = this.props; const {sasEvent, reciprocateQREvent} = request.verifier; - request.verifier.off('show_sas', this._updateVerifierState); - request.verifier.off('show_reciprocate_qr', this._updateVerifierState); + request.verifier.off('show_sas', this.updateVerifierState); + request.verifier.off('show_reciprocate_qr', this.updateVerifierState); this.setState({sasEvent, reciprocateQREvent}); }; - _onRequestChange = async () => { + private onRequestChange = async () => { const {request} = this.props; - const hadVerifier = this._hasVerifier; - this._hasVerifier = !!request.verifier; - if (!hadVerifier && this._hasVerifier) { - request.verifier.on('show_sas', this._updateVerifierState); - request.verifier.on('show_reciprocate_qr', this._updateVerifierState); + const hadVerifier = this.hasVerifier; + this.hasVerifier = !!request.verifier; + if (!hadVerifier && this.hasVerifier) { + request.verifier.on('show_sas', this.updateVerifierState); + request.verifier.on('show_reciprocate_qr', this.updateVerifierState); try { - // on the requester side, this is also awaited in _startSAS, + // on the requester side, this is also awaited in startSAS, // but that's ok as verify should return the same promise. await request.verifier.verify(); } catch (err) { @@ -390,21 +423,21 @@ export default class VerificationPanel extends React.PureComponent { componentDidMount() { const {request} = this.props; - request.on("change", this._onRequestChange); + request.on("change", this.onRequestChange); if (request.verifier) { const {request} = this.props; const {sasEvent, reciprocateQREvent} = request.verifier; this.setState({sasEvent, reciprocateQREvent}); } - this._onRequestChange(); + this.onRequestChange(); } componentWillUnmount() { const {request} = this.props; if (request.verifier) { - request.verifier.off('show_sas', this._updateVerifierState); - request.verifier.off('show_reciprocate_qr', this._updateVerifierState); + request.verifier.off('show_sas', this.updateVerifierState); + request.verifier.off('show_reciprocate_qr', this.updateVerifierState); } - request.off("change", this._onRequestChange); + request.off("change", this.onRequestChange); } } From 5ddae04fb0ac89c3e73900fddd418c5a2cdb7586 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 23:51:56 +0530 Subject: [PATCH 014/136] lint --- src/components/views/right_panel/EncryptionPanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 77af180db1..8d5530cfa3 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -25,8 +25,8 @@ import Modal from "../../../Modal"; import {PHASE_REQUESTED, PHASE_UNSENT} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import * as sdk from "../../../index"; import {_t} from "../../../languageHandler"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; -import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import {RoomMember} from "matrix-js-sdk/src/models/room-member"; // cancellation codes which constitute a key mismatch const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"]; From 0f0b4035b73eb74a1e103ed6f550c7f633c12564 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Sat, 18 Jul 2020 23:52:41 +0530 Subject: [PATCH 015/136] Fix shadow variable errors --- .../views/right_panel/VerificationPanel.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index c2c065c022..d2ee728438 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -110,17 +110,17 @@ export default class VerificationPanel extends React.PureComponent

{_t("Scan this unique code")}

; } if (showSAS) { - sasBlock = + sasBlockDialog =

{_t("Compare unique emoji")}

{_t("Compare a unique set of emoji if you don't have a camera on either device")} @@ -129,15 +129,15 @@ export default class VerificationPanel extends React.PureComponent
; } - const or = qrBlock && sasBlock ? + const or = qrBlockDialog && sasBlockDialog ?
{_t("or")}
: null; return (
{_t("Verify this session by completing one of the following:")}
- {qrBlock} + {qrBlockDialog} {or} - {sasBlock} + {sasBlockDialog} {noCommonMethodError}
@@ -425,7 +425,6 @@ export default class VerificationPanel extends React.PureComponent Date: Sat, 18 Jul 2020 23:55:28 +0530 Subject: [PATCH 016/136] Fix shadow-variable errors in EncryptionPanel --- src/components/views/right_panel/EncryptionPanel.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 8d5530cfa3..df52e5cabd 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -59,10 +59,10 @@ const EncryptionPanel: React.FC = (props: IProps) => { useEffect(() => { async function awaitPromise() { setRequesting(true); - const request = await verificationRequestPromise; + const requestFromPromise = await verificationRequestPromise; setRequesting(false); - setRequest(request); - setPhase(request.phase); + setRequest(requestFromPromise); + setPhase(requestFromPromise.phase); } if (verificationRequestPromise) { awaitPromise(); @@ -115,9 +115,9 @@ const EncryptionPanel: React.FC = (props: IProps) => { setRequesting(true); const cli = MatrixClientPeg.get(); const roomId = await ensureDMExists(cli, member.userId); - const verificationRequest = await cli.requestVerificationDM(member.userId, roomId); - setRequest(verificationRequest); - setPhase(verificationRequest.phase); + const verificationRequest_ = await cli.requestVerificationDM(member.userId, roomId); + setRequest(verificationRequest_); + setPhase(verificationRequest_.phase); }, [member.userId]); const requested = From d7728881a3bf9053f35bddfaa61d5be74e12d801 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 22 Jul 2020 15:22:16 +0530 Subject: [PATCH 017/136] lint --- src/components/views/groups/GroupMemberList.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index e7143f0dd2..031b875409 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -24,8 +24,6 @@ import GroupStore from '../../../stores/GroupStore'; import PropTypes from 'prop-types'; import { showGroupInviteDialog } from '../../../GroupAddressPicker'; import AccessibleButton from '../elements/AccessibleButton'; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; -import TintableSvg from '../elements/TintableSvg'; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import {Action} from "../../../dispatcher/actions"; From 5ea7be5d530f5ebad14fc7ca9800b0d5c9cb6ce0 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 22 Jul 2020 21:17:58 +0530 Subject: [PATCH 018/136] Cleanup comments --- .../views/right_panel/VerificationPanel.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index d2ee728438..a4180bcd8a 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -68,22 +68,6 @@ interface IState { } export default class VerificationPanel extends React.PureComponent { - /* static propTypes = { */ - /* layout: PropTypes.string, */ - /* request: PropTypes.object.isRequired, */ - /* member: PropTypes.object.isRequired, */ - /* phase: PropTypes.oneOf([ */ - /* PHASE_UNSENT, */ - /* PHASE_REQUESTED, */ - /* PHASE_READY, */ - /* PHASE_STARTED, */ - /* PHASE_CANCELLED, */ - /* PHASE_DONE, */ - /* ]).isRequired, */ - /* onClose: PropTypes.func.isRequired, */ - /* isRoomEncrypted: PropTypes.bool, */ - /* }; */ - private hasVerifier: boolean; constructor(props: IProps) { From 499d9a513d05077fc62f47e55da4bd36b1c25868 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 18:40:44 +0000 Subject: [PATCH 019/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index c248189b23..8e99862732 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1139,7 +1139,7 @@ "Invite users": "Convidar usuários", "Use Single Sign On to continue": "Use \"Single Sign On\" para continuar", "Confirm adding this email address by using Single Sign On to prove your identity.": "Confirme a inclusão deste endereço de correio eletrônico usando o Single Sign On para comprovar sua identidade.", - "Single Sign On": "Single Sign On", + "Single Sign On": "Autenticação Única", "Confirm adding email": "Confirmar a inclusão de email", "Click the button below to confirm adding this email address.": "Clique no botão abaixo para confirmar a adição deste endereço de email.", "Confirm": "Confirmar", @@ -1215,7 +1215,7 @@ "Opens chat with the given user": "Abre um chat com determinada pessoa", "Sends a message to the given user": "Envia uma mensagem com determinada pessoa", "%(senderName)s made no change.": "%(senderName)s não fez nenhuma alteração.", - "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s alterou o nome da sala de %(oldRoomName)s para", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s alterou o nome da sala de %(oldRoomName)s para %(newRoomName)s.", "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s adicionou os endereços alternativos %(addresses)s para esta sala.", "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s adicionou o endereço alternativo %(addresses)s para esta sala.", "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s removeu os endereços alternativos %(addresses)s para esta sala.", @@ -1228,10 +1228,10 @@ "%(senderName)s placed a video call.": "%(senderName)s iniciou uma chamada de vídeo.", "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s iniciou uma chamada de vídeo. (não suportada por este navegador)", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s cancelou o convite a %(targetDisplayName)s para entrar na sala.", - "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s removeu a regra banindo usuárias(os) correspondendo a", - "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s removeu uma regra banindo salas correspondendo a", - "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s removeu a regra banindo servidores correspondendo a", - "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s removeu uma regra de banimento correspondendo a", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s removeu a regra que bane usuárias(os) que correspondem a %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s removeu a regra que bane salas que correspondem a %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s removeu a regra que bane servidores que correspondem a %(glob)s", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s removeu uma regra de banimento correspondendo a %(glob)s", "%(senderName)s updated an invalid ban rule": "%(senderName)s atualizou uma regra de banimento inválida", "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s atualizou a regra de banimento de usuárias(os) correspondendo a %(glob)s por %(reason)s", "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s atualizou a regra banindo salas correspondendo a %(glob)s por %(reason)s", @@ -1283,7 +1283,7 @@ "%(num)s days from now": "dentro de %(num)s dias", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", "The user's homeserver does not support the version of the room.": "O servidor desta(e) usuária(o) não suporta a versão desta sala.", - "Help us improve %(brand)s": "Ajude-nos a melhorar o", + "Help us improve %(brand)s": "Ajude-nos a melhorar %(brand)s", "Send anonymous usage data which helps us improve %(brand)s. This will use a cookie.": "Envie dados anônimos de uso que nos ajudam a melhorar o %(brand)s. Isso necessitará do uso de um cookie.", "I want to help": "Quero ajudar", "Review where you’re logged in": "Revisar onde você está logada(o)", @@ -1291,7 +1291,7 @@ "Review": "Revisar", "Later": "Mais tarde", "Your homeserver has exceeded its user limit.": "Seu servidor ultrapassou seu limite de usuárias(os).", - "Your homeserver has exceeded one of its resource limits.": "Seu servidor excedeu um de seus limites de recursos", + "Your homeserver has exceeded one of its resource limits.": "Seu servidor excedeu um de seus limites de recursos.", "Contact your server admin.": "Entre em contato com sua(seu) administrador(a) do servidor.", "Ok": "Ok", "Set password": "Definir senha", @@ -1301,12 +1301,12 @@ "Verify this session": "Verificar esta sessão", "Upgrade": "Atualizar", "Verify": "Verificar", - "Verify yourself & others to keep your chats safe": "Faça a sua auto-verificação e verifique seus contatos para manter suas conversas seguras!", + "Verify yourself & others to keep your chats safe": "Verifique a sua conta e as dos seus contatos, para manter suas conversas seguras", "Other users may not trust it": "Outras(os) usuárias(os) podem não confiar nela", "New login. Was this you?": "Novo login. Foi você?", - "Verify the new login accessing your account: %(name)s": "Verifique o novo login acessando sua conta:", + "Verify the new login accessing your account: %(name)s": "Verifique o novo login acessando sua conta: %(name)s", "Restart": "Reiniciar", - "Upgrade your %(brand)s": "Atualize seu", + "Upgrade your %(brand)s": "Atualize o seu %(brand)s", "A new version of %(brand)s is available!": "Uma nova versão do %(brand)s está disponível!", "Guest": "Convidada(o)", "You joined the call": "Você entrou na chamada", @@ -1319,10 +1319,10 @@ "%(senderName)s started a call": "%(senderName)s iniciou uma chamada", "Waiting for answer": "Esperando por uma resposta", "%(senderName)s is calling": "%(senderName)s está chamando", - "* %(senderName)s %(emote)s": "* %(senderName)s", - "%(senderName)s: %(message)s": "%(senderName)s:", - "%(senderName)s: %(reaction)s": "%(senderName)s:", - "%(senderName)s: %(stickerName)s": "%(senderName)s:", + "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", + "%(senderName)s: %(message)s": "%(senderName)s: %(message)s", + "%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s", + "%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s", "New spinner design": "Novo design do spinner", "Multiple integration managers": "Múltiplos gestores de integrações", "Try out new ways to ignore people (experimental)": "Tente novas maneiras de ignorar pessoas (experimental)", @@ -1378,14 +1378,14 @@ "Decline (%(counter)s)": "Recusar (%(counter)s)", "Accept to continue:": "Aceitar para continuar:", "Upload": "Enviar", - "This bridge was provisioned by .": "Esta ponte foi disponibilizada por", + "This bridge was provisioned by .": "Esta ponte foi disponibilizada por .", "This bridge is managed by .": "Esta ponte é gerida por .", - "Workspace: %(networkName)s": "Espaço de trabalho:", - "Channel: %(channelName)s": "Canal:", + "Workspace: %(networkName)s": "Espaço de trabalho: %(networkName)s", + "Channel: %(channelName)s": "Canal: %(channelName)s", "Show less": "Mostrar menos", "Show more": "Mostrar mais", "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 mudar a senha, você apagará quaisquer chaves de criptografia ponta-a-ponta existentes em todas as sessões, fazendo com que o histórico de conversas criptografadas fique ilegível, a não ser que você exporte as salas das chaves criptografadas antes de mudar a senha e então as importe novamente depois. No futuro, isso será melhorado.", - "Your homeserver does not support cross-signing.": "Seu servidor não suporta assinatura cruzada", + "Your homeserver does not support cross-signing.": "Seu servidor não suporta assinatura cruzada.", "Cross-signing and secret storage are enabled.": "Assinaturas cruzadas e armazenamento secreto estão habilitadas.", "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Sua conta tem uma identidade de assinatura cruzada em um armazenamento secreto, mas ainda não é considerada confiável por esta sessão.", "Cross-signing and secret storage are not yet set up.": "A assinatura cruzada e o armazenamento seguro ainda não foram configurados.", @@ -1439,13 +1439,13 @@ "not stored": "não armazenado", "Backup has a valid signature from this user": "A cópia de segurança (backup) tem uma assinatura válida deste(a) usuário(a)", "Backup has a invalid signature from this user": "A cópia de segurança (backup) tem uma assinatura inválida deste(a) usuário(a)", - "Backup has a signature from unknown user with ID %(deviceId)s": "Fazer cópia de segurança (backup) de usuária(o) desconhecida(o) com ID", - "Backup has a signature from unknown session with ID %(deviceId)s": "Fazer cópia de segurança (backup) de uma sessão desconhecida com ID", + "Backup has a signature from unknown user with ID %(deviceId)s": "A cópia de segurança tem uma assinatura de um(a) usuário desconhecido com ID %(deviceId)s", + "Backup has a signature from unknown session with ID %(deviceId)s": "A cópia de segurança tem uma assinatura de uma sessão desconhecida com ID %(deviceId)s", "Backup has a valid signature from this session": "A cópia de segurança (backup) tem uma assinatura válida desta sessão", "Backup has an invalid signature from this session": "A cópia de segurança (backup) tem uma assinatura inválida desta sessão", "Backup has a valid signature from verified session ": "A cópia de segurança (backup) tem uma assinatura válida da sessão verificada ", - "Backup has a valid signature from unverified session ": "A cópia de segurança (backup) tem uma assinatura válida da sessão não verificada", - "Backup has an invalid signature from verified session ": "A cópia de segurança (backup) tem uma assinatura inválida de uma sessão verificada", + "Backup has a valid signature from unverified session ": "A cópia de segurança tem uma assinatura válida de uma sessão não verificada ", + "Backup has an invalid signature from verified session ": "A cópia de segurança tem uma assinatura inválida de uma sessão verificada ", "Backup has an invalid signature from unverified session ": "A cópia de segurança (backup) tem uma assinatura inválida de uma sessão não verificada ", "Backup is not signed by any of your sessions": "A cópia de segurança (backup) não foi assinada por nenhuma de suas sessões", "This backup is trusted because it has been restored on this session": "Esta cópia de segurança (backup) é confiável, pois foi restaurada nesta sessão", @@ -1531,7 +1531,7 @@ "You'll lose access to your encrypted messages": "Você perderá acesso às suas mensagens criptografadas", "Session key": "Chave da sessão", "Verify session": "Verificar sessão", - "We recommend you change your password and recovery key in Settings immediately": "Nós recomendamos que você altere imediatamente sua senha e chave de recuperação nas configurações.", + "We recommend you change your password and recovery key in Settings immediately": "Nós recomendamos que você altere imediatamente sua senha e chave de recuperação nas Configurações", "Use this session to verify your new one, granting it access to encrypted messages:": "Use esta sessão para verificar a sua nova sessão, dando a ela acesso às mensagens criptografadas:", "You’re already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at element.io/get-started.": "Você já está logada(o) e pode começar a usar à vontade, mas você também pode buscar pelas últimas versões do app em todas as plataformas em element.io/get-started.", "Go to Element": "Ir a Element", @@ -1560,7 +1560,7 @@ "Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Está faltando a chave pública do captcha no Servidor (homeserver). Por favor, reporte isso aos(às) administradores(as) do servidor.", "Enter the location of your Element Matrix Services homeserver. It may use your own domain name or be a subdomain of element.io.": "Entre com a localização do seu Servidor Matrix. Pode ser seu próprio domínio ou ser um subdomínio de element.io.", "Create your Matrix account on %(serverName)s": "Criar sua conta Matrix em %(serverName)s", - "Create your Matrix account on ": "Criar sua conta Matrix em", + "Create your Matrix account on ": "Crie sua conta Matrix em ", "Welcome to %(appName)s": "Desejamos boas vindas ao %(appName)s", "Liberate your communication": "Liberte sua comunicação", "Send a Direct Message": "Envie uma mensagem direta", From a6edb09c7011134af0a741b61367e7ed765736a9 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 27 Jul 2020 19:04:54 +0000 Subject: [PATCH 020/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 8e99862732..f9abeee1fb 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1147,7 +1147,7 @@ "Confirm adding phone number": "Confirmar adição de número de telefone", "Add Phone Number": "Adicionar número de telefone", "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Se estiver usando %(brand)s em um aparelho onde touch é o mecanismo primário de entrada de dados", - "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está usando a funcionalidade 'breadcrumbs' (imagens acima da lista de salas)", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está utilizando a opção de 'breadcrumbs' (ícones sobre a lista de de salas)", "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração do servidor", From 4420cd6cf9dc818bf91bd80a9b422b376452a966 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:04:59 +0000 Subject: [PATCH 021/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index f9abeee1fb..bc07dd5424 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1147,7 +1147,7 @@ "Confirm adding phone number": "Confirmar adição de número de telefone", "Add Phone Number": "Adicionar número de telefone", "Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Se estiver usando %(brand)s em um aparelho onde touch é o mecanismo primário de entrada de dados", - "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está utilizando a opção de 'breadcrumbs' (ícones sobre a lista de de salas)", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está usando ou não a funcionalidade 'breadcrumbs' (fotos acima da lista de salas)", "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração do servidor", From 6b1b1300bff60e0a211c8241aaf51b5d031469d3 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 27 Jul 2020 19:07:54 +0000 Subject: [PATCH 022/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index bc07dd5424..3ba5d27daf 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1150,7 +1150,7 @@ "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está usando ou não a funcionalidade 'breadcrumbs' (fotos acima da lista de salas)", "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", - "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração do servidor", + "Call failed due to misconfigured server": "Chamada falhou devido a falha de configuração no servidor", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça aos administradores do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN para que as chamadas funcionem de forma estável.", "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.": "Alternativamente, pode tentar usar o servidor público em turn.matrix.org, mas não será tão fiável e partilhará o seu IP com esse servidor. Também pode gerir isso nas definições.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", From e4d14b4ddd4460e22ac245ec893a909f55d3536e Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:08:02 +0000 Subject: [PATCH 023/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 3ba5d27daf..2695d44746 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1150,7 +1150,7 @@ "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Se você está usando ou não a funcionalidade 'breadcrumbs' (fotos acima da lista de salas)", "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", - "Call failed due to misconfigured server": "Chamada falhou devido a falha de configuração no servidor", + "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça aos administradores do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN para que as chamadas funcionem de forma estável.", "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.": "Alternativamente, pode tentar usar o servidor público em turn.matrix.org, mas não será tão fiável e partilhará o seu IP com esse servidor. Também pode gerir isso nas definições.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", From fc63b931368fb4470001f56006c0047c92af0bc6 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 27 Jul 2020 19:10:38 +0000 Subject: [PATCH 024/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 2695d44746..faa918f6d1 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1151,7 +1151,7 @@ "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", - "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça aos administradores do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN para que as chamadas funcionem de forma estável.", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Pergunte ao administrador do seu homeserver (%(homeserverDomain)s) para configurar um servidor TURN para que ligações funcionem com boa qualidade.", "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.": "Alternativamente, pode tentar usar o servidor público em turn.matrix.org, mas não será tão fiável e partilhará o seu IP com esse servidor. Também pode gerir isso nas definições.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", From 4e637b5ffd1f07d99386c4613ee9e57e96bc312f Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:10:54 +0000 Subject: [PATCH 025/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index faa918f6d1..ab6b9b78d4 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1151,7 +1151,7 @@ "Whether you're using %(brand)s as an installed Progressive Web App": "Se estiver usando %(brand)s como uma Progressive Web App (PWA)", "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", - "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Pergunte ao administrador do seu homeserver (%(homeserverDomain)s) para configurar um servidor TURN para que ligações funcionem com boa qualidade.", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", "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.": "Alternativamente, pode tentar usar o servidor público em turn.matrix.org, mas não será tão fiável e partilhará o seu IP com esse servidor. Também pode gerir isso nas definições.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", From 6e1ef2ed4c714f85b4f68e5f3dcc87c65c8dc796 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 27 Jul 2020 19:14:22 +0000 Subject: [PATCH 026/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index ab6b9b78d4..003c351a68 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1152,7 +1152,7 @@ "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", - "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.": "Alternativamente, pode tentar usar o servidor público em turn.matrix.org, mas não será tão fiável e partilhará o seu IP com esse servidor. Também pode gerir isso nas definições.", + "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.": "Alternativamente, voce pode tentar utilizar o servidor público turn.matrix.org, mas este servidor nao será tão estável, e o seu endereço IP será compartilhado com aquele servidor. Voce pode controlar isso em Configurações.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", From a623b1167d9d05aab98baad63c7994454faed2f7 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:14:27 +0000 Subject: [PATCH 027/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 003c351a68..3bc8c584ab 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1152,7 +1152,7 @@ "Your user agent": "Seu agente de usuária(o)", "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", - "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.": "Alternativamente, voce pode tentar utilizar o servidor público turn.matrix.org, mas este servidor nao será tão estável, e o seu endereço IP será compartilhado com aquele servidor. Voce pode controlar isso em Configurações.", + "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", From f586e8d6a13c1206a31ca74a4bb858559022d3ed Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Mon, 27 Jul 2020 19:15:21 +0000 Subject: [PATCH 028/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 3bc8c584ab..b30bd794be 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1153,7 +1153,7 @@ "Call failed due to misconfigured server": "A chamada caiu por conta de má configuração no servidor", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", - "Try using turn.matrix.org": "Tentar utilizar turn.matrix.org", + "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", From 30608bde64aa62b98bde73572926ee68d1177d48 Mon Sep 17 00:00:00 2001 From: KenjiKenjiIcantLetYouGo Date: Mon, 27 Jul 2020 19:16:04 +0000 Subject: [PATCH 029/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index b30bd794be..2c3d5e8968 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1154,7 +1154,7 @@ "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", - "Replying With Files": "Responder com arquivos", + "Replying With Files": "Respondendo Com Arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", From a973149803bae5f5124adb6d40f4f8283472f021 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:16:15 +0000 Subject: [PATCH 030/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 2c3d5e8968..b30bd794be 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1154,7 +1154,7 @@ "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor, peça ao administrador do seu servidor (%(homeserverDomain)s) para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável.", "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", - "Replying With Files": "Respondendo Com Arquivos", + "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", From 8a997dae0b49cd908e208c989e8ae60481ca0b53 Mon Sep 17 00:00:00 2001 From: KenjiKenjiIcantLetYouGo Date: Mon, 27 Jul 2020 19:18:55 +0000 Subject: [PATCH 031/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index b30bd794be..b24ac9f91c 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1155,7 +1155,7 @@ "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", - "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você quer fazer upload deste arquivo sem responder à mensagem?", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você gostaria de fazer o upload deste arquivo sem encaminha-lo?", "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", "Cancel entering passphrase?": "Cancelar a introdução da frase de senha?", From 2d887dad6a6217290561ac36a8bb4a40f4399b7b Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:18:59 +0000 Subject: [PATCH 032/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index b24ac9f91c..b341724153 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1155,7 +1155,7 @@ "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.": "Alternativamente, você pode tentar usar o servidor público em turn.matrix.org. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações.", "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", - "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Neste momento não é possível responder com um arquivo. Você gostaria de fazer o upload deste arquivo sem encaminha-lo?", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Momentaneamente, não é possível responder com um arquivo. Você quer fazer o envio deste arquivo sem responder a mensagem?", "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", "Cancel entering passphrase?": "Cancelar a introdução da frase de senha?", From 31cfcc8aab79509cd05fa96481f3c81f293dc79e Mon Sep 17 00:00:00 2001 From: KenjiKenjiIcantLetYouGo Date: Mon, 27 Jul 2020 19:19:23 +0000 Subject: [PATCH 033/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 73.0% (1695 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index b341724153..c5146a9898 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1156,7 +1156,7 @@ "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Momentaneamente, não é possível responder com um arquivo. Você quer fazer o envio deste arquivo sem responder a mensagem?", - "The file '%(fileName)s' failed to upload.": "O arquivo '%(fileName)s' não pôde ser enviado.", + "The file '%(fileName)s' failed to upload.": "O upload do arquivo '%(fileName)s' falhou", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", "Cancel entering passphrase?": "Cancelar a introdução da frase de senha?", "Are you sure you want to cancel entering passphrase?": "Tem certeza que quer cancelar a introdução da frase de senha?", From a9fcbcb677843817d87f706722441db5786d347a Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 19:19:39 +0000 Subject: [PATCH 034/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 75.7% (1758 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 69 +++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index c5146a9898..8d3ddafe48 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -29,7 +29,7 @@ "Failed to leave room": "Falha ao tentar deixar a sala", "Failed to reject invitation": "Falha ao tentar rejeitar convite", "Failed to unban": "Não foi possível desfazer o banimento", - "Favourite": "Favorito", + "Favourite": "Favoritar", "Favourites": "Favoritos", "Filter room members": "Filtrar integrantes da sala", "Forget room": "Esquecer sala", @@ -1156,7 +1156,7 @@ "Try using turn.matrix.org": "Tente utilizar turn.matrix.org", "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Momentaneamente, não é possível responder com um arquivo. Você quer fazer o envio deste arquivo sem responder a mensagem?", - "The file '%(fileName)s' failed to upload.": "O upload do arquivo '%(fileName)s' falhou", + "The file '%(fileName)s' failed to upload.": "O envio do arquivo '%(fileName)s' falhou.", "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", "Cancel entering passphrase?": "Cancelar a introdução da frase de senha?", "Are you sure you want to cancel entering passphrase?": "Tem certeza que quer cancelar a introdução da frase de senha?", @@ -1700,5 +1700,68 @@ "Verification Requests": "Solicitações de verificação", "Integrations are disabled": "As integrações estão desativadas", "Integrations not allowed": "As integrações não estão permitidas", - "End": "Fim" + "End": "Fim", + "List options": "Opções da Lista", + "Jump to first unread room.": "Ir para a primeira sala não lida.", + "Jump to first invite.": "Ir para o primeiro convite.", + "Add room": "Adicionar sala", + "Show %(count)s more|other": "Mostrar %(count)s a mais", + "Show %(count)s more|one": "Mostrar %(count)s a mais", + "Use default": "Usar o padrão", + "Room options": "Opções da Sala", + "%(count)s unread messages including mentions.|other": "%(count)s mensagens não lidas, incluindo menções.", + "%(count)s unread messages including mentions.|one": "1 menção não lida.", + "%(count)s unread messages.|other": "%(count)s mensagens não lidas.", + "%(count)s unread messages.|one": "1 mensagem não lida.", + "Unread messages.": "Mensagens não lidas.", + "This room is public": "Esta sala é pública", + "Away": "Ausente", + "This room has already been upgraded.": "Esta sala já foi atualizada.", + "This room is running room version , which this homeserver has marked as unstable.": "Esta sala está executando a versão , que este servidor marcou como instável.", + "Local address": "Endereço local", + "Published Addresses": "Endereços publicados", + "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.": "Os endereços publicados podem ser usados por qualquer pessoa em qualquer servidor para entrar na sala. Para publicar um endereço, primeiramente ele precisa ser definido como um endereço local.", + "Other published addresses:": "Outros endereços publicados:", + "New published address (e.g. #alias:server)": "Novo endereço publicado (por exemplo, #apelido:server)", + "Local Addresses": "Endereços locais", + "%(name)s cancelled verifying": "%(name)s cancelou a verificação", + "Your display name": "Seu nome de exibição", + "Your avatar URL": "A URL da sua foto de perfil", + "Your user ID": "Sua ID de usuário", + "%(brand)s URL": "URL de %(brand)s", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Se você usar esse widget, os dados poderão ser compartilhados com %(widgetDomain)s & seu Servidor de Integrações.", + "Using this widget may share data with %(widgetDomain)s.": "Se você usar esse widget, os dados poderão ser compartilhados com %(widgetDomain)s.", + "%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)s não fizeram alterações %(count)s vezes", + "%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)s não fizeram alterações", + "%(oneUser)smade no changes %(count)s times|other": "%(oneUser)s não fez alteraçõe s%(count)s vezes", + "%(oneUser)smade no changes %(count)s times|one": "%(oneUser)s não fez alterações", + "Power level": "Nível de permissão", + "Please provide a room address": "Digite um endereço para a sala", + "Looks good": "Muito bem", + "Are you sure you want to remove %(serverName)s": "Tem certeza de que deseja remover %(serverName)s", + "%(networkName)s rooms": "Salas em %(networkName)s", + "Matrix rooms": "Salas em Matrix", + "Close dialog": "Fechar caixa de diálogo", + "GitHub issue": "Questão no GitHub", + "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.": "Se houver um contexto adicional que ajude a analisar o problema, tal como o que você estava fazendo no momento, IDs de salas, IDs de usuários etc, inclua essas coisas aqui.", + "Topic (optional)": "Descrição (opcional)", + "There was a problem communicating with the server. Please try again.": "Ocorreu um problema na comunicação com o servidor. Por favor, tente novamente.", + "Server did not require any authentication": "O servidor não exigiu autenticação", + "Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Se você verificar esse usuário, a sessão será marcada como confiável para você e para ele.", + "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Verificar este aparelho o marcará como confiável, e os usuários que confirmaram com você também confiarão neste aparelho.", + "Keep going...": "Continue...", + "The username field must not be blank.": "O campo do nome de usuário não pode ficar em branco.", + "Username": "Nome de usuário", + "Use an email address to recover your account": "Use um endereço de e-mail para recuperar sua conta", + "Enter email address (required on this homeserver)": "Digite o endereço de e-mail (necessário neste servidor)", + "Doesn't look like a valid email address": "Este não parece ser um endereço de email válido", + "Passwords don't match": "As senhas não correspondem", + "Other users can invite you to rooms using your contact details": "Outros usuários podem convidá-lo para salas usando seus detalhes de contato", + "Enter phone number (required on this homeserver)": "Digite o número de celular (necessário neste servidor)", + "Doesn't look like a valid phone number": "Este não parece ser um número de telefone válido", + "Use lowercase letters, numbers, dashes and underscores only": "Use apenas letras minúsculas, números, traços e sublinhados", + "Enter username": "Digite o nome de usuário", + "Email (optional)": "E-mail (opcional)", + "Phone (optional)": "Número de celular (opcional)", + "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Defina um e-mail para recuperação da conta. Opcionalmente, use e-mail ou número de celular para ser encontrado por seus contatos." } From 7f9820e52c80b91628051e9b1044440966a8ee44 Mon Sep 17 00:00:00 2001 From: KenjiKenjiIcantLetYouGo Date: Mon, 27 Jul 2020 19:21:11 +0000 Subject: [PATCH 035/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 75.7% (1758 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 8d3ddafe48..2369e1f652 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1157,7 +1157,7 @@ "Replying With Files": "Responder com arquivos", "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Momentaneamente, não é possível responder com um arquivo. Você quer fazer o envio deste arquivo sem responder a mensagem?", "The file '%(fileName)s' failed to upload.": "O envio do arquivo '%(fileName)s' falhou.", - "The server does not support the room version specified.": "Este servidor não suporta a versão de sala especificada.", + "The server does not support the room version specified.": "O servidor não suporta a versão da sala especificada.", "Cancel entering passphrase?": "Cancelar a introdução da frase de senha?", "Are you sure you want to cancel entering passphrase?": "Tem certeza que quer cancelar a introdução da frase de senha?", "Go Back": "Voltar", From 6d2cf3bc9db35c4abb23a23913b9d9dea807eca1 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Mon, 27 Jul 2020 19:22:41 +0000 Subject: [PATCH 036/136] Translated using Weblate (Ukrainian) Currently translated at 42.9% (995 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 7ccb338785..41f7905c15 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -311,7 +311,7 @@ "Changes your display nickname": "Змінює ваш нік", "Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати", "Leave room": "Покинути кімнату", - "Kicks user with given id": "Вилучити з кімнати користувача з вказаним ідентифікатором", + "Kicks user with given id": "Викидає з кімнати користувача з вказаним ідентифікатором", "Ignores a user, hiding their messages from you": "Ігнорує користувача, приховуючи повідомлення від них", "Ignored user": "Користувача ігноровано", "You are now ignoring %(userId)s": "Ви ігноруєте %(userId)s", From 02a3d60be7429d525d503dd26e57509de68afe00 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 20:34:21 +0000 Subject: [PATCH 037/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 75.8% (1759 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 2369e1f652..fc960e27fc 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -1763,5 +1763,6 @@ "Enter username": "Digite o nome de usuário", "Email (optional)": "E-mail (opcional)", "Phone (optional)": "Número de celular (opcional)", - "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Defina um e-mail para recuperação da conta. Opcionalmente, use e-mail ou número de celular para ser encontrado por seus contatos." + "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Defina um e-mail para recuperação da conta. Opcionalmente, use e-mail ou número de celular para ser encontrado por seus contatos.", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Você não pode entrar na sua conta. Entre em contato com o administrador do servidor para obter mais informações." } From 46b08c520d6fed6122a774de591ab752ac33f56c Mon Sep 17 00:00:00 2001 From: hugues de keyzer Date: Tue, 28 Jul 2020 08:01:18 +0000 Subject: [PATCH 038/136] Translated using Weblate (Esperanto) Currently translated at 99.9% (2320 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index cda198ea02..8d07db7eaf 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1721,8 +1721,8 @@ "To be secure, do this in person or use a trusted way to communicate.": "Por plia sekureco, faru tion persone, aŭ uzu alian fidatan komunikilon.", "Verify yourself & others to keep your chats safe": "Kontrolu vin mem kaj aliajn por sekurigi viajn babilojn", "Channel: %(channelName)s": "Kanalo: %(channelName)s", - "Show less": "Montri pli", - "Show more": "Montri malpli", + "Show less": "Montri malpli", + "Show more": "Montri pli", "Help": "Helpo", "Session verified": "Salutaĵo kontroliĝis", "Copy": "Kopii", @@ -2376,7 +2376,7 @@ "Set a Security Phrase": "Agordi Sekurecan frazon", "Confirm Security Phrase": "Konfirmi Sekurecan frazon", "Save your Security Key": "Konservi vian Sekurecan ŝlosilon", - "New spinner design": "", + "New spinner design": "Nova fasono de la turniĝilo", "Show rooms with unread messages first": "Montri ĉambrojn kun nelegitaj mesaĝoj kiel unuajn", "Show previews of messages": "Montri antaŭrigardojn al mesaĝoj", "This room is public": "Ĉi tiu ĉambro estas publika", @@ -2386,5 +2386,6 @@ "Are you sure you want to cancel entering passphrase?": "Ĉu vi certe volas nuligi enigon de pasfrazo?", "Enable advanced debugging for the room list": "Ŝalti altnivelan erarserĉadon por la ĉambrobreto", "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", - "Custom Tag": "Propra etikedo" + "Custom Tag": "Propra etikedo", + "Feedback": "Prikomenti" } From 9a490a2a9d89ecb7deede32366fe4ac4e2e51bcc Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 28 Jul 2020 05:18:44 +0000 Subject: [PATCH 039/136] Translated using Weblate (Hungarian) Currently translated at 100.0% (2322 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index e2f29db47d..d72787a932 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2387,5 +2387,6 @@ "Click to view edits": "A szerkesztések megtekintéséhez kattints", "You’re already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at element.io/get-started.": "Már be vagy jelentkezve és ez rendben van, de minden platformon az alkalmazás legfrissebb verziójának beszerzéséhez látogass el ide: element.io/get-started.", "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use %(brand)s with an existing Matrix account on a different homeserver.": "Használhatod a más szerver opciót, hogy egy másik matrix szerverre jelentkezz be amihez megadod a szerver url címét. Ezzel használhatod %(brand)s klienst egy már létező Matrix fiókkal egy másik matrix szerveren.", - "Enter the location of your Element Matrix Services homeserver. It may use your own domain name or be a subdomain of element.io.": "Add meg az Element Matrix Services matrix szerveredet. Használhatod a saját domain-edet vagy az element.io al-domain-jét." + "Enter the location of your Element Matrix Services homeserver. It may use your own domain name or be a subdomain of element.io.": "Add meg az Element Matrix Services matrix szerveredet. Használhatod a saját domain-edet vagy az element.io al-domain-jét.", + "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s" } From a80c0310dd6c39e7154a56902219caafa62bcb3f Mon Sep 17 00:00:00 2001 From: call_xz Date: Tue, 28 Jul 2020 04:45:45 +0000 Subject: [PATCH 040/136] Translated using Weblate (Japanese) Currently translated at 58.1% (1348 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 5387c3a3e8..75bce7f155 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -1360,5 +1360,9 @@ "Leave Room": "部屋を退出", "Failed to connect to integration manager": "インテグレーションマネージャへの接続に失敗しました", "Start verification again from their profile.": "プロフィールから再度検証を開始してください。", - "Integration Manager": "インテグレーションマネージャ" + "Integration Manager": "インテグレーションマネージャ", + "Do not use an identity server": "ID サーバーを使用しない", + "Composer": "入力欄", + "Sort by": "並び替え", + "List options": "一覧の設定" } From d1b9d6f9733ca419256b8136f3d70a237b555267 Mon Sep 17 00:00:00 2001 From: Rintan Date: Tue, 28 Jul 2020 05:49:08 +0000 Subject: [PATCH 041/136] Translated using Weblate (Japanese) Currently translated at 58.1% (1348 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 75bce7f155..9611ea9a3e 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -1364,5 +1364,6 @@ "Do not use an identity server": "ID サーバーを使用しない", "Composer": "入力欄", "Sort by": "並び替え", - "List options": "一覧の設定" + "List options": "一覧の設定", + "Use Single Sign On to continue": "シングルサインオンを使用して続行" } From 71e301b823ed1d51a3e71b316b2ae53569deffc0 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 28 Jul 2020 01:07:00 +0000 Subject: [PATCH 042/136] Translated using Weblate (Lojban) Currently translated at 21.1% (489 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/jbo/ --- src/i18n/strings/jbo.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/jbo.json b/src/i18n/strings/jbo.json index 8937e10cf1..9ac42af1de 100644 --- a/src/i18n/strings/jbo.json +++ b/src/i18n/strings/jbo.json @@ -3,7 +3,7 @@ "This phone number is already in use": ".i xa'o pilno fa da le fonxa judri", "Failed to verify email address: make sure you clicked the link in the email": ".i da nabmi fi lo nu facki le du'u do ponse le te samymri .i ko birti le du'u do samcu'a le judrysni pe le se samymri", "The platform you're on": "jicmu vau je se pilno do", - "Your language of choice": "se cuxna fi le ka bangu", + "Your language of choice": "se cuxna fo le ka bangu", "Which officially provided instance you are using, if any": "samtcise'u vau je catni jai te selfu vau je se pilno do", "Whether or not you're using the Richtext mode of the Rich Text Editor": "jei do pilno le se jadni ciska tadji pe le notci ciska tutci", "Your homeserver's URL": "judri le samtcise'u", @@ -30,7 +30,7 @@ "Unable to capture screen": ".i na ka'e facki le du'u vidvi fi le vidni", "Existing Call": ".i xa'o ca'o fonjo'e", "Server may be unavailable, overloaded, or you hit a bug.": ".i la'a cu'i gi ja le samtcise'u cu spofu vau ja mutce le ka gunka gi da samcfi", - "Send": "benji", + "Send": "nu zilbe'i", "Sun": "jy. dy. ze", "Mon": "jy. dy. pa", "Tue": "jy. dy. re", @@ -100,7 +100,7 @@ "/ddg is not a command": "zoi ny. /ddg .ny. na nu minde", "Changes your display nickname": "", "Invites user with given id to current room": ".i vi'ecpe lo pilno poi se judri ti ku le kumfa pe'a", - "Leave room": "nu cliva le ve zilbe'i", + "Leave room": "nu do zilvi'u le se zilbe'i", "Kicks user with given id": ".i rinka lo nu lo pilno poi se judri ti cu cliva", "Bans user with given id": ".i rinka lo nu lo pilno poi se judri ti cu vitno cliva", "Ignores a user, hiding their messages from you": ".i rinka lo nu no'e jundi lo pilno gi'e mipri lo notci be fi py. do", @@ -200,8 +200,8 @@ "Accept": "nu fonjo'e", "Error": "nabmi", "Incorrect verification code": ".i na'e drani ke lacri lerpoi", - "Submit": "benji", - "Phone": "lo fonxa", + "Submit": "nu zilbe'i", + "Phone": "fonxa", "Add": "jmina", "Failed to upload profile picture!": ".i da nabmi lo nu kibdu'a le pixra sinxa", "No display name": ".i na da cmene", From 48482fecc6ea8a4a6be976b732f4457d6acb331e Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Mon, 27 Jul 2020 20:34:56 +0000 Subject: [PATCH 043/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 82.3% (1911 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 196 ++++++++++++++++++++++++++++++++---- 1 file changed, 174 insertions(+), 22 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index fc960e27fc..d5eff339ac 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -9,7 +9,7 @@ "Are you sure you want to reject the invitation?": "Você tem certeza que deseja rejeitar este convite?", "Banned users": "Usuárias/os banidas/os", "Bans user with given id": "Banir usuários com o identificador informado", - "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s mudou o tópico para \"%(topic)s\".", + "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s mudou a descrição para \"%(topic)s\".", "Changes your display nickname": "Troca o seu apelido", "Click here to fix": "Clique aqui para resolver isso", "Commands": "Comandos", @@ -66,7 +66,7 @@ "Privileged Users": "Usuárias/os privilegiadas/os", "Profile": "Perfil", "Reject invitation": "Rejeitar convite", - "Remove": "Remover", + "Remove": "Apagar", "Return to login screen": "Retornar à tela de login", "Room Colour": "Cores da sala", "Rooms": "Salas", @@ -275,7 +275,7 @@ "Failed to invite": "Falha ao enviar o convite", "Failed to invite the following users to the %(roomName)s room:": "Falha ao convidar as(os) seguintes usuárias(os) para a sala %(roomName)s:", "Confirm Removal": "Confirmar a remoção", - "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.": "Você tem certeza que quer apagar este evento? Note que se você apaga o nome de uma sala ou uma mudança de tópico, esta ação não poderá ser desfeita.", + "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.": "Tem certeza de que deseja apagar este evento? Observe que, se você apagar o nome ou alterar a descrição de uma sala, pode desfazer a alteração.", "Unknown error": "Erro desconhecido", "Incorrect password": "Senha incorreta", "Unable to restore session": "Não foi possível restaurar a sessão", @@ -318,10 +318,10 @@ "No Webcams detected": "Não foi detectada nenhuma Webcam", "No media permissions": "Não há permissões de uso de vídeo/áudio no seu navegador", "You may need to manually permit %(brand)s to access your microphone/webcam": "Você talvez precise autorizar manualmente que o %(brand)s acesse seu microfone e webcam", - "Default Device": "Dispositivo padrão", + "Default Device": "Aparelho padrão", "Microphone": "Microfone", "Camera": "Câmera de vídeo", - "Add a topic": "Adicionar um tópico", + "Add a topic": "Adicionar uma descrição", "Anyone": "Qualquer pessoa", "Are you sure you want to leave the room '%(roomName)s'?": "Você tem certeza que deseja sair da sala '%(roomName)s'?", "Custom level": "Nível personalizado", @@ -400,7 +400,7 @@ "Edit": "Editar", "Unpin Message": "Desafixar Mensagem", "Add rooms to this community": "Adicionar salas na comunidade", - "The version of %(brand)s": "A Versão do %(brand)s", + "The version of %(brand)s": "A versão do %(brand)s", "The platform you're on": "A plataforma que você está usando", "Your language of choice": "O idioma que você selecionou", "Which officially provided instance you are using, if any": "Qual instância oficial você está usando, se for o caso", @@ -746,7 +746,7 @@ "Off": "Desativado", "Mentions only": "Apenas menções", "Wednesday": "Quarta", - "You can now return to your account after signing out, and sign in on other devices.": "Você pode retornar agora para a sua conta depois de fazer logout, e então fazer login em outros dispositivos.", + "You can now return to your account after signing out, and sign in on other devices.": "Agora você pode retornar à sua conta depois de sair, e fazer login em outros aparelhos.", "Enable email notifications": "Ativar notificações por email", "Event Type": "Tipo do Evento", "Download this file": "Baixar este arquivo", @@ -762,7 +762,7 @@ "Checking for an update...": "Verificando se há atualizações...", "Every page you use in the app": "Toda a página que você usa no aplicativo", "e.g. ": "ex. ", - "Your device resolution": "Sua resolução de dispositivo", + "Your device resolution": "A resolução do seu aparelho", "Call in Progress": "Chamada em andamento", "A call is currently being placed!": "Uma chamada está sendo feita atualmente!", "A call is already in progress!": "Uma chamada já está em andamento!", @@ -825,7 +825,7 @@ "Unable to load key backup status": "Não é possível carregar o status da chave de backup", "Backup version: ": "Versão do Backup: ", "Algorithm: ": "Algoritmo: ", - "This event could not be displayed": "O evento não pôde ser exibido", + "This event could not be displayed": "Este evento não pôde ser exibido", "Use a longer keyboard pattern with more turns": "Use um padrão de teclas em diferentes direções e sentido", "Share Link to User": "Compartilhar Link com Usuário", "This room has been replaced and is no longer active.": "Esta sala foi substituída e não está mais ativa.", @@ -865,9 +865,9 @@ "Failed to send logs: ": "Falha ao enviar registros: ", "Submit debug logs": "Submeter registros de depuração", "Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Os registros de depuração contêm dados de uso do aplicativo, incluindo seu nome de usuário, os IDs ou aliases das salas ou grupos que você visitou e os nomes de usuários de outros usuários. Eles não contêm mensagens.", - "Before submitting logs, you must create a GitHub issue to describe your problem.": "Antes de enviar os registros, você deve criar uma questão no GitHub para descrever seu problema.", + "Before submitting logs, you must create a GitHub issue to describe your problem.": "Antes de enviar os registros, você deve criar um bilhete de erro no GitHub para descrever seu problema.", "Unable to load commit detail: %(msg)s": "Não é possível carregar os detalhes do commit: %(msg)s", - "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this": "Para evitar perder seu histórico de bate-papo, você deve exportar as chaves do seu quarto antes de fazer logout. Você precisará voltar para a versão mais recente do %(brand)s para fazer isso", + "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this": "Para evitar perder seu histórico de bate-papo, você deve exportar as chaves da sua sala antes de se desconectar. Para fazer isso, você precisará retornar na versão mais atual do %(brand)s", "Incompatible Database": "Banco de dados incompatível", "Continue With Encryption Disabled": "Continuar com criptografia desativada", "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.": "Isso tornará sua conta permanentemente inutilizável. Você não poderá efetuar login e ninguém poderá registrar novamente o mesmo ID de usuário. Isso fará com que sua conta deixe todas as salas nas quais está participando e removerá os detalhes da sua conta do seu servidor de identidade. Esta ação é irreversível.", @@ -933,7 +933,7 @@ "You can't send any messages until you review and agree to our terms and conditions.": "Você não pode enviar nenhuma mensagem até revisar e concordar com nossos termos e condições.", "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.": "Sua mensagem não foi enviada porque este homeserver atingiu seu Limite de usuário ativo mensal. Por favor, entre em contato com o seu administrador de serviços para continuar usando o serviço.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Sua mensagem não foi enviada porque este homeserver excedeu o limite de recursos. Por favor, entre em contato com o seu administrador de serviços para continuar usando o serviço.", - "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Se você enviou um bug por meio do GitHub, os logs de depuração podem nos ajudar a rastrear o problema. Os logs de depuração contêm dados de uso do aplicativo, incluindo seu nome de usuário, os IDs ou aliases das salas ou grupos que você visitou e os nomes de usuários de outros usuários. Eles não contêm mensagens.", + "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Se você enviou um bug por meio do GitHub, os logs de depuração podem nos ajudar a rastrear o problema. Os logs de depuração contêm dados de uso do aplicativo, incluindo seu nome de usuário, os IDs ou apelidos das salas ou grupos que você visitou e os nomes de usuários de outros usuários. Eles não contêm mensagens.", "Legal": "Legal", "No Audio Outputs detected": "Nenhuma saída de áudio detectada", "Audio Output": "Saída de áudio", @@ -945,7 +945,7 @@ "Sign in with single sign-on": "Entre com o logon único", "That matches!": "Isto corresponde!", "That doesn't match.": "Isto não corresponde.", - "Go back to set it again.": "Volte e configure novamente.", + "Go back to set it again.": "Voltar para configurar novamente.", "Download": "Baixar", "Print it and store it somewhere safe": "Imprima-o e armazene-o em algum lugar seguro", "Save it on a USB key or backup drive": "Salve isto em uma chave USB ou unidade de backup", @@ -968,8 +968,8 @@ "Invite anyway": "Convide mesmo assim", "Whether or not you're logged in (we don't record your username)": "Se você está logado ou não (não gravamos seu nome de usuário)", "Upgrades a room to a new version": "Atualiza uma sala para uma nova versão", - "Gets or sets the room topic": "Obtém ou define o tópico da sala", - "This room has no topic.": "Esta sala não tem assunto.", + "Gets or sets the room topic": "Consultar ou definir a descrição da sala", + "This room has no topic.": "Esta sala não tem descrição.", "Sets the room name": "Define o nome da sala", "Group & filter rooms by custom tags (refresh to apply changes)": "Agrupar e filtrar salas por tags personalizadas (atualize para aplicar as alterações)", "Render simple counters in room header": "Renderizar contadores simples no cabeçalho da sala", @@ -1132,7 +1132,7 @@ "Change main address for the room": "Alterar o endereço principal da sala", "Change history visibility": "Alterar a visibilidade do histórico", "Change permissions": "Alterar permissões", - "Change topic": "Alterar o tópico", + "Change topic": "Alterar a descrição", "Modify widgets": "Modificar widgets", "Default role": "Papel padrão", "Send messages": "Enviar mensagens", @@ -1189,7 +1189,7 @@ "Changes the avatar of the current room": "Altera a imagem da sala atual", "Changes your avatar in this current room only": "Muda sua imagem de perfil apenas nesta sala", "Changes your avatar in all rooms": "Muda sua imagem de perfil em todas as salas", - "Failed to set topic": "Não foi possível definir o tópico", + "Failed to set topic": "Não foi possível definir a descrição", "Use an identity server": "Usar um servidor de identidade", "Use an identity server to invite by email. Manage in Settings.": "Use um servidor de identidade para convidar pessoas por email. Gerencie nas Configurações.", "Joins room with given address": "Entra em uma sala com o endereço fornecido", @@ -1343,7 +1343,7 @@ "Show shortcuts to recently viewed rooms above the room list": "Mostrar atalhos para salas recentemente visualizadas acima da lista de salas", "Show hidden events in timeline": "Mostrar eventos ocultos na timeline", "Low bandwidth mode": "Modo de baixo uso de internet", - "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 respaldo de assistência de chamadas turn.matrix.org quando seu servidor não o ofereça (seu endereço IP será compartilhado numa chamada, neste caso)", + "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 a assistência do servidor de chamadas reserva turn.matrix.org quando seu servidor não oferecer este serviço (seu endereço IP será transmitido quando você ligar)", "Send read receipts for messages (requires compatible homeserver to disable)": "Enviar confirmação de leitura para mensagens (necessita um servidor compatível para desativar)", "Show previews/thumbnails for images": "Mostrar miniaturas e resumos para imagens", "Enable message search in encrypted rooms": "Ativar busca de mensagens em salas criptografadas", @@ -1509,9 +1509,9 @@ "%(name)s wants to verify": "%(name)s deseja verificar", "Smileys & People": "Emoticons e Pessoas", "Widgets do not use message encryption.": "Widgets não usam criptografia de mensagens.", - "Please create a new issue on GitHub so that we can investigate this bug.": "Por favor, crie uma nova issue no GitHub para que possamos investigar esta falha.", + "Please create a new issue on GitHub so that we can investigate this bug.": "Por favor, crie um novo bilhete de erro no GitHub para que possamos investigar esta falha.", "Enter the name of a new server you want to explore.": "Entre com o nome do novo servidor que você quer explorar.", - "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Por favor, diga-nos o que aconteceu de errado ou, ainda melhor, crie uma issue no GitHub que descreva o problema.", + "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Por favor, diga-nos o que aconteceu de errado ou, ainda melhor, crie um bilhete de erro no GitHub que descreva o problema.", "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Apagar todos os dados desta sessão é uma ação permanente. Mensagens criptografadas serão perdidas, a não ser que suas chaves tenham sido copiadas para o backup.", "Set a room address to easily share your room with other people.": "Defina um endereço de sala para facilmente compartilhar sua sala com outras pessoas.", "You can’t disable this later. Bridges & most bots won’t work yet.": "Você não poderá desabilitar depois. Pontes e a maioria dos bots não funcionarão no momento.", @@ -1742,7 +1742,7 @@ "%(networkName)s rooms": "Salas em %(networkName)s", "Matrix rooms": "Salas em Matrix", "Close dialog": "Fechar caixa de diálogo", - "GitHub issue": "Questão no GitHub", + "GitHub issue": "Bilhete de erro no GitHub", "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.": "Se houver um contexto adicional que ajude a analisar o problema, tal como o que você estava fazendo no momento, IDs de salas, IDs de usuários etc, inclua essas coisas aqui.", "Topic (optional)": "Descrição (opcional)", "There was a problem communicating with the server. Please try again.": "Ocorreu um problema na comunicação com o servidor. Por favor, tente novamente.", @@ -1764,5 +1764,157 @@ "Email (optional)": "E-mail (opcional)", "Phone (optional)": "Número de celular (opcional)", "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Defina um e-mail para recuperação da conta. Opcionalmente, use e-mail ou número de celular para ser encontrado por seus contatos.", - "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Você não pode entrar na sua conta. Entre em contato com o administrador do servidor para obter mais informações." + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Você não pôde se conectar na sua conta. Entre em contato com o administrador do servidor para obter mais informações.", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Confirme a adição deste número de telefone usando o Login Único para provar sua identidade.", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Use um servidor de identidade para convidar por e-mail. Clique em continuar para usar o servidor de identidade padrão (%(defaultIdentityServerName)s) ou gerencie nas Configurações.", + "You might have configured them in a client other than %(brand)s. You cannot tune them in %(brand)s but they still apply.": "Você pode ter configurado estas opções em um cliente que não seja %(brand)s. Você não pode ajustar essas opções no %(brand)s, mas elas ainda se aplicam.", + "Enable audible notifications for this session": "Ativar notificações sonoras para esta sessão", + "Display Name": "Nome em exibição", + "Identity Server URL must be HTTPS": "O URL do Servidor de Identidade deve ser HTTPS", + "Not a valid Identity Server (status code %(code)s)": "Servidor de Identidade inválido (código de status %(code)s)", + "Could not connect to Identity Server": "Não foi possível conectar-se ao Servidor de Identidade", + "Checking server": "Verificando servidor", + "Change identity server": "Mudar o servidor de identidade", + "Disconnect from the identity server and connect to instead?": "Desconectar-se do servidor de identidade e conectar-se em em vez disso?", + "Terms of service not accepted or the identity server is invalid.": "Termos de serviço não aceitos ou o servidor de identidade é inválido.", + "The identity server you have chosen does not have any terms of service.": "O servidor de identidade que você escolheu não possui nenhum termo de serviço.", + "Disconnect identity server": "Desconectar servidor de identidade", + "Disconnect from the identity server ?": "Desconectar-se 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.": "Você deve remover seus dados pessoais do servidor de identidade antes de desconectar. Infelizmente, o servidor de identidade está atualmente offline ou não pode ser acessado.", + "You should:": "Você deveria:", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "verifique se há extensões no seu navegador que possam bloquear o servidor de identidade (por exemplo, Privacy Badger)", + "contact the administrators of identity server ": "entre em contato com os administradores do servidor de identidade ", + "Disconnect anyway": "Desconectar de qualquer maneira", + "You are still sharing your personal data on the identity server .": "Você ainda está compartilhando seus dados pessoais no servidor de identidade .", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Recomendamos que você remova seus endereços de e-mail e números de telefone do servidor de identidade antes de desconectar.", + "Go back": "Voltar", + "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.": "No momento, você está usando para descobrir e ser descoberto pelos contatos existentes que você conhece. Você pode alterar seu servidor de identidade abaixo.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Se você não quiser usar para descobrir e ser detectável pelos contatos existentes, digite outro servidor de identidade abaixo.", + "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.": "No momento, você não está usando um servidor de identidade. Para descobrir e ser descoberto pelos contatos existentes, adicione um abaixo.", + "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.": "Desconectar-se do servidor de identidade significa que você não poderá ser descoberto por outros usuários e não poderá convidar outras pessoas por e-mail ou número de celular.", + "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 um servidor de identidade é opcional. Se você optar por não usar um servidor de identidade, não poderá ser descoberto por outros usuários e não poderá convidar outras pessoas por e-mail ou por número de celular.", + "Do not use an identity server": "Não usar um servidor de identidade", + "Enter a new identity server": "Digitar um novo servidor de identidade", + "Change": "Alterar", + "Manage integrations": "Gerenciar integrações", + "New version available. Update now.": "Nova versão disponível. Atualize agora.", + "Hey you. You're the best!": "Ei, você aí. Você é incrível!", + "Size must be a number": "O tamanho deve ser um número", + "Custom font size can only be between %(min)s pt and %(max)s pt": "O tamanho da fonte personalizada só pode estar entre %(min)s pt e %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Use entre %(min)s pt e %(max)s pt", + "Invalid theme schema.": "Esquema inválido de tema.", + "Error downloading theme information.": "Erro ao baixar as informações do tema.", + "Theme added!": "Tema adicionado!", + "Custom theme URL": "URL do tema personalizado", + "Add theme": "Adicionar tema", + "Message layout": "Aparência da mensagem", + "Compact": "Compacto", + "Modern": "Moderno", + "Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Defina o nome de uma fonte instalada no seu sistema e %(brand)s tentará usá-la.", + "Customise your appearance": "Personalize sua aparência", + "Appearance Settings only affect this %(brand)s session.": "As Configurações de aparência afetam apenas esta sessão do %(brand)s.", + "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações por push em outras sessões até fazer login novamente nelas", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Concordar com os Termos de Serviço do servidor de identidade (%(serverName)s), para permitir aos seus contatos encontrarem-na(no) por endereço de e-mail ou por número de celular.", + "Discovery": "Contatos", + "Deactivate account": "Desativar conta", + "Clear cache and reload": "Limpar cache e recarregar", + "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.": "Para relatar um problema de segurança relacionado à tecnologia Matrix, leia a Política de Divulgação de Segurança da Matrix.org.", + "Always show the window menu bar": "Sempre mostrar a barra de menu na janela", + "Show tray icon and minimize window to it on close": "Mostrar ícone na barra de tarefas, que permanece visível ao fechar a janela", + "Read Marker lifetime (ms)": "Duração do marcador de leitura (ms)", + "Read Marker off-screen lifetime (ms)": "Vida útil do marcador de leitura fora da tela (ms)", + "Change settings": "Alterar configurações", + "Send %(eventType)s events": "Enviar eventos de %(eventType)s", + "Roles & Permissions": "Papeis & Permissões", + "Select the roles required to change various parts of the room": "Selecione as permissões necessárias para alterar várias partes da sala", + "Emoji picker": "Seletor de emoji", + "Room %(name)s": "Sala %(name)s", + "No recently visited rooms": "Não há salas visitadas recentemente", + "Custom Tag": "Etiqueta personalizada", + "Joining room …": "Entrando na sala…", + "Loading …": "Carregando…", + "Rejecting invite …": "Rejeitando convite…", + "Join the conversation with an account": "Participar da conversa com uma conta", + "Sign Up": "Inscrever-se", + "Loading room preview": "Carregando visualização da sala", + "You were kicked from %(roomName)s by %(memberName)s": "Você foi removida(o) de %(roomName)s por %(memberName)s", + "Reason: %(reason)s": "Razão: %(reason)s", + "Forget this room": "Esquecer esta sala", + "Re-join": "Entrar novamente", + "You were banned from %(roomName)s by %(memberName)s": "Você foi banida(o) de %(roomName)s por %(memberName)s", + "Something went wrong with your invite to %(roomName)s": "Ocorreu um erro no seu convite para %(roomName)s", + "An error (%(errcode)s) was returned while trying to validate your invite. You could try to pass this information on to a room admin.": "Ocorreu um erro (%(errcode)s) ao validar seu convite. Você pode passar essas informações para um administrador da sala.", + "You can only join it with a working invite.": "Você só pode participar com um convite válido.", + "Try to join anyway": "Tentar entrar mesmo assim", + "You can still join it because this is a public room.": "Você ainda pode entrar, porque esta é uma sala pública.", + "Join the discussion": "Participar da discussão", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "Este convite para %(roomName)s foi enviado para %(email)s, que não está associado à sua conta", + "Link this email with your account in Settings to receive invites directly in %(brand)s.": "Vincule esse e-mail à sua conta em Configurações, para receber convites diretamente em %(brand)s.", + "This invite to %(roomName)s was sent to %(email)s": "Este convite para %(roomName)s foi enviado para %(email)s", + "Use an identity server in Settings to receive invites directly in %(brand)s.": "Use um servidor de identidade em Configurações para receber convites diretamente em %(brand)s.", + "Share this email in Settings to receive invites directly in %(brand)s.": "Compartilhe este e-mail em Configurações para receber convites diretamente em %(brand)s.", + "Do you want to chat with %(user)s?": "Deseja conversar com %(user)s?", + " wants to chat": " quer conversar", + "Do you want to join %(roomName)s?": "Deseja se juntar a %(roomName)s?", + " invited you": " convidou você", + "Reject & Ignore user": "Rejeitar e ignorar usuário", + "You're previewing %(roomName)s. Want to join it?": "Você está visualizando %(roomName)s. Deseja participar?", + "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s não pode ser visualizado. Deseja participar?", + "This room doesn't exist. Are you sure you're at the right place?": "Esta sala não existe. Tem certeza de que você está no lugar certo?", + "Securely back up your keys to avoid losing them. Learn more.": "Faça backup de suas chaves com segurança para evitar perdê-las. Saiba mais.", + "Not now": "Agora não", + "Don't ask me again": "Não me pergunte novamente", + "Appearance": "Aparência", + "Show rooms with unread messages first": "Mostrar salas com mensagens não lidas primeiro", + "Show previews of messages": "Mostrar pré-visualizações de mensagens", + "Sort by": "Ordenar por", + "Activity": "Atividade", + "A-Z": "A-Z", + "Unknown Command": "Comando desconhecido", + "Unrecognised command: %(commandText)s": "Comando não reconhecido: %(commandText)s", + "You can use /help to list available commands. Did you mean to send this as a message?": "Você pode usar /help para listar os comandos disponíveis. Você quis enviar isso como uma mensagem?", + "Send as message": "Enviar como mensagem", + "Room Topic": "Descrição da sala", + "React": "Reagir", + "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Se você encontrar algum erro ou tiver um comentário que gostaria de compartilhar, informe-nos no GitHub.", + "Resend %(unsentCount)s reaction(s)": "Reenviar %(unsentCount)s reações", + "Notification settings": "Configurar notificações", + "Want more than a community? Get your own server": "Quer mais do que uma comunidade? Obtenha seu próprio servidor", + "Switch to light mode": "Alternar para o modo claro", + "Switch to dark mode": "Alternar para o modo escuro", + "Security & privacy": "Segurança & privacidade", + "All settings": "Todas as configurações", + "You're signed out": "Você foi desconectada(o)", + "Clear personal data": "Limpar dados pessoais", + "Command Autocomplete": "Preenchimento automático de comandos", + "Community Autocomplete": "Preenchimento automático da comunidade", + "DuckDuckGo Results": "Resultados no DuckDuckGo", + "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Se você não excluiu o método de recuperação, um invasor pode estar tentando acessar sua conta. Altere a senha da sua conta e defina imediatamente um novo método de recuperação nas Configurações.", + "Room List": "Lista de salas", + "Autocomplete": "Autocompletar", + "Alt": "Alt", + "Alt Gr": "Alt Gr", + "Shift": "Shift", + "Super": "Super", + "Ctrl": "Ctrl", + "Toggle Bold": "Negrito", + "Toggle Italics": "Itálico", + "Toggle Quote": "Citar", + "New line": "Nova linha", + "Navigate recent messages to edit": "Navegue pelas mensagens recentes para editar", + "Cancel replying to a message": "Cancelar resposta à mensagem", + "Toggle microphone mute": "Ativar/desativar som do microfone", + "Toggle video on/off": "Ativar/desativar o vídeo", + "Scroll up/down in the timeline": "Rolar para cima/baixo na linha do tempo", + "Dismiss read marker and jump to bottom": "Ignorar o marcador de leitura e ir para o final", + "Jump to oldest unread message": "Ir para a mensagem não lida mais antiga", + "Upload a file": "Enviar um arquivo", + "Jump to room search": "Ir para a pesquisa de salas", + "Navigate up/down in the room list": "Navegue para cima/baixo na lista de salas", + "Select room from the room list": "Selecionar sala da lista de salas", + "Collapse room list section": "Esconder seção da lista de salas", + "Expand room list section": "Mostrar seção da lista de salas" } From 29f9b7c88349723fe5ebd322393e9408cd6c22d9 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Tue, 28 Jul 2020 03:08:19 +0000 Subject: [PATCH 044/136] Translated using Weblate (Ukrainian) Currently translated at 45.2% (1050 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 80 +++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 41f7905c15..2084f16b68 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -6,7 +6,7 @@ "Dismiss": "Відхилити", "Error": "Помилка", "Failed to forget room %(errCode)s": "Не вдалось видалити кімнату %(errCode)s", - "Favourite": "Вибране", + "Favourite": "Улюблені", "Mute": "Стишити", "Notifications": "Сповіщення", "Operation failed": "Не вдалося виконати дію", @@ -266,7 +266,7 @@ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(fullYear)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", "Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?", - "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Якщо дана сторінка містить особисту інформацію, як то назва кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.", + "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Там, де ця сторінка містить ототожненну інформацію, як-от назва кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.", "Call in Progress": "Іде виклик", "A call is currently being placed!": "Зараз іде виклик!", "A call is already in progress!": "Вже здійснюється дзвінок!", @@ -312,8 +312,8 @@ "Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати", "Leave room": "Покинути кімнату", "Kicks user with given id": "Викидає з кімнати користувача з вказаним ідентифікатором", - "Ignores a user, hiding their messages from you": "Ігнорує користувача, приховуючи повідомлення від них", - "Ignored user": "Користувача ігноровано", + "Ignores a user, hiding their messages from you": "Ігнорує користувача, приховуючи його повідомлення від вас", + "Ignored user": "Зігнорований користувач", "You are now ignoring %(userId)s": "Ви ігноруєте %(userId)s", "Stops ignoring a user, showing their messages going forward": "Припиняє ігнорувати користувача, від цього моменту показуючи їхні повідомлення", "Unignored user": "Припинено ігнорування користувача", @@ -460,7 +460,7 @@ "Sends a message as plain text, without interpreting it as markdown": "Надсилає повідомлення як чистий текст, не використовуючи markdown", "Upgrades a room to a new version": "Покращує кімнату до нової версії", "You do not have the required permissions to use this command.": "Вам бракує дозволу на використання цієї команди.", - "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.": "Увага!: Покращення кімнати не перенесе автоматично усіх учасників до нової версії кімнати. Ми опублікуємо посилання на нову кімнату у старій версії кімнати, а учасники мають власноруч клацнути це посилання, щоб приєднатися до нової кімнати.", + "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.": "Увага!: Поліпшення кімнати не перенесе автоматично усіх учасників до нової версії кімнати. Ми опублікуємо посилання на нову кімнату у старій версії кімнати, а учасники мають власноруч клацнути це посилання, щоб приєднатися до нової кімнати.", "Changes your display nickname in the current room only": "Змінює ваше псевдо тільки для поточної кімнати", "Changes the avatar of the current room": "Змінює аватар поточної кімнати", "Changes your avatar in this current room only": "Змінює ваш аватар для поточної кімнати", @@ -675,10 +675,10 @@ "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s змінив(ла) альтернативні адреси для цієї кімнати.", "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s змінив(ла) головні та альтернативні адреси для цієї кімнати.", "%(senderName)s changed the addresses for this room.": "%(senderName)s змінив(ла) адреси для цієї кімнати.", - "%(senderName)s placed a voice call.": "%(senderName)s зробив голосовий виклик.", - "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s зробив голосовий виклик. (не підтримується вашим браузером)", - "%(senderName)s placed a video call.": "%(senderName)s здійснив відео-виклик.", - "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s здійснив відео-виклик. (не підтримується вашим браузером)", + "%(senderName)s placed a voice call.": "%(senderName)s розпочав(-ла) голосовий виклик.", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s розпочав(-ла) голосовий виклик. (не підтримується цим переглядачем)", + "%(senderName)s placed a video call.": "%(senderName)s розпочав(-ла) відеовиклик.", + "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s розпочав(-ла) відеовиклик. (не підтримується цим переглядачем)", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s відкликав(ла) запрошення %(targetDisplayName)s приєднання до кімнати.", "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s видалив(ла) правило блокування користувачів по шаблону %(glob)s", "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s видалив(ла) правило блокування кімнат по шаблону %(glob)s", @@ -769,9 +769,9 @@ "Ok": "Гаразд", "Set password": "Встановити пароль", "Set up encryption": "Налаштування шифрування", - "Encryption upgrade available": "Доступне оновлення шифрування", + "Encryption upgrade available": "Доступне поліпшене шифрування", "Set up": "Налаштувати", - "Upgrade": "Оновлення", + "Upgrade": "Поліпшити", "Other users may not trust it": "Інші користувачі можуть не довіряти цьому", "New login. Was this you?": "Новий вхід у вашу обліківку. Це були Ви?", "Restart": "Перезапустити", @@ -855,7 +855,7 @@ "Preferences": "Параметри", "Room list": "Перелік кімнат", "Composer": "Редактор", - "Security & Privacy": "Безпека та конфіденціальність", + "Security & Privacy": "Безпека та конфіденційність", "Where you’re logged in": "Де ви ввійшли", "Skip": "Пропустити", "Notification settings": "Налаштування сповіщень", @@ -1004,5 +1004,59 @@ "Enter a new identity server": "Введіть новий сервер ідентифікації", "Change": "Змінити", "Manage integrations": "Керування інтеграціями", - "Size must be a number": "Розмір повинен бути числом" + "Size must be a number": "Розмір повинен бути числом", + "Incoming voice call": "Входовий голосовий виклик", + "Incoming video call": "Входовий відеовиклик", + "Upgrade to your own domain": "Поліпшити до свого власного домену", + "No Audio Outputs detected": "Звуковий вивід не виявлено", + "Audio Output": "Звуковий вивід", + "Voice & Video": "Голос та відео", + "Upgrade this room to the recommended room version": "Поліпшити цю кімнату до рекомендованої версії", + "this room": "ця кімната", + "Upgrade the room": "Поліпшити кімнату", + "Unable to revoke sharing for email address": "Не вдалось відкликати оприлюднювання адреси е-пошти", + "Revoke": "Відкликати", + "Unable to revoke sharing for phone number": "Не вдалось відкликати оприлюднювання телефонного номеру", + "Filter room members": "Відфільтрувати учасників кімнати", + "Voice call": "Голосовий виклик", + "Video call": "Відеовиклик", + "Not now": "Не зараз", + "Don't ask me again": "Не запитувати мене знову", + "Appearance": "Вигляд", + "Show rooms with unread messages first": "Показувати вгорі кімнати з непрочитаними повідомленнями", + "Show previews of messages": "Показувати попередній перегляд повідомлень", + "Sort by": "Упорядкувати за", + "Activity": "Активністю", + "A-Z": "А-Я", + "List options": "Параметри переліку", + "Use default": "Типово", + "Mentions & Keywords": "Згадки та ключові слова", + "Notification options": "Параметри сповіщень", + "Leave Room": "Вийти з кімнати", + "Forget Room": "Забути кімнату", + "Favourited": "Улюблено", + "%(count)s unread messages including mentions.|other": "%(count)s непрочитаних повідомлень включно зі згадками.", + "%(count)s unread messages including mentions.|one": "1 непрочитана згадка.", + "%(count)s unread messages.|other": "%(count)s непрочитаних повідомлень.", + "%(count)s unread messages.|one": "1 непрочитане повідомлення.", + "Unread messages.": "Непрочитані повідомлення.", + "This room is public": "Ця кімната є прилюдною", + "Show Stickers": "Показати наліпки", + "Failed to revoke invite": "Не вдалось відкликати запрошення", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Не вдалось відкликати запрошення. Сервер може мати тимчасові збої або у вас немає достатніх дозволів щоб відкликати запрошення.", + "Revoke invite": "Відкликати запрошення", + "Security": "Безпека", + "Report bugs & give feedback": "Відзвітувати про вади та залишити відгук", + "Report Content to Your Homeserver Administrator": "Поскаржитись на зміст адміністратору вашого домашнього сервера", + "Failed to upgrade room": "Не вдалось поліпшити кімнату", + "The room upgrade could not be completed": "Поліпшення кімнати не може бути завершене", + "Upgrade this room to version %(version)s": "Поліпшити цю кімнату до версії %(version)s", + "Upgrade Room Version": "Поліпшити версію кімнати", + "Upgrade private room": "Поліпшити закриту кімнату", + "You'll upgrade this room from to .": "Ви поліпшите цю кімнату з до версії.", + "Share Room Message": "Поширити повідомлення кімнати", + "Report Content": "Поскаржитись на зміст", + "Feedback": "Зворотній зв'язок", + "General failure": "Загальний збій", + "Enter your account password to confirm the upgrade:": "Введіть пароль вашого облікового запису щоб підтвердити поліпшення:" } From 41a749d3f632d5a1c3639d529f30152d560f117a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=9A=D0=BE=D1=80=D0=B0=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=80=D0=B0?= Date: Tue, 28 Jul 2020 09:30:08 +0000 Subject: [PATCH 045/136] Translated using Weblate (Ukrainian) Currently translated at 45.2% (1050 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 2084f16b68..09b89c26a6 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -1058,5 +1058,6 @@ "Report Content": "Поскаржитись на зміст", "Feedback": "Зворотній зв'язок", "General failure": "Загальний збій", - "Enter your account password to confirm the upgrade:": "Введіть пароль вашого облікового запису щоб підтвердити поліпшення:" + "Enter your account password to confirm the upgrade:": "Введіть пароль вашого облікового запису щоб підтвердити поліпшення:", + "Security & privacy": "Безпека та конфіденційність" } From e9b59a46697423dcd001e5a7afc7d17a893c870f Mon Sep 17 00:00:00 2001 From: rkfg Date: Tue, 28 Jul 2020 09:52:53 +0000 Subject: [PATCH 046/136] Translated using Weblate (Russian) Currently translated at 90.8% (2108 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index bc3ed92ec3..5e29bfed09 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -1547,8 +1547,8 @@ "%(creator)s created and configured the room.": "%(creator)s создал и настроил комнату.", "Preview": "Заглянуть", "View": "Просмотр", - "Find a room…": "Найди комнату…", - "Find a room… (e.g. %(exampleRoom)s)": "Найди комнату... (напр. %(exampleRoom)s)", + "Find a room…": "Поиск комнат…", + "Find a room… (e.g. %(exampleRoom)s)": "Поиск комнат... (напр. %(exampleRoom)s)", "Explore rooms": "Список комнат", "No identity server is configured: add one in server settings to reset your password.": "Идентификационный сервер не настроен: добавьте его в настройки сервера, чтобы сбросить пароль.", "Command Autocomplete": "Автозаполнение команды", @@ -1609,8 +1609,8 @@ "Verifies a user, session, and pubkey tuple": "Проверяет пользователя, сессию и публичные ключи", "Unknown (user, session) pair:": "Неизвестная (пользователь:сессия) пара:", "Session already verified!": "Сессия уже подтверждена!", - "Never send encrypted messages to unverified sessions from this session": "Никогда не отправляйте зашифрованные сообщения в непроверенные сессий из этой сессии", - "Never send encrypted messages to unverified sessions in this room from this session": "Никогда не отправляйте зашифрованные сообщения в непроверенные сессии в эту комнату из этой сессии", + "Never send encrypted messages to unverified sessions from this session": "Никогда не отправлять зашифрованные сообщения непроверенным сессиям в этой сессии", + "Never send encrypted messages to unverified sessions in this room from this session": "Никогда не отправлять зашифрованные сообщения непроверенным сессиям в этой комнате и в этой сессии", "Your keys are not being backed up from this session.": "Ваши ключи не резервируются с этой сессии.", "Server or user ID to ignore": "Сервер или ID пользователя для игнорирования", "Subscribed lists": "Подписанные списки", @@ -2172,5 +2172,8 @@ "This address is already in use": "Этот адрес уже используется", "Are you sure you want to remove %(serverName)s": "Вы уверены, что хотите удалить %(serverName)s", "Enter the name of a new server you want to explore.": "Введите имя нового сервера для просмотра.", - "Reminder: Your browser is unsupported, so your experience may be unpredictable.": "Напоминание: ваш браузер не поддерживается, возможны непредвиденные проблемы." + "Reminder: Your browser is unsupported, so your experience may be unpredictable.": "Напоминание: ваш браузер не поддерживается, возможны непредвиденные проблемы.", + "Notification settings": "Настройки уведомлений", + "Switch to light mode": "Переключить в светлый режим", + "Switch to dark mode": "Переключить в тёмный режим" } From 4db4625e34e1d9991ce922b5efc192815385691e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 11:37:59 -0600 Subject: [PATCH 047/136] Move SettingLevel to its own file --- src/settings/SettingLevel.ts | 29 +++++++++++++++++++++++++++++ src/settings/SettingsStore.js | 15 +++------------ 2 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 src/settings/SettingLevel.ts diff --git a/src/settings/SettingLevel.ts b/src/settings/SettingLevel.ts new file mode 100644 index 0000000000..e4703be1a9 --- /dev/null +++ b/src/settings/SettingLevel.ts @@ -0,0 +1,29 @@ +/* +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. +*/ + +/** + * Represents the various setting levels supported by the SettingsStore. + */ +export enum SettingLevel { + // TODO: [TS] Follow naming convention + DEVICE = "device", + ROOM_DEVICE = "room-device", + ROOM_ACCOUNT = "room-account", + ACCOUNT = "account", + ROOM = "room", + CONFIG = "config", + DEFAULT = "default", +} diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 488a15d003..a2e2fbadfc 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -28,21 +28,12 @@ import dis from '../dispatcher/dispatcher'; import {SETTINGS} from "./Settings"; import LocalEchoWrapper from "./handlers/LocalEchoWrapper"; import {WatchManager} from "./WatchManager"; +import {SettingLevel as SL2} from "./SettingLevel"; /** - * Represents the various setting levels supported by the SettingsStore. + * @deprecated Use SettingLevel directly */ -export const SettingLevel = { - // Note: This enum is not used in this class or in the Settings file - // This should always be used elsewhere in the project. - DEVICE: "device", - ROOM_DEVICE: "room-device", - ROOM_ACCOUNT: "room-account", - ACCOUNT: "account", - ROOM: "room", - CONFIG: "config", - DEFAULT: "default", -}; +export const SettingLevel = SL2; const defaultWatchManager = new WatchManager(); From 1f7f40736bab305be6afe957b75b8c219bd4868e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 11:53:43 -0600 Subject: [PATCH 048/136] Fix imports for SettingLevel to point at new file --- src/CallHandler.js | 3 ++- src/CallMediaHandler.js | 3 ++- src/Notifier.js | 3 ++- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 3 ++- .../views/dialogs/eventindex/ManageEventIndexDialog.js | 3 ++- src/components/structures/MatrixChat.tsx | 3 ++- src/components/structures/RoomView.js | 3 ++- src/components/structures/UserMenu.tsx | 3 ++- src/components/views/auth/LanguageSelector.js | 3 ++- src/components/views/dialogs/AskInviteAnywayDialog.js | 2 +- .../views/dialogs/WidgetOpenIDPermissionsDialog.js | 3 ++- src/components/views/elements/AppTile.js | 3 ++- src/components/views/elements/IRCTimelineProfileResizer.tsx | 3 ++- src/components/views/room_settings/ColorSettings.js | 3 ++- src/components/views/room_settings/UrlPreviewSettings.js | 3 ++- src/components/views/rooms/RoomRecoveryReminder.js | 3 ++- src/components/views/settings/E2eAdvancedPanel.js | 2 +- src/components/views/settings/EventIndexPanel.js | 3 ++- src/components/views/settings/Notifications.js | 3 ++- src/components/views/settings/SetIntegrationManager.js | 3 ++- .../views/settings/tabs/room/NotificationSettingsTab.js | 2 +- .../views/settings/tabs/room/SecurityRoomSettingsTab.js | 2 +- .../views/settings/tabs/user/AppearanceUserSettingsTab.tsx | 3 ++- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- .../views/settings/tabs/user/LabsUserSettingsTab.js | 3 ++- .../views/settings/tabs/user/PreferencesUserSettingsTab.js | 2 +- .../views/settings/tabs/user/SecurityUserSettingsTab.js | 2 +- .../views/settings/tabs/user/VoiceUserSettingsTab.js | 2 +- src/emojipicker/recent.ts | 3 ++- src/indexing/EventIndex.js | 3 ++- src/indexing/EventIndexPeg.js | 3 ++- src/languageHandler.tsx | 3 ++- src/mjolnir/Mjolnir.js | 3 ++- src/settings/SettingsStore.js | 6 ------ src/settings/handlers/AccountSettingsHandler.js | 2 +- src/settings/handlers/DeviceSettingsHandler.js | 2 +- src/settings/handlers/RoomAccountSettingsHandler.js | 2 +- src/settings/handlers/RoomDeviceSettingsHandler.js | 2 +- src/settings/handlers/RoomSettingsHandler.js | 2 +- src/settings/watchers/FontWatcher.ts | 3 ++- src/settings/watchers/ThemeWatcher.ts | 3 ++- src/stores/BreadcrumbsStore.ts | 3 ++- src/stores/RightPanelStore.js | 3 ++- 43 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index 4414bce457..810b2895d8 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -62,10 +62,11 @@ import Matrix from 'matrix-js-sdk'; import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; -import SettingsStore, { SettingLevel } from './settings/SettingsStore'; +import SettingsStore from './settings/SettingsStore'; import {generateHumanReadableId} from "./utils/NamingUtils"; import {Jitsi} from "./widgets/Jitsi"; import {WidgetType} from "./widgets/WidgetType"; +import {SettingLevel} from "./settings/SettingLevel"; global.mxCalls = { //room_id: MatrixCall diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index a0364f798a..8d56467c57 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -15,7 +15,8 @@ */ import * as Matrix from 'matrix-js-sdk'; -import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import SettingsStore from "./settings/SettingsStore"; +import {SettingLevel} from "./settings/SettingLevel"; export default { hasAnyLabeledDevices: async function() { diff --git a/src/Notifier.js b/src/Notifier.js index c6fc7d7985..2ed302267e 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -27,10 +27,11 @@ import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; -import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import SettingsStore from "./settings/SettingsStore"; import { hideToast as hideNotificationsToast, } from "./toasts/DesktopNotificationsToast"; +import {SettingLevel} from "./settings/SettingLevel"; /* * Dispatches: diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index ec4b88f759..de50feaedb 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -20,9 +20,10 @@ import PropTypes from 'prop-types'; import dis from "../../../../dispatcher/dispatcher"; import { _t } from '../../../../languageHandler'; -import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; +import SettingsStore from "../../../../settings/SettingsStore"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import {Action} from "../../../../dispatcher/actions"; +import {SettingLevel} from "../../../../settings/SettingLevel"; /* * Allows the user to disable the Event Index. diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index a9dd5be34b..be3368b87b 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -19,11 +19,12 @@ import * as sdk from '../../../../index'; import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; import SdkConfig from '../../../../SdkConfig'; -import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; +import SettingsStore from "../../../../settings/SettingsStore"; import Modal from '../../../../Modal'; import {formatBytes, formatCountLong} from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; +import {SettingLevel} from "../../../../settings/SettingLevel"; /* * Allows the user to introspect the event index state and disable it. diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index e68e1c53ae..7fba18f4e5 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -51,7 +51,7 @@ import { getHomePageUrl } from '../../utils/pages'; import createRoom from "../../createRoom"; import {_t, _td, getCurrentLanguage} from '../../languageHandler'; -import SettingsStore, { SettingLevel } from "../../settings/SettingsStore"; +import SettingsStore from "../../settings/SettingsStore"; import ThemeController from "../../settings/controllers/ThemeController"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; @@ -75,6 +75,7 @@ import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificat import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; import ErrorDialog from "../views/dialogs/ErrorDialog"; import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore"; +import { SettingLevel } from "../../settings/SettingLevel"; /** constants for MatrixChat.state.view */ export enum Views { diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7dc2d57ff0..f585a97fde 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -48,7 +48,7 @@ import RightPanel from './RightPanel'; import RoomViewStore from '../../stores/RoomViewStore'; import RoomScrollStateStore from '../../stores/RoomScrollStateStore'; import WidgetEchoStore from '../../stores/WidgetEchoStore'; -import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; +import SettingsStore from "../../settings/SettingsStore"; import AccessibleButton from "../views/elements/AccessibleButton"; import RightPanelStore from "../../stores/RightPanelStore"; import {haveTileForEvent} from "../views/rooms/EventTile"; @@ -56,6 +56,7 @@ import RoomContext from "../../contexts/RoomContext"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { shieldStatusForRoom } from '../../utils/ShieldUtils'; import {Action} from "../../dispatcher/actions"; +import {SettingLevel} from "../../settings/SettingLevel"; const DEBUG = false; let debuglog = function() {}; diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 8f9ca8d734..3f2e387ccb 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -26,7 +26,7 @@ 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 SettingsStore from "../../settings/SettingsStore"; import {getCustomTheme} from "../../theme"; import {getHostingLink} from "../../utils/HostingLink"; import {ButtonEvent} from "../views/elements/AccessibleButton"; @@ -37,6 +37,7 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore"; import BaseAvatar from '../views/avatars/BaseAvatar'; import classNames from "classnames"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; +import { SettingLevel } from "../../settings/SettingLevel"; interface IProps { isMinimized: boolean; diff --git a/src/components/views/auth/LanguageSelector.js b/src/components/views/auth/LanguageSelector.js index 83db5d225b..0738ee43e4 100644 --- a/src/components/views/auth/LanguageSelector.js +++ b/src/components/views/auth/LanguageSelector.js @@ -16,10 +16,11 @@ limitations under the License. import SdkConfig from "../../../SdkConfig"; import {getCurrentLanguage} from "../../../languageHandler"; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; import PlatformPeg from "../../../PlatformPeg"; import * as sdk from '../../../index'; import React from 'react'; +import {SettingLevel} from "../../../settings/SettingLevel"; function onChange(newLang) { if (getCurrentLanguage() !== newLang) { diff --git a/src/components/views/dialogs/AskInviteAnywayDialog.js b/src/components/views/dialogs/AskInviteAnywayDialog.js index 120ad8deca..7a12d2bd20 100644 --- a/src/components/views/dialogs/AskInviteAnywayDialog.js +++ b/src/components/views/dialogs/AskInviteAnywayDialog.js @@ -19,8 +19,8 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import {SettingLevel} from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default createReactClass({ propTypes: { diff --git a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js index 162cb4736a..42a5304f13 100644 --- a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js +++ b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js @@ -17,10 +17,11 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../languageHandler"; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; import * as sdk from "../../../index"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import WidgetUtils from "../../../utils/WidgetUtils"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default class WidgetOpenIDPermissionsDialog extends React.Component { static propTypes = { diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 3e4418f945..d0fc56743f 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -35,12 +35,13 @@ import dis from '../../../dispatcher/dispatcher'; import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; import classNames from 'classnames'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore 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"; +import {SettingLevel} from "../../../settings/SettingLevel"; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; const ENABLE_REACT_PERF = false; diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 65140707d5..1098d0293e 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -15,8 +15,9 @@ limitations under the License. */ import React from 'react'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; import Draggable, {ILocationState} from './Draggable'; +import { SettingLevel } from "../../../settings/SettingLevel"; interface IProps { // Current room diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 1d26e956ab..4e8b6ba42f 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -20,7 +20,8 @@ import createReactClass from 'create-react-class'; import Tinter from '../../../Tinter'; import dis from '../../../dispatcher/dispatcher'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; const ROOM_COLORS = [ // magic room default values courtesy of Ribot diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js index 51f6954975..fa5c1baf8f 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.js +++ b/src/components/views/room_settings/UrlPreviewSettings.js @@ -22,10 +22,11 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from "../../../index"; import { _t, _td } from '../../../languageHandler'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; import dis from "../../../dispatcher/dispatcher"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {Action} from "../../../dispatcher/actions"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default createReactClass({ diff --git a/src/components/views/rooms/RoomRecoveryReminder.js b/src/components/views/rooms/RoomRecoveryReminder.js index a29467b07f..859df6dd1b 100644 --- a/src/components/views/rooms/RoomRecoveryReminder.js +++ b/src/components/views/rooms/RoomRecoveryReminder.js @@ -21,7 +21,8 @@ import * as sdk from "../../../index"; import { _t } from "../../../languageHandler"; import Modal from "../../../Modal"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default class RoomRecoveryReminder extends React.PureComponent { static propTypes = { diff --git a/src/components/views/settings/E2eAdvancedPanel.js b/src/components/views/settings/E2eAdvancedPanel.js index 709465bcb0..2ba6190a9b 100644 --- a/src/components/views/settings/E2eAdvancedPanel.js +++ b/src/components/views/settings/E2eAdvancedPanel.js @@ -18,7 +18,7 @@ import React from 'react'; import * as sdk from '../../../index'; import {_t} from "../../../languageHandler"; -import {SettingLevel} from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; const SETTING_MANUALLY_VERIFY_ALL_SESSIONS = "e2ee.manuallyVerifyAllSessions"; diff --git a/src/components/views/settings/EventIndexPanel.js b/src/components/views/settings/EventIndexPanel.js index 6fd1247c4b..35c751d91e 100644 --- a/src/components/views/settings/EventIndexPanel.js +++ b/src/components/views/settings/EventIndexPanel.js @@ -20,10 +20,11 @@ import { _t } from '../../../languageHandler'; import SdkConfig from "../../../SdkConfig"; import * as sdk from '../../../index'; import Modal from '../../../Modal'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; import {formatBytes, formatCountLong} from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default class EventIndexPanel extends React.Component { constructor() { diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js index cbdf3ec1a7..5c16e1ed4f 100644 --- a/src/components/views/settings/Notifications.js +++ b/src/components/views/settings/Notifications.js @@ -20,7 +20,7 @@ import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import SettingsStore, {SettingLevel} from '../../../settings/SettingsStore'; +import SettingsStore from '../../../settings/SettingsStore'; import Modal from '../../../Modal'; import { NotificationUtils, @@ -31,6 +31,7 @@ import { import SdkConfig from "../../../SdkConfig"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import AccessibleButton from "../elements/AccessibleButton"; +import {SettingLevel} from "../../../settings/SettingLevel"; // TODO: this "view" component still has far too much application logic in it, // which should be factored out to other files. diff --git a/src/components/views/settings/SetIntegrationManager.js b/src/components/views/settings/SetIntegrationManager.js index da2953482f..e6fb3f6e1c 100644 --- a/src/components/views/settings/SetIntegrationManager.js +++ b/src/components/views/settings/SetIntegrationManager.js @@ -18,7 +18,8 @@ import React from 'react'; import {_t} from "../../../languageHandler"; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; import * as sdk from '../../../index'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; export default class SetIntegrationManager extends React.Component { constructor() { diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 257f4a5d23..dd88b5018f 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -21,7 +21,7 @@ import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; import SettingsStore from '../../../../../settings/SettingsStore'; -import { SettingLevel } from '../../../../../settings/SettingsStore'; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class NotificationsSettingsTab extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js index c67596a3a5..596344d7cd 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js @@ -20,9 +20,9 @@ import {_t} from "../../../../../languageHandler"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; -import {SettingLevel} from "../../../../../settings/SettingsStore"; import Modal from "../../../../../Modal"; import QuestionDialog from "../../../dialogs/QuestionDialog"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class SecurityRoomSettingsTab extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 9f3b8ba46c..c646025bbe 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../../languageHandler"; import SdkConfig from "../../../../../SdkConfig"; -import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore"; +import SettingsStore from "../../../../../settings/SettingsStore"; import { enumerateThemes } from "../../../../../theme"; import ThemeWatcher from "../../../../../settings/watchers/ThemeWatcher"; import Slider from "../../../elements/Slider"; @@ -35,6 +35,7 @@ import Field from '../../../elements/Field'; import EventTilePreview from '../../../elements/EventTilePreview'; import StyledRadioGroup from "../../../elements/StyledRadioGroup"; import classNames from 'classnames'; +import { SettingLevel } from "../../../../../settings/SettingLevel"; interface IProps { } diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index c7e7ce7c2c..1ebefae590 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -20,7 +20,6 @@ import React from 'react'; import {_t} from "../../../../../languageHandler"; import ProfileSettings from "../../ProfileSettings"; import * as languageHandler from "../../../../../languageHandler"; -import {SettingLevel} from "../../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore"; import LanguageDropdown from "../../../elements/LanguageDropdown"; import AccessibleButton from "../../../elements/AccessibleButton"; @@ -37,6 +36,7 @@ import IdentityAuthClient from "../../../../../IdentityAuthClient"; import {abbreviateUrl} from "../../../../../utils/UrlUtils"; import { getThreepidsWithBindStatus } from '../../../../../boundThreepids'; import Spinner from "../../../elements/Spinner"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class GeneralUserSettingsTab extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js index 2b8d7c5d3f..25dfd9e100 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js @@ -17,9 +17,10 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../../languageHandler"; import PropTypes from "prop-types"; -import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore"; +import SettingsStore from "../../../../../settings/SettingsStore"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import * as sdk from "../../../../../index"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export class LabsSettingToggle extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index fe60a4a179..a77815a68c 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -17,12 +17,12 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../../languageHandler"; -import {SettingLevel} from "../../../../../settings/SettingsStore"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class PreferencesUserSettingsTab extends React.Component { static ROOM_LIST_SETTINGS = [ diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 591927411d..fda7ccb005 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -19,7 +19,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../../../languageHandler"; import SdkConfig from "../../../../../SdkConfig"; -import {SettingLevel} from "../../../../../settings/SettingsStore"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as FormattingUtils from "../../../../../utils/FormattingUtils"; import AccessibleButton from "../../../elements/AccessibleButton"; @@ -29,6 +28,7 @@ import * as sdk from "../../../../.."; import {sleep} from "../../../../../utils/promise"; import dis from "../../../../../dispatcher/dispatcher"; import {privateShouldBeEncrypted} from "../../../../../createRoom"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export class IgnoredUser extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js index efd184069e..4114f6bb22 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js @@ -21,10 +21,10 @@ import SdkConfig from "../../../../../SdkConfig"; import CallMediaHandler from "../../../../../CallMediaHandler"; import Field from "../../../elements/Field"; import AccessibleButton from "../../../elements/AccessibleButton"; -import {SettingLevel} from "../../../../../settings/SettingsStore"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../../index"; import Modal from "../../../../../Modal"; +import {SettingLevel} from "../../../../../settings/SettingLevel"; export default class VoiceUserSettingsTab extends React.Component { constructor() { diff --git a/src/emojipicker/recent.ts b/src/emojipicker/recent.ts index 1ba15d87b8..d86aad660d 100644 --- a/src/emojipicker/recent.ts +++ b/src/emojipicker/recent.ts @@ -15,8 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; +import SettingsStore from "../settings/SettingsStore"; import {orderBy} from "lodash"; +import { SettingLevel } from "../settings/SettingLevel"; interface ILegacyFormat { [emoji: string]: [number, number]; // [count, date] diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 1dc31869c9..d5abb3f2d2 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -18,8 +18,9 @@ import PlatformPeg from "../PlatformPeg"; import {MatrixClientPeg} from "../MatrixClientPeg"; import {EventTimeline, RoomMember} from 'matrix-js-sdk'; import {sleep} from "../utils/promise"; -import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; +import SettingsStore from "../settings/SettingsStore"; import {EventEmitter} from "events"; +import {SettingLevel} from "../settings/SettingLevel"; /* * Event indexing class that wraps the platform specific event indexing. diff --git a/src/indexing/EventIndexPeg.js b/src/indexing/EventIndexPeg.js index 20e05f985d..58e8430825 100644 --- a/src/indexing/EventIndexPeg.js +++ b/src/indexing/EventIndexPeg.js @@ -21,7 +21,8 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import EventIndex from "../indexing/EventIndex"; -import SettingsStore, {SettingLevel} from '../settings/SettingsStore'; +import SettingsStore from '../settings/SettingsStore'; +import {SettingLevel} from "../settings/SettingLevel"; const INDEX_VERSION = 1; diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 91d90d4e6c..9d28b7d07d 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -21,11 +21,12 @@ import request from 'browser-request'; import counterpart from 'counterpart'; import React from 'react'; -import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; +import SettingsStore from "./settings/SettingsStore"; import PlatformPeg from "./PlatformPeg"; // @ts-ignore - $webapp is a webpack resolve alias pointing to the output directory, see webpack config import webpackLangJsonUrl from "$webapp/i18n/languages.json"; +import { SettingLevel } from "./settings/SettingLevel"; const i18nFolder = 'i18n/'; diff --git a/src/mjolnir/Mjolnir.js b/src/mjolnir/Mjolnir.js index 9876cb1f7f..891438bbb9 100644 --- a/src/mjolnir/Mjolnir.js +++ b/src/mjolnir/Mjolnir.js @@ -16,9 +16,10 @@ limitations under the License. import {MatrixClientPeg} from "../MatrixClientPeg"; import {ALL_RULE_TYPES, BanList} from "./BanList"; -import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; +import SettingsStore from "../settings/SettingsStore"; import {_t} from "../languageHandler"; import dis from "../dispatcher/dispatcher"; +import {SettingLevel} from "../settings/SettingLevel"; // TODO: Move this and related files to the js-sdk or something once finalized. diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index a2e2fbadfc..a487f4e459 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -28,12 +28,6 @@ import dis from '../dispatcher/dispatcher'; import {SETTINGS} from "./Settings"; import LocalEchoWrapper from "./handlers/LocalEchoWrapper"; import {WatchManager} from "./WatchManager"; -import {SettingLevel as SL2} from "./SettingLevel"; - -/** - * @deprecated Use SettingLevel directly - */ -export const SettingLevel = SL2; const defaultWatchManager = new WatchManager(); diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index 4048d8ddea..5b980801d4 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -17,8 +17,8 @@ limitations under the License. import {MatrixClientPeg} from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; -import {SettingLevel} from "../SettingsStore"; import {objectClone, objectKeyChanges} from "../../utils/objects"; +import {SettingLevel} from "../SettingLevel"; const BREADCRUMBS_LEGACY_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms"; const BREADCRUMBS_EVENT_TYPE = "im.vector.setting.breadcrumbs"; diff --git a/src/settings/handlers/DeviceSettingsHandler.js b/src/settings/handlers/DeviceSettingsHandler.js index 44f89b9086..47c9bbd5b9 100644 --- a/src/settings/handlers/DeviceSettingsHandler.js +++ b/src/settings/handlers/DeviceSettingsHandler.js @@ -18,7 +18,7 @@ limitations under the License. import SettingsHandler from "./SettingsHandler"; import {MatrixClientPeg} from "../../MatrixClientPeg"; -import {SettingLevel} from "../SettingsStore"; +import {SettingLevel} from "../SettingLevel"; /** * Gets and sets settings at the "device" level for the current device. diff --git a/src/settings/handlers/RoomAccountSettingsHandler.js b/src/settings/handlers/RoomAccountSettingsHandler.js index b2af81779b..86fd5cb61f 100644 --- a/src/settings/handlers/RoomAccountSettingsHandler.js +++ b/src/settings/handlers/RoomAccountSettingsHandler.js @@ -17,8 +17,8 @@ limitations under the License. import {MatrixClientPeg} from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; -import {SettingLevel} from "../SettingsStore"; import {objectClone, objectKeyChanges} from "../../utils/objects"; +import {SettingLevel} from "../SettingLevel"; const ALLOWED_WIDGETS_EVENT_TYPE = "im.vector.setting.allowed_widgets"; diff --git a/src/settings/handlers/RoomDeviceSettingsHandler.js b/src/settings/handlers/RoomDeviceSettingsHandler.js index a9cf686c4c..cd1f9e9265 100644 --- a/src/settings/handlers/RoomDeviceSettingsHandler.js +++ b/src/settings/handlers/RoomDeviceSettingsHandler.js @@ -16,7 +16,7 @@ limitations under the License. */ import SettingsHandler from "./SettingsHandler"; -import {SettingLevel} from "../SettingsStore"; +import {SettingLevel} from "../SettingLevel"; /** * Gets and sets settings at the "room-device" level for the current device in a particular diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.js index 00dd5b8bec..321f329ea0 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.js @@ -17,8 +17,8 @@ limitations under the License. import {MatrixClientPeg} from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; -import {SettingLevel} from "../SettingsStore"; import {objectClone, objectKeyChanges} from "../../utils/objects"; +import {SettingLevel} from "../SettingLevel"; /** * Gets and sets settings at the "room" level. diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index 53ef999f9b..6a17bf2aa3 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -15,10 +15,11 @@ limitations under the License. */ import dis from '../../dispatcher/dispatcher'; -import SettingsStore, {SettingLevel} from '../SettingsStore'; +import SettingsStore from '../SettingsStore'; import IWatcher from "./Watcher"; import { toPx } from '../../utils/units'; import { Action } from '../../dispatcher/actions'; +import { SettingLevel } from "../SettingLevel"; export class FontWatcher implements IWatcher { public static readonly MIN_SIZE = 8; diff --git a/src/settings/watchers/ThemeWatcher.ts b/src/settings/watchers/ThemeWatcher.ts index ce0db881c0..8c9e126497 100644 --- a/src/settings/watchers/ThemeWatcher.ts +++ b/src/settings/watchers/ThemeWatcher.ts @@ -15,12 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore, { SettingLevel } from '../SettingsStore'; +import SettingsStore from '../SettingsStore'; import dis from '../../dispatcher/dispatcher'; import { Action } from '../../dispatcher/actions'; import ThemeController from "../controllers/ThemeController"; import { setTheme } from "../../theme"; import { ActionPayload } from '../../dispatcher/payloads'; +import { SettingLevel } from "../SettingLevel"; export default class ThemeWatcher { // XXX: I think this is unused. diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index 34affbe746..e921a3ff3e 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -14,13 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore, { SettingLevel } from "../settings/SettingsStore"; +import SettingsStore 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"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; +import { SettingLevel } from "../settings/SettingLevel"; const MAX_ROOMS = 20; // arbitrary const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js index a73f3befbb..6920226448 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.js @@ -17,8 +17,9 @@ limitations under the License. import dis from '../dispatcher/dispatcher'; import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; -import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; +import SettingsStore from "../settings/SettingsStore"; import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases"; +import {SettingLevel} from "../settings/SettingLevel"; const INITIAL_STATE = { // Whether or not to show the right panel at all. We split out rooms and groups From 27b81d1e262a059ed9f2246f9e283f804a9fb922 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 12:02:02 -0600 Subject: [PATCH 049/136] Roughly convert Settings to TS --- src/settings/{Settings.js => Settings.ts} | 166 ++++++++++++---------- 1 file changed, 90 insertions(+), 76 deletions(-) rename src/settings/{Settings.js => Settings.ts} (81%) diff --git a/src/settings/Settings.js b/src/settings/Settings.ts similarity index 81% rename from src/settings/Settings.js rename to src/settings/Settings.ts index 50d6ffc09e..f869c6c83e 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.ts @@ -16,9 +16,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClient} from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; -import {_td} from '../languageHandler'; +import { _td } from '../languageHandler'; import { AudioNotificationsEnabledController, NotificationBodyEnabledController, @@ -28,75 +28,88 @@ import CustomStatusController from "./controllers/CustomStatusController"; import ThemeController from './controllers/ThemeController'; import PushToMatrixClientController from './controllers/PushToMatrixClientController'; import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; -import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases"; +import { RIGHT_PANEL_PHASES } from "../stores/RightPanelStorePhases"; import FontSizeController from './controllers/FontSizeController'; import SystemFontController from './controllers/SystemFontController'; import UseSystemFontController from './controllers/UseSystemFontController'; +import { SettingLevel } from "./SettingLevel"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times -const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; -const LEVELS_ROOM_OR_ACCOUNT = ['room-account', 'account']; -const LEVELS_ROOM_SETTINGS_WITH_ROOM = ['device', 'room-device', 'room-account', 'account', 'config', 'room']; -const LEVELS_ACCOUNT_SETTINGS = ['device', 'account', 'config']; -const LEVELS_FEATURE = ['device', 'config']; -const LEVELS_DEVICE_ONLY_SETTINGS = ['device']; -const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = ['device', 'config']; +const LEVELS_ROOM_SETTINGS = [ + SettingLevel.DEVICE, + SettingLevel.ROOM_DEVICE, + SettingLevel.ROOM_ACCOUNT, + SettingLevel.ACCOUNT, + SettingLevel.CONFIG, +]; +const LEVELS_ROOM_OR_ACCOUNT = [ + SettingLevel.ROOM_ACCOUNT, + SettingLevel.ACCOUNT, +]; +const LEVELS_ROOM_SETTINGS_WITH_ROOM = [ + SettingLevel.DEVICE, + SettingLevel.ROOM_DEVICE, + SettingLevel.ROOM_ACCOUNT, + SettingLevel.ACCOUNT, + SettingLevel.CONFIG, + SettingLevel.ROOM, +]; +const LEVELS_ACCOUNT_SETTINGS = [ + SettingLevel.DEVICE, + SettingLevel.ACCOUNT, + SettingLevel.CONFIG, +]; +const LEVELS_FEATURE = [ + SettingLevel.DEVICE, + SettingLevel.CONFIG, +]; +const LEVELS_DEVICE_ONLY_SETTINGS = [ + SettingLevel.DEVICE, +]; +const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = [ + SettingLevel.DEVICE, + SettingLevel.CONFIG, +]; -export const SETTINGS = { - // EXAMPLE SETTING: - // "my-setting": { - // // Must be set to true for features. Default is 'false'. - // isFeature: false, - // - // // Display names are strongly recommended for clarity. - // displayName: _td("Cool Name"), - // - // // Display name can also be an object for different levels. - // //displayName: { - // // "device": _td("Name for when the setting is used at 'device'"), - // // "room": _td("Name for when the setting is used at 'room'"), - // // "default": _td("The name for all other levels"), - // //} - // - // // The supported levels are required. Preferably, use the preset arrays - // // at the top of this file to define this rather than a custom array. - // supportedLevels: [ - // // The order does not matter. - // - // "device", // Affects the current device only - // "room-device", // Affects the current room on the current device - // "room-account", // Affects the current room for the current account - // "account", // Affects the current account - // "room", // Affects the current room (controlled by room admins) - // "config", // Affects the current application - // - // // "default" is always supported and does not get listed here. - // ], - // - // // Required. Can be any data type. The value specified here should match - // // the data being stored (ie: if a boolean is used, the setting should - // // represent a boolean). - // default: { - // your: "value", - // }, - // - // // Optional settings controller. See SettingsController for more information. - // controller: new MySettingController(), - // - // // Optional flag to make supportedLevels be respected as the order to handle - // // settings. The first element is treated as "most preferred". The "default" - // // level is always appended to the end. - // supportedLevelsAreOrdered: false, - // - // // Optional value to invert a boolean setting's value. The string given will - // // be read as the setting's ID instead of the one provided as the key for the - // // setting definition. By setting this, the returned value will automatically - // // be inverted, except for when the default value is returned. Inversion will - // // occur after the controller is asked for an override. This should be used by - // // historical settings which we don't want existing user's values be wiped. Do - // // not use this for new settings. - // invertedSettingName: "my-negative-setting", - // }, +interface ISetting { + // Must be set to true for features. Default is 'false'. + isFeature?: boolean; + + // Display names are strongly recommended for clarity. + // Display name can also be an object for different levels. + displayName?: string | { + // @ts-ignore - TS wants the key to be a string, but we know better + [level: SettingLevel]: string; + }; + + // The supported levels are required. Preferably, use the preset arrays + // at the top of this file to define this rather than a custom array. + supportedLevels?: SettingLevel[]; + + // Required. Can be any data type. The value specified here should match + // the data being stored (ie: if a boolean is used, the setting should + // represent a boolean). + default: any; + + // Optional settings controller. See SettingsController for more information. + controller?: any; // TODO: [TS] Type + + // Optional flag to make supportedLevels be respected as the order to handle + // settings. The first element is treated as "most preferred". The "default" + // level is always appended to the end. + supportedLevelsAreOrdered?: boolean; + + // Optional value to invert a boolean setting's value. The string given will + // be read as the setting's ID instead of the one provided as the key for the + // setting definition. By setting this, the returned value will automatically + // be inverted, except for when the default value is returned. Inversion will + // occur after the controller is asked for an override. This should be used by + // historical settings which we don't want existing user's values be wiped. Do + // not use this for new settings. + invertedSettingName?: string; +} + +export const SETTINGS: {[setting: string]: ISetting} = { "feature_new_spinner": { isFeature: true, displayName: _td("New spinner design"), @@ -153,11 +166,11 @@ export const SETTINGS = { default: false, }, "mjolnirRooms": { - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: [], }, "mjolnirPersonalRoom": { - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: null, }, "feature_bridge_state": { @@ -354,24 +367,24 @@ export const SETTINGS = { }, "breadcrumb_rooms": { // not really a setting - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: [], }, "recent_emoji": { // not really a setting - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: [], }, "room_directory_servers": { - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: [], }, "integrationProvisioning": { - supportedLevels: ['account'], + supportedLevels: [SettingLevel.ACCOUNT], default: true, }, "allowedWidgets": { - supportedLevels: ['room-account'], + supportedLevels: [SettingLevel.ROOM_ACCOUNT], default: {}, // none allowed }, "analyticsOptIn": { @@ -398,7 +411,7 @@ export const SETTINGS = { "blacklistUnverifiedDevices": { // We specifically want to have room-device > device so that users may set a device default // with a per-room override. - supportedLevels: ['room-device', 'device'], + supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.ROOM_ACCOUNT], supportedLevelsAreOrdered: true, displayName: { "default": _td('Never send encrypted messages to unverified sessions from this session'), @@ -416,7 +429,7 @@ export const SETTINGS = { default: true, }, "urlPreviewsEnabled_e2ee": { - supportedLevels: ['room-device', 'room-account'], + supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.ROOM_ACCOUNT], displayName: { "room-account": _td("Enable URL previews for this room (only affects you)"), }, @@ -455,7 +468,7 @@ export const SETTINGS = { default: false, }, "PinnedEvents.isOpen": { - supportedLevels: ['room-device'], + supportedLevels: [SettingLevel.ROOM_DEVICE], default: false, }, "promptBeforeInviteUnknownUsers": { @@ -565,7 +578,8 @@ export const SETTINGS = { "ircDisplayNameWidth": { // We specifically want to have room-device > device so that users may set a device default // with a per-room override. - supportedLevels: ['room-device', 'device'], + supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.DEVICE], + supportedLevelsAreOrdered: true, displayName: _td("IRC display name width"), default: 80, }, From 62970049570aefe61216e8aec722b0ea0fd3fcd3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:13:38 -0600 Subject: [PATCH 050/136] Convert SettingController to TS --- .../{SettingController.js => SettingController.ts} | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename src/settings/controllers/{SettingController.js => SettingController.ts} (87%) diff --git a/src/settings/controllers/SettingController.js b/src/settings/controllers/SettingController.ts similarity index 87% rename from src/settings/controllers/SettingController.js rename to src/settings/controllers/SettingController.ts index a7d0ccf21a..35a116e001 100644 --- a/src/settings/controllers/SettingController.js +++ b/src/settings/controllers/SettingController.ts @@ -1,5 +1,6 @@ /* Copyright 2017 Travis Ralston +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. @@ -14,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { SettingLevel } from "../SettingLevel"; + /** * Represents a controller for individual settings to alter the reading behaviour * based upon environmental conditions, or to react to changes and therefore update @@ -34,7 +37,7 @@ export default class SettingController { * calculated at. May be null. * @return {*} The value that should be used, or null if no override is applicable. */ - getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { return null; // no override } @@ -44,7 +47,7 @@ export default class SettingController { * @param {String} roomId The room ID, may be null. * @param {*} newValue The new value for the setting, may be null. */ - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // do nothing by default } } From d4fd3b32625677d05af42fa9aee174442ef57a4b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:17:01 -0600 Subject: [PATCH 051/136] Convert CustomStatusController to TS --- .../{CustomStatusController.js => CustomStatusController.ts} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename src/settings/controllers/{CustomStatusController.js => CustomStatusController.ts} (85%) diff --git a/src/settings/controllers/CustomStatusController.js b/src/settings/controllers/CustomStatusController.ts similarity index 85% rename from src/settings/controllers/CustomStatusController.js rename to src/settings/controllers/CustomStatusController.ts index 031387bb6a..27865c2163 100644 --- a/src/settings/controllers/CustomStatusController.js +++ b/src/settings/controllers/CustomStatusController.ts @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,9 +17,10 @@ limitations under the License. import SettingController from "./SettingController"; import dis from "../../dispatcher/dispatcher"; +import { SettingLevel } from "../SettingLevel"; export default class CustomStatusController extends SettingController { - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // Dispatch setting change so that some components that are still visible when the // Settings page is open (such as RoomTiles) can reflect the change. dis.dispatch({ From 437ccb2421a4081c9883d082e20b25feb13b9b78 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:19:11 -0600 Subject: [PATCH 052/136] Convert NotificationControllers to TS --- ...onControllers.js => NotificationControllers.ts} | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) rename src/settings/controllers/{NotificationControllers.js => NotificationControllers.ts} (80%) diff --git a/src/settings/controllers/NotificationControllers.js b/src/settings/controllers/NotificationControllers.ts similarity index 80% rename from src/settings/controllers/NotificationControllers.js rename to src/settings/controllers/NotificationControllers.ts index e38a5bded1..23b753fc11 100644 --- a/src/settings/controllers/NotificationControllers.js +++ b/src/settings/controllers/NotificationControllers.ts @@ -1,5 +1,6 @@ /* Copyright 2017 Travis Ralston +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. @@ -16,13 +17,14 @@ limitations under the License. import SettingController from "./SettingController"; import {MatrixClientPeg} from '../../MatrixClientPeg'; +import { SettingLevel } from "../SettingLevel"; // XXX: This feels wrong. import {PushProcessor} from "matrix-js-sdk/src/pushprocessor"; // .m.rule.master being enabled means all events match that push rule // default action on this rule is dont_notify, but it could be something else -function isPushNotifyDisabled() { +function isPushNotifyDisabled(): boolean { // Return the value of the master push rule as a default const processor = new PushProcessor(MatrixClientPeg.get()); const masterRule = processor.getPushRuleById(".m.rule.master"); @@ -36,14 +38,14 @@ function isPushNotifyDisabled() { return masterRule.enabled && !masterRule.actions.includes("notify"); } -function getNotifier() { +function getNotifier(): any { // TODO: [TS] Formal type that doesn't cause a cyclical reference. let Notifier = require('../../Notifier'); // avoids cyclical references if (Notifier.default) Notifier = Notifier.default; // correct for webpack require() weirdness return Notifier; } export class NotificationsEnabledController extends SettingController { - getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { if (!getNotifier().isPossible()) return false; if (calculatedValue === null || calculatedAtLevel === "default") { @@ -53,7 +55,7 @@ export class NotificationsEnabledController extends SettingController { return calculatedValue; } - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue) { if (getNotifier().supportsDesktopNotifications()) { getNotifier().setEnabled(newValue); } @@ -61,7 +63,7 @@ export class NotificationsEnabledController extends SettingController { } export class NotificationBodyEnabledController extends SettingController { - getValueOverride(level, roomId, calculatedValue) { + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any { if (!getNotifier().isPossible()) return false; if (calculatedValue === null) { @@ -73,7 +75,7 @@ export class NotificationBodyEnabledController extends SettingController { } export class AudioNotificationsEnabledController extends SettingController { - getValueOverride(level, roomId, calculatedValue) { + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any { if (!getNotifier().isPossible()) return false; // Note: Audio notifications are *not* enabled by default. From 227b6114218992de14b3da9017873a9cb555dbdf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:21:10 -0600 Subject: [PATCH 053/136] Convert PushToMatrixClientController to TS --- ...oller.js => PushToMatrixClientController.ts} | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) rename src/settings/controllers/{PushToMatrixClientController.js => PushToMatrixClientController.ts} (67%) diff --git a/src/settings/controllers/PushToMatrixClientController.js b/src/settings/controllers/PushToMatrixClientController.ts similarity index 67% rename from src/settings/controllers/PushToMatrixClientController.js rename to src/settings/controllers/PushToMatrixClientController.ts index b7c285227f..8fbf7eb34c 100644 --- a/src/settings/controllers/PushToMatrixClientController.js +++ b/src/settings/controllers/PushToMatrixClientController.ts @@ -15,23 +15,20 @@ limitations under the License. */ import { MatrixClientPeg } from '../../MatrixClientPeg'; +import { SettingLevel } from "../SettingLevel"; +import SettingController from "./SettingController"; /** * When the value changes, call a setter function on the matrix client with the new value */ -export default class PushToMatrixClientController { - constructor(setter, inverse) { - this._setter = setter; - this._inverse = inverse; +export default class PushToMatrixClientController extends SettingController { + constructor(private setter: Function, private inverse: boolean) { + super(); } - getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { - return null; // no override - } - - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // XXX does this work? This surely isn't necessarily the effective value, // but it's what NotificationsEnabledController does... - this._setter.call(MatrixClientPeg.get(), this._inverse ? !newValue : newValue); + this.setter.call(MatrixClientPeg.get(), this.inverse ? !newValue : newValue); } } From d7e90e045da621bf72bdba53883f021d26c6e152 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:21:53 -0600 Subject: [PATCH 054/136] Convert ReloadOnChangeController to TS --- ...loadOnChangeController.js => ReloadOnChangeController.ts} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename src/settings/controllers/{ReloadOnChangeController.js => ReloadOnChangeController.ts} (80%) diff --git a/src/settings/controllers/ReloadOnChangeController.js b/src/settings/controllers/ReloadOnChangeController.ts similarity index 80% rename from src/settings/controllers/ReloadOnChangeController.js rename to src/settings/controllers/ReloadOnChangeController.ts index eadaee89ca..12bc23ef6c 100644 --- a/src/settings/controllers/ReloadOnChangeController.js +++ b/src/settings/controllers/ReloadOnChangeController.ts @@ -1,5 +1,5 @@ /* -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. @@ -16,9 +16,10 @@ limitations under the License. import SettingController from "./SettingController"; import PlatformPeg from "../../PlatformPeg"; +import { SettingLevel } from "../SettingLevel"; export default class ReloadOnChangeController extends SettingController { - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { PlatformPeg.get().reload(); } } From ef062c3511ad8c7182f4903203e41b2d477b076f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:22:54 -0600 Subject: [PATCH 055/136] Convert ThemeController to TS --- .../controllers/{ThemeController.js => ThemeController.ts} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename src/settings/controllers/{ThemeController.js => ThemeController.ts} (81%) diff --git a/src/settings/controllers/ThemeController.js b/src/settings/controllers/ThemeController.ts similarity index 81% rename from src/settings/controllers/ThemeController.js rename to src/settings/controllers/ThemeController.ts index 4098a5ca3e..01082a43a4 100644 --- a/src/settings/controllers/ThemeController.js +++ b/src/settings/controllers/ThemeController.ts @@ -1,6 +1,6 @@ /* -Copyright 2019 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +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. @@ -17,11 +17,12 @@ limitations under the License. import SettingController from "./SettingController"; import {DEFAULT_THEME, enumerateThemes} from "../../theme"; +import { SettingLevel } from "../SettingLevel"; export default class ThemeController extends SettingController { - static isLogin = false; + public static isLogin = false; - getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { if (!calculatedValue) return null; // Don't override null themes if (ThemeController.isLogin) return 'light'; From b5be352e889d80762adb04dd5e25fec0f65b7ae5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:23:25 -0600 Subject: [PATCH 056/136] Fix copyright in CustomStatusController --- src/settings/controllers/CustomStatusController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/settings/controllers/CustomStatusController.ts b/src/settings/controllers/CustomStatusController.ts index 27865c2163..c7dfad0b3b 100644 --- a/src/settings/controllers/CustomStatusController.ts +++ b/src/settings/controllers/CustomStatusController.ts @@ -1,6 +1,5 @@ /* -Copyright 2019 New Vector Ltd -Copyright 2020 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. From 985073c24946e8bffec69be241814d2319830559 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:24:08 -0600 Subject: [PATCH 057/136] Fix copyright in Settings --- src/settings/Settings.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index f869c6c83e..51f450d8f8 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -1,7 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2018, 2019 New Vector Ltd. -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2018, 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. From 7584a296e4d55db71e061d4c7b788c738818cff0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:24:32 -0600 Subject: [PATCH 058/136] Use SettingController type in Settings --- src/settings/Settings.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 51f450d8f8..289a67cb57 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -32,6 +32,7 @@ import FontSizeController from './controllers/FontSizeController'; import SystemFontController from './controllers/SystemFontController'; import UseSystemFontController from './controllers/UseSystemFontController'; import { SettingLevel } from "./SettingLevel"; +import SettingController from "./controllers/SettingController"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -91,7 +92,7 @@ interface ISetting { default: any; // Optional settings controller. See SettingsController for more information. - controller?: any; // TODO: [TS] Type + controller?: SettingController; // Optional flag to make supportedLevels be respected as the order to handle // settings. The first element is treated as "most preferred". The "default" From c96def81ae3338de0ead747650ef3f19bf822565 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:25:57 -0600 Subject: [PATCH 059/136] Convert SettingsHandler to TS --- .../{SettingsHandler.js => SettingsHandler.ts} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename src/settings/handlers/{SettingsHandler.js => SettingsHandler.ts} (89%) diff --git a/src/settings/handlers/SettingsHandler.js b/src/settings/handlers/SettingsHandler.ts similarity index 89% rename from src/settings/handlers/SettingsHandler.js rename to src/settings/handlers/SettingsHandler.ts index 7d987fc136..3be0fa4636 100644 --- a/src/settings/handlers/SettingsHandler.js +++ b/src/settings/handlers/SettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -28,7 +28,7 @@ export default class SettingsHandler { * @param {String} roomId The room ID to read from, may be null. * @returns {*} The setting value, or null if not found. */ - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { console.error("Invalid operation: getValue was not overridden"); return null; } @@ -44,7 +44,7 @@ export default class SettingsHandler { * @param {*} newValue The new value for the setting, may be null. * @returns {Promise} Resolves when the setting has been saved. */ - setValue(settingName, roomId, newValue) { + public setValue(settingName: string, roomId: string, newValue): Promise { console.error("Invalid operation: setValue was not overridden"); return Promise.reject(); } @@ -56,7 +56,7 @@ export default class SettingsHandler { * @param {String} roomId The room ID to check in, may be null * @returns {boolean} True if the setting can be set by the user, false otherwise. */ - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { return false; } @@ -64,7 +64,7 @@ export default class SettingsHandler { * Determines if this level is supported on this device. * @returns {boolean} True if this level is supported on the current device. */ - isSupported() { + public isSupported(): boolean { return false; } } From fbea8c61bbabe14799842fc8fb214d2977643cc6 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Tue, 28 Jul 2020 17:31:27 -0400 Subject: [PATCH 060/136] add logging for keytar/pickle key and remember when we had key, so that we know that we should expect one --- src/Lifecycle.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index a05392c3e9..2bebe22f14 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -306,6 +306,11 @@ async function _restoreFromLocalStorage(opts) { } const pickleKey = await PlatformPeg.get().getPickleKey(userId, deviceId); + if (pickleKey) { + console.log("Got pickle key"); + } else { + console.log("No pickle key available"); + } console.log(`Restoring session for ${userId}`); await _doSetLoggedIn({ @@ -364,6 +369,12 @@ export async function setLoggedIn(credentials) { ? await PlatformPeg.get().createPickleKey(credentials.userId, credentials.deviceId) : null; + if (pickleKey) { + console.log("Created pickle key"); + } else { + console.log("Pickle key not created"); + } + return _doSetLoggedIn(Object.assign({}, credentials, {pickleKey}), true); } @@ -501,6 +512,14 @@ function _persistCredentialsToLocalStorage(credentials) { localStorage.setItem("mx_access_token", credentials.accessToken); localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest)); + if (credentials.pickleKey) { + localStorage.setItem("mx_has_pickle_key", true); + } else { + if (localStorage.getItem("mx_has_pickle_key")) { + console.error("Expected a pickle key, but none provided. Encryption may not work."); + } + } + // if we didn't get a deviceId from the login, leave mx_device_id unset, // rather than setting it to "undefined". // From d627baf508517304037c3379ea4a6767fff773fb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 15:40:24 -0600 Subject: [PATCH 061/136] Convert WatchManager to TS --- .../{WatchManager.js => WatchManager.ts} | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) rename src/settings/{WatchManager.js => WatchManager.ts} (56%) diff --git a/src/settings/WatchManager.js b/src/settings/WatchManager.ts similarity index 56% rename from src/settings/WatchManager.js rename to src/settings/WatchManager.ts index 3f54ca929e..560f5956b7 100644 --- a/src/settings/WatchManager.js +++ b/src/settings/WatchManager.ts @@ -14,41 +14,52 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { SettingLevel } from "./SettingLevel"; + +export type CallbackFn = (changedInRoomId: string, atLevel: SettingLevel, newValAtLevel: any) => void; + +const IRRELEVANT_ROOM: symbol = Symbol("any room"); + +interface RoomWatcherMap { + // @ts-ignore - TS wants string-only keys but we know better - https://github.com/Microsoft/TypeScript/issues/1863 + [roomId: string | symbol]: CallbackFn[]; +} + /** * Generalized management class for dealing with watchers on a per-handler (per-level) * basis without duplicating code. Handlers are expected to push updates through this * class, which are then proxied outwards to any applicable watchers. */ export class WatchManager { - _watchers = {}; // { settingName: { roomId: callbackFns[] } } + private watchers: {[settingName: string]: RoomWatcherMap} = {}; // Proxy for handlers to delegate changes to this manager - watchSetting(settingName, roomId, cb) { - if (!this._watchers[settingName]) this._watchers[settingName] = {}; - if (!this._watchers[settingName][roomId]) this._watchers[settingName][roomId] = []; - this._watchers[settingName][roomId].push(cb); + public watchSetting(settingName: string, roomId: string | null, cb: CallbackFn) { + if (!this.watchers[settingName]) this.watchers[settingName] = {}; + if (!this.watchers[settingName][roomId]) this.watchers[settingName][roomId] = []; + this.watchers[settingName][roomId].push(cb); } // Proxy for handlers to delegate changes to this manager - unwatchSetting(cb) { - for (const settingName of Object.keys(this._watchers)) { - for (const roomId of Object.keys(this._watchers[settingName])) { + public unwatchSetting(cb: CallbackFn) { + for (const settingName of Object.keys(this.watchers)) { + for (const roomId of Object.keys(this.watchers[settingName])) { let idx; - while ((idx = this._watchers[settingName][roomId].indexOf(cb)) !== -1) { - this._watchers[settingName][roomId].splice(idx, 1); + while ((idx = this.watchers[settingName][roomId].indexOf(cb)) !== -1) { + this.watchers[settingName][roomId].splice(idx, 1); } } } } - notifyUpdate(settingName, inRoomId, atLevel, newValueAtLevel) { + public notifyUpdate(settingName: string, inRoomId: string, atLevel: SettingLevel, newValueAtLevel: any) { // Dev note: We could avoid raising changes for ultimately inconsequential changes, but // we also don't have a reliable way to get the old value of a setting. Instead, we'll just // let it fall through regardless and let the receiver dedupe if they want to. - if (!this._watchers[settingName]) return; + if (!this.watchers[settingName]) return; - const roomWatchers = this._watchers[settingName]; + const roomWatchers = this.watchers[settingName]; const callbacks = []; if (inRoomId !== null && roomWatchers[inRoomId]) { @@ -59,8 +70,8 @@ export class WatchManager { // Fire updates to all the individual room watchers too, as they probably // care about the change higher up. callbacks.push(...Object.values(roomWatchers).reduce((r, a) => [...r, ...a], [])); - } else if (roomWatchers[null]) { - callbacks.push(...roomWatchers[null]); + } else if (roomWatchers[IRRELEVANT_ROOM]) { + callbacks.push(...roomWatchers[IRRELEVANT_ROOM]); } for (const callback of callbacks) { From 008c19ea2664c373712ddbc98a722bb2a377b984 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 16:14:49 -0600 Subject: [PATCH 062/136] Remove unused prop from ThemeWatcher --- src/settings/watchers/ThemeWatcher.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/settings/watchers/ThemeWatcher.ts b/src/settings/watchers/ThemeWatcher.ts index 8c9e126497..c297181490 100644 --- a/src/settings/watchers/ThemeWatcher.ts +++ b/src/settings/watchers/ThemeWatcher.ts @@ -24,9 +24,6 @@ import { ActionPayload } from '../../dispatcher/payloads'; import { SettingLevel } from "../SettingLevel"; export default class ThemeWatcher { - // XXX: I think this is unused. - static _instance = null; - private themeWatchRef: string; private systemThemeWatchRef: string; private dispatcherRef: string; From 5f5efa14485290805117788c22d58f439bc80afa Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 16:15:09 -0600 Subject: [PATCH 063/136] Decorate abstract classes as abstract --- src/settings/controllers/SettingController.ts | 2 +- src/settings/handlers/SettingsHandler.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/controllers/SettingController.ts b/src/settings/controllers/SettingController.ts index 35a116e001..db7c10793e 100644 --- a/src/settings/controllers/SettingController.ts +++ b/src/settings/controllers/SettingController.ts @@ -25,7 +25,7 @@ import { SettingLevel } from "../SettingLevel"; * This is not intended to replace the functionality of a SettingsHandler, it is only * intended to handle environmental factors for specific settings. */ -export default class SettingController { +export default abstract class SettingController { /** * Gets the overridden value for the setting, if any. This must return null if the * value is not to be overridden, otherwise it must return the new value. diff --git a/src/settings/handlers/SettingsHandler.ts b/src/settings/handlers/SettingsHandler.ts index 3be0fa4636..af605e7e2a 100644 --- a/src/settings/handlers/SettingsHandler.ts +++ b/src/settings/handlers/SettingsHandler.ts @@ -19,7 +19,7 @@ limitations under the License. * Represents the base class for all level handlers. This class performs no logic * and should be overridden. */ -export default class SettingsHandler { +export default abstract class SettingsHandler { /** * Gets the value for a particular setting at this level for a particular room. * If no room is applicable, the roomId may be null. The roomId may not be From e3364ba7a422d172d8232a353cdcd9c9f36372f4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 16:20:05 -0600 Subject: [PATCH 064/136] Convert MatrixClientBackedSettingsHandler to TS --- ...s => MatrixClientBackedSettingsHandler.ts} | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename src/settings/handlers/{MatrixClientBackedSettingsHandler.js => MatrixClientBackedSettingsHandler.ts} (65%) diff --git a/src/settings/handlers/MatrixClientBackedSettingsHandler.js b/src/settings/handlers/MatrixClientBackedSettingsHandler.ts similarity index 65% rename from src/settings/handlers/MatrixClientBackedSettingsHandler.js rename to src/settings/handlers/MatrixClientBackedSettingsHandler.ts index 63725b4dff..c9c670f5d9 100644 --- a/src/settings/handlers/MatrixClientBackedSettingsHandler.js +++ b/src/settings/handlers/MatrixClientBackedSettingsHandler.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd. +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. @@ -15,6 +15,7 @@ limitations under the License. */ import SettingsHandler from "./SettingsHandler"; +import { MatrixClient } from "matrix-js-sdk/src/client"; // Dev note: This whole class exists in the event someone logs out and back in - we want // to make sure the right MatrixClient is listening for changes. @@ -23,30 +24,30 @@ import SettingsHandler from "./SettingsHandler"; * Represents the base class for settings handlers which need access to a MatrixClient. * This class performs no logic and should be overridden. */ -export default class MatrixClientBackedSettingsHandler extends SettingsHandler { - static _matrixClient; - static _instances = []; +export default abstract class MatrixClientBackedSettingsHandler extends SettingsHandler { + private static _matrixClient: MatrixClient; + private static instances: MatrixClientBackedSettingsHandler[] = []; - static set matrixClient(client) { + public static set matrixClient(client: MatrixClient) { const oldClient = MatrixClientBackedSettingsHandler._matrixClient; MatrixClientBackedSettingsHandler._matrixClient = client; - for (const instance of MatrixClientBackedSettingsHandler._instances) { + for (const instance of MatrixClientBackedSettingsHandler.instances) { instance.initMatrixClient(oldClient, client); } } - constructor() { + protected constructor() { super(); - MatrixClientBackedSettingsHandler._instances.push(this); + MatrixClientBackedSettingsHandler.instances.push(this); } - get client() { - return MatrixClientBackedSettingsHandler._matrixClient; + public get client(): MatrixClient { + return MatrixClientBackedSettingsHandler.matrixClient; } - initMatrixClient() { + protected initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { console.warn("initMatrixClient not overridden"); } } From 7de727915d9469859135ebbbe55025ab70652db1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 16:31:13 -0600 Subject: [PATCH 065/136] Convert AccountSettingsHandler to TS --- src/settings/WatchManager.ts | 2 +- ...gsHandler.js => AccountSettingsHandler.ts} | 69 +++++++++---------- 2 files changed, 35 insertions(+), 36 deletions(-) rename src/settings/handlers/{AccountSettingsHandler.js => AccountSettingsHandler.ts} (72%) diff --git a/src/settings/WatchManager.ts b/src/settings/WatchManager.ts index 560f5956b7..18179c2fd5 100644 --- a/src/settings/WatchManager.ts +++ b/src/settings/WatchManager.ts @@ -52,7 +52,7 @@ export class WatchManager { } } - public notifyUpdate(settingName: string, inRoomId: string, atLevel: SettingLevel, newValueAtLevel: any) { + public notifyUpdate(settingName: string, inRoomId: string | null, atLevel: SettingLevel, newValueAtLevel: any) { // Dev note: We could avoid raising changes for ultimately inconsequential changes, but // we also don't have a reliable way to get the old value of a setting. Instead, we'll just // let it fall through regardless and let the receiver dedupe if they want to. diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.ts similarity index 72% rename from src/settings/handlers/AccountSettingsHandler.js rename to src/settings/handlers/AccountSettingsHandler.ts index 5b980801d4..53180aeba8 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -19,12 +19,14 @@ import {MatrixClientPeg} from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; import {objectClone, objectKeyChanges} from "../../utils/objects"; import {SettingLevel} from "../SettingLevel"; +import { WatchManager } from "../WatchManager"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; const BREADCRUMBS_LEGACY_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms"; const BREADCRUMBS_EVENT_TYPE = "im.vector.setting.breadcrumbs"; const BREADCRUMBS_EVENT_TYPES = [BREADCRUMBS_LEGACY_EVENT_TYPE, BREADCRUMBS_EVENT_TYPE]; const RECENT_EMOJI_EVENT_TYPE = "io.element.recent_emoji"; - const INTEG_PROVISIONING_EVENT_TYPE = "im.vector.setting.integration_provisioning"; /** @@ -32,22 +34,19 @@ const INTEG_PROVISIONING_EVENT_TYPE = "im.vector.setting.integration_provisionin * This handler does not make use of the roomId parameter. */ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHandler { - constructor(watchManager) { + constructor(private watchers: WatchManager) { super(); - - this._watchers = watchManager; - this._onAccountData = this._onAccountData.bind(this); } - initMatrixClient(oldClient, newClient) { + public initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { if (oldClient) { - oldClient.removeListener("accountData", this._onAccountData); + oldClient.removeListener("accountData", this.onAccountData); } - newClient.on("accountData", this._onAccountData); + newClient.on("accountData", this.onAccountData); } - _onAccountData(event, prevEvent) { + private onAccountData = (event: MatrixEvent, prevEvent: MatrixEvent) => { if (event.getType() === "org.matrix.preview_urls") { let val = event.getContent()['disable']; if (typeof(val) !== "boolean") { @@ -56,30 +55,30 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa val = !val; } - this._watchers.notifyUpdate("urlPreviewsEnabled", null, SettingLevel.ACCOUNT, val); + this.watchers.notifyUpdate("urlPreviewsEnabled", null, SettingLevel.ACCOUNT, val); } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; const changedSettings = objectKeyChanges(prevContent, event.getContent()); for (const settingName of changedSettings) { const val = event.getContent()[settingName]; - this._watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val); + this.watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val); } } else if (BREADCRUMBS_EVENT_TYPES.includes(event.getType())) { - this._notifyBreadcrumbsUpdate(event); + this.notifyBreadcrumbsUpdate(event); } else if (event.getType() === INTEG_PROVISIONING_EVENT_TYPE) { const val = event.getContent()['enabled']; - this._watchers.notifyUpdate("integrationProvisioning", null, SettingLevel.ACCOUNT, val); + this.watchers.notifyUpdate("integrationProvisioning", null, SettingLevel.ACCOUNT, val); } else if (event.getType() === RECENT_EMOJI_EVENT_TYPE) { const val = event.getContent()['enabled']; - this._watchers.notifyUpdate("recent_emoji", null, SettingLevel.ACCOUNT, val); + this.watchers.notifyUpdate("recent_emoji", null, SettingLevel.ACCOUNT, val); } } - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings("org.matrix.preview_urls") || {}; + const content = this.getSettings("org.matrix.preview_urls") || {}; // Check to make sure that we actually got a boolean if (typeof(content['disable']) !== "boolean") return null; @@ -88,9 +87,9 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // Special case for breadcrumbs if (settingName === "breadcrumb_rooms") { - let content = this._getSettings(BREADCRUMBS_EVENT_TYPE); + let content = this.getSettings(BREADCRUMBS_EVENT_TYPE); if (!content || !content['recent_rooms']) { - content = this._getSettings(BREADCRUMBS_LEGACY_EVENT_TYPE); + content = this.getSettings(BREADCRUMBS_LEGACY_EVENT_TYPE); // This is a bit of a hack, but it makes things slightly easier if (content) content['recent_rooms'] = content['rooms']; @@ -101,17 +100,17 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // Special case recent emoji if (settingName === "recent_emoji") { - const content = this._getSettings(RECENT_EMOJI_EVENT_TYPE); + const content = this.getSettings(RECENT_EMOJI_EVENT_TYPE); return content ? content["recent_emoji"] : null; } // Special case integration manager provisioning if (settingName === "integrationProvisioning") { - const content = this._getSettings(INTEG_PROVISIONING_EVENT_TYPE); + const content = this.getSettings(INTEG_PROVISIONING_EVENT_TYPE); return content ? content['enabled'] : null; } - const settings = this._getSettings() || {}; + const settings = this.getSettings() || {}; let preferredValue = settings[settingName]; if (preferredValue === null || preferredValue === undefined) { @@ -124,10 +123,10 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return preferredValue; } - setValue(settingName, roomId, newValue) { + public setValue(settingName: string, roomId: string, newValue: any): Promise { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings("org.matrix.preview_urls") || {}; + const content = this.getSettings("org.matrix.preview_urls") || {}; content['disable'] = !newValue; return MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", content); } @@ -135,9 +134,9 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // Special case for breadcrumbs if (settingName === "breadcrumb_rooms") { // We read the value first just to make sure we preserve whatever random keys might be present. - let content = this._getSettings(BREADCRUMBS_EVENT_TYPE); + let content = this.getSettings(BREADCRUMBS_EVENT_TYPE); if (!content || !content['recent_rooms']) { - content = this._getSettings(BREADCRUMBS_LEGACY_EVENT_TYPE); + content = this.getSettings(BREADCRUMBS_LEGACY_EVENT_TYPE); } if (!content) content = {}; // If we still don't have content, make some @@ -147,33 +146,33 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa // Special case recent emoji if (settingName === "recent_emoji") { - const content = this._getSettings(RECENT_EMOJI_EVENT_TYPE) || {}; + const content = this.getSettings(RECENT_EMOJI_EVENT_TYPE) || {}; content["recent_emoji"] = newValue; return MatrixClientPeg.get().setAccountData(RECENT_EMOJI_EVENT_TYPE, content); } // Special case integration manager provisioning if (settingName === "integrationProvisioning") { - const content = this._getSettings(INTEG_PROVISIONING_EVENT_TYPE) || {}; + const content = this.getSettings(INTEG_PROVISIONING_EVENT_TYPE) || {}; content['enabled'] = newValue; return MatrixClientPeg.get().setAccountData(INTEG_PROVISIONING_EVENT_TYPE, content); } - const content = this._getSettings() || {}; + const content = this.getSettings() || {}; content[settingName] = newValue; return MatrixClientPeg.get().setAccountData("im.vector.web.settings", content); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { return true; // It's their account, so they should be able to } - isSupported() { + public isSupported(): boolean { const cli = MatrixClientPeg.get(); return cli !== undefined && cli !== null; } - _getSettings(eventType = "im.vector.web.settings") { + private getSettings(eventType = "im.vector.web.settings"): any { // TODO: [TS] Types on return const cli = MatrixClientPeg.get(); if (!cli) return null; @@ -182,11 +181,11 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return objectClone(event.getContent()); // clone to prevent mutation } - _notifyBreadcrumbsUpdate(event) { + private notifyBreadcrumbsUpdate(event: MatrixEvent) { let val = []; if (event.getType() === BREADCRUMBS_LEGACY_EVENT_TYPE) { // This seems fishy - try and get the event for the new rooms - const newType = this._getSettings(BREADCRUMBS_EVENT_TYPE); + const newType = this.getSettings(BREADCRUMBS_EVENT_TYPE); if (newType) val = newType['recent_rooms']; else val = event.getContent()['rooms']; } else if (event.getType() === BREADCRUMBS_EVENT_TYPE) { @@ -194,6 +193,6 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa } else { return; // for sanity, not because we expect to be here. } - this._watchers.notifyUpdate("breadcrumb_rooms", null, SettingLevel.ACCOUNT, val || []); + this.watchers.notifyUpdate("breadcrumb_rooms", null, SettingLevel.ACCOUNT, val || []); } } From 4969cfe9deef2ebefcbeaff0e9ca6acb2525ad5c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 16:37:09 -0600 Subject: [PATCH 066/136] Appease the linter --- src/CallHandler.js | 2 +- src/components/structures/MatrixChat.tsx | 2 +- src/settings/WatchManager.ts | 2 +- src/settings/controllers/NotificationControllers.ts | 1 + src/settings/watchers/ThemeWatcher.ts | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index 810b2895d8..d5e058ef1e 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -62,7 +62,7 @@ import Matrix from 'matrix-js-sdk'; import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; -import SettingsStore from './settings/SettingsStore'; +import SettingsStore from './settings/SettingsStore'; import {generateHumanReadableId} from "./utils/NamingUtils"; import {Jitsi} from "./widgets/Jitsi"; import {WidgetType} from "./widgets/WidgetType"; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 7fba18f4e5..a66d4c043f 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -51,7 +51,7 @@ import { getHomePageUrl } from '../../utils/pages'; import createRoom from "../../createRoom"; import {_t, _td, getCurrentLanguage} from '../../languageHandler'; -import SettingsStore from "../../settings/SettingsStore"; +import SettingsStore from "../../settings/SettingsStore"; import ThemeController from "../../settings/controllers/ThemeController"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; diff --git a/src/settings/WatchManager.ts b/src/settings/WatchManager.ts index 18179c2fd5..d51439459c 100644 --- a/src/settings/WatchManager.ts +++ b/src/settings/WatchManager.ts @@ -18,7 +18,7 @@ import { SettingLevel } from "./SettingLevel"; export type CallbackFn = (changedInRoomId: string, atLevel: SettingLevel, newValAtLevel: any) => void; -const IRRELEVANT_ROOM: symbol = Symbol("any room"); +const IRRELEVANT_ROOM = Symbol("any room"); interface RoomWatcherMap { // @ts-ignore - TS wants string-only keys but we know better - https://github.com/Microsoft/TypeScript/issues/1863 diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index 23b753fc11..ff38f41b7e 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -39,6 +39,7 @@ function isPushNotifyDisabled(): boolean { } function getNotifier(): any { // TODO: [TS] Formal type that doesn't cause a cyclical reference. + // eslint-disable-next-line @typescript-eslint/no-var-requires let Notifier = require('../../Notifier'); // avoids cyclical references if (Notifier.default) Notifier = Notifier.default; // correct for webpack require() weirdness return Notifier; diff --git a/src/settings/watchers/ThemeWatcher.ts b/src/settings/watchers/ThemeWatcher.ts index c297181490..4330a15f57 100644 --- a/src/settings/watchers/ThemeWatcher.ts +++ b/src/settings/watchers/ThemeWatcher.ts @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore from '../SettingsStore'; +import SettingsStore from '../SettingsStore'; import dis from '../../dispatcher/dispatcher'; import { Action } from '../../dispatcher/actions'; import ThemeController from "../controllers/ThemeController"; From e4d8cca861a186dc180bac4642ec1ac7d647b3f5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:46:45 -0600 Subject: [PATCH 067/136] Specify an error to appease the linter --- src/settings/handlers/SettingsHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/handlers/SettingsHandler.ts b/src/settings/handlers/SettingsHandler.ts index af605e7e2a..53adb9aca6 100644 --- a/src/settings/handlers/SettingsHandler.ts +++ b/src/settings/handlers/SettingsHandler.ts @@ -46,7 +46,7 @@ export default abstract class SettingsHandler { */ public setValue(settingName: string, roomId: string, newValue): Promise { console.error("Invalid operation: setValue was not overridden"); - return Promise.reject(); + return Promise.reject(new Error("Invalid operation: setValue was not overridden")); } /** From 32844d46245beea873177c8a8736756135e2edfa Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:47:57 -0600 Subject: [PATCH 068/136] Convert ConfigSettingsHandler to TS --- ...nfigSettingsHandler.js => ConfigSettingsHandler.ts} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename src/settings/handlers/{ConfigSettingsHandler.js => ConfigSettingsHandler.ts} (81%) diff --git a/src/settings/handlers/ConfigSettingsHandler.js b/src/settings/handlers/ConfigSettingsHandler.ts similarity index 81% rename from src/settings/handlers/ConfigSettingsHandler.js rename to src/settings/handlers/ConfigSettingsHandler.ts index 3b5b4b626e..3e8b1724c1 100644 --- a/src/settings/handlers/ConfigSettingsHandler.js +++ b/src/settings/handlers/ConfigSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd +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. @@ -24,7 +24,7 @@ import {isNullOrUndefined} from "matrix-js-sdk/src/utils"; * roomId parameter. */ export default class ConfigSettingsHandler extends SettingsHandler { - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { const config = SdkConfig.get() || {}; // Special case themes @@ -37,15 +37,15 @@ export default class ConfigSettingsHandler extends SettingsHandler { return settingsConfig[settingName]; } - setValue(settingName, roomId, newValue) { + public async setValue(settingName: string, roomId: string, newValue: any): Promise { throw new Error("Cannot change settings at the config level"); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { return false; } - isSupported() { + public isSupported(): boolean { return true; // SdkConfig is always there } } From 8097810784a4e8ffe83a6c78aa92976818850bb5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:49:55 -0600 Subject: [PATCH 069/136] Convert DefaultSettingsHandler to TS --- ...ngsHandler.js => DefaultSettingsHandler.ts} | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) rename src/settings/handlers/{DefaultSettingsHandler.js => DefaultSettingsHandler.ts} (72%) diff --git a/src/settings/handlers/DefaultSettingsHandler.js b/src/settings/handlers/DefaultSettingsHandler.ts similarity index 72% rename from src/settings/handlers/DefaultSettingsHandler.js rename to src/settings/handlers/DefaultSettingsHandler.ts index 37bc5b2348..fc1c92c159 100644 --- a/src/settings/handlers/DefaultSettingsHandler.js +++ b/src/settings/handlers/DefaultSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -27,29 +27,27 @@ export default class DefaultSettingsHandler extends SettingsHandler { * @param {object} defaults The default setting values, keyed by setting name. * @param {object} invertedDefaults The default inverted setting values, keyed by setting name. */ - constructor(defaults, invertedDefaults) { + constructor(private defaults: any, private invertedDefaults: any) { // TODO: [TS] Appropriate types super(); - this._defaults = defaults; - this._invertedDefaults = invertedDefaults; } - getValue(settingName, roomId) { - let value = this._defaults[settingName]; + public getValue(settingName: string, roomId: string): any { + let value = this.defaults[settingName]; if (value === undefined) { - value = this._invertedDefaults[settingName]; + value = this.invertedDefaults[settingName]; } return value; } - setValue(settingName, roomId, newValue) { + public async setValue(settingName: string, roomId: string, newValue: any): Promise { throw new Error("Cannot set values on the default level handler"); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string) { return false; } - isSupported() { + public isSupported(): boolean { return true; } } From 4f6480c037e01fde19713b7df3b43bad50c31eee Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:53:07 -0600 Subject: [PATCH 070/136] Convert DeviceSettingsHandler to TS --- ...ngsHandler.js => DeviceSettingsHandler.ts} | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) rename src/settings/handlers/{DeviceSettingsHandler.js => DeviceSettingsHandler.ts} (72%) diff --git a/src/settings/handlers/DeviceSettingsHandler.js b/src/settings/handlers/DeviceSettingsHandler.ts similarity index 72% rename from src/settings/handlers/DeviceSettingsHandler.js rename to src/settings/handlers/DeviceSettingsHandler.ts index 47c9bbd5b9..2096203598 100644 --- a/src/settings/handlers/DeviceSettingsHandler.js +++ b/src/settings/handlers/DeviceSettingsHandler.ts @@ -19,6 +19,7 @@ limitations under the License. import SettingsHandler from "./SettingsHandler"; import {MatrixClientPeg} from "../../MatrixClientPeg"; import {SettingLevel} from "../SettingLevel"; +import { CallbackFn, WatchManager } from "../WatchManager"; /** * Gets and sets settings at the "device" level for the current device. @@ -29,17 +30,15 @@ export default class DeviceSettingsHandler extends SettingsHandler { /** * Creates a new device settings handler * @param {string[]} featureNames The names of known features. - * @param {WatchManager} watchManager The watch manager to notify updates to + * @param {WatchManager} watchers The watch manager to notify updates to */ - constructor(featureNames, watchManager) { + constructor(private featureNames: string[], private watchers: WatchManager) { super(); - this._featureNames = featureNames; - this._watchers = watchManager; } - getValue(settingName, roomId) { - if (this._featureNames.includes(settingName)) { - return this._readFeature(settingName); + public getValue(settingName: string, roomId: string): any { + if (this.featureNames.includes(settingName)) { + return this.readFeature(settingName); } // Special case notifications @@ -68,28 +67,28 @@ export default class DeviceSettingsHandler extends SettingsHandler { return val['value']; } - const settings = this._getSettings() || {}; + const settings = this.getSettings() || {}; return settings[settingName]; } - setValue(settingName, roomId, newValue) { - if (this._featureNames.includes(settingName)) { - this._writeFeature(settingName, newValue); + public setValue(settingName: string, roomId: string, newValue: any): Promise { + if (this.featureNames.includes(settingName)) { + this.writeFeature(settingName, newValue); return Promise.resolve(); } // Special case notifications if (settingName === "notificationsEnabled") { localStorage.setItem("notifications_enabled", newValue); - this._watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); return Promise.resolve(); } else if (settingName === "notificationBodyEnabled") { localStorage.setItem("notifications_body_enabled", newValue); - this._watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); return Promise.resolve(); } else if (settingName === "audioNotificationsEnabled") { localStorage.setItem("audio_notifications_enabled", newValue); - this._watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); return Promise.resolve(); } @@ -103,35 +102,35 @@ export default class DeviceSettingsHandler extends SettingsHandler { "lastRightPanelPhaseForGroup", ].includes(settingName)) { localStorage.setItem(`mx_${settingName}`, JSON.stringify({value: newValue})); - this._watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); return Promise.resolve(); } - const settings = this._getSettings() || {}; + const settings = this.getSettings() || {}; settings[settingName] = newValue; localStorage.setItem("mx_local_settings", JSON.stringify(settings)); - this._watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); + this.watchers.notifyUpdate(settingName, null, SettingLevel.DEVICE, newValue); return Promise.resolve(); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { return true; // It's their device, so they should be able to } - isSupported() { + public isSupported(): boolean { return localStorage !== undefined && localStorage !== null; } - watchSetting(settingName, roomId, cb) { - this._watchers.watchSetting(settingName, roomId, cb); + public watchSetting(settingName: string, roomId: string, cb: CallbackFn) { + this.watchers.watchSetting(settingName, roomId, cb); } - unwatchSetting(cb) { - this._watchers.unwatchSetting(cb); + public unwatchSetting(cb: CallbackFn) { + this.watchers.unwatchSetting(cb); } - _getSettings() { + private getSettings(): any { // TODO: [TS] Type return const value = localStorage.getItem("mx_local_settings"); if (!value) return null; return JSON.parse(value); @@ -140,7 +139,7 @@ export default class DeviceSettingsHandler extends SettingsHandler { // Note: features intentionally don't use the same key as settings to avoid conflicts // and to be backwards compatible. - _readFeature(featureName) { + private readFeature(featureName: string): boolean | null { if (MatrixClientPeg.get() && MatrixClientPeg.get().isGuest()) { // Guests should not have any labs features enabled. return false; @@ -153,8 +152,8 @@ export default class DeviceSettingsHandler extends SettingsHandler { return null; } - _writeFeature(featureName, enabled) { - localStorage.setItem("mx_labs_feature_" + featureName, enabled); - this._watchers.notifyUpdate(featureName, null, SettingLevel.DEVICE, enabled); + private writeFeature(featureName: string, enabled: boolean | null) { + localStorage.setItem("mx_labs_feature_" + featureName, `${enabled}`); + this.watchers.notifyUpdate(featureName, null, SettingLevel.DEVICE, enabled); } } From 21e5bccb62d5d7b77edc4e9dc574ad028b52c9a9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:57:00 -0600 Subject: [PATCH 071/136] Convert LocalEchoWrapper to TS --- ...ocalEchoWrapper.js => LocalEchoWrapper.ts} | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) rename src/settings/handlers/{LocalEchoWrapper.js => LocalEchoWrapper.ts} (63%) diff --git a/src/settings/handlers/LocalEchoWrapper.js b/src/settings/handlers/LocalEchoWrapper.ts similarity index 63% rename from src/settings/handlers/LocalEchoWrapper.js rename to src/settings/handlers/LocalEchoWrapper.ts index fd0510296a..d940413b9b 100644 --- a/src/settings/handlers/LocalEchoWrapper.js +++ b/src/settings/handlers/LocalEchoWrapper.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -23,47 +23,48 @@ import SettingsHandler from "./SettingsHandler"; * handler as much as possible to ensure values are not stale. */ export default class LocalEchoWrapper extends SettingsHandler { + private cache: { + [settingName: string]: { + [roomId: string]: any; + }; + } = {}; + /** * Creates a new local echo wrapper * @param {SettingsHandler} handler The handler to wrap */ - constructor(handler) { + constructor(private handler: SettingsHandler) { super(); - this._handler = handler; - this._cache = { - // settingName: { roomId: value } - }; } - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { const cacheRoomId = roomId ? roomId : "UNDEFINED"; // avoid weird keys - const bySetting = this._cache[settingName]; + const bySetting = this.cache[settingName]; if (bySetting && bySetting.hasOwnProperty(cacheRoomId)) { return bySetting[cacheRoomId]; } - return this._handler.getValue(settingName, roomId); + return this.handler.getValue(settingName, roomId); } - setValue(settingName, roomId, newValue) { - if (!this._cache[settingName]) this._cache[settingName] = {}; - const bySetting = this._cache[settingName]; + public setValue(settingName: string, roomId: string, newValue: any): Promise { + if (!this.cache[settingName]) this.cache[settingName] = {}; + const bySetting = this.cache[settingName]; const cacheRoomId = roomId ? roomId : "UNDEFINED"; // avoid weird keys bySetting[cacheRoomId] = newValue; - const handlerPromise = this._handler.setValue(settingName, roomId, newValue); + const handlerPromise = this.handler.setValue(settingName, roomId, newValue); return Promise.resolve(handlerPromise).finally(() => { delete bySetting[cacheRoomId]; }); } - - canSetValue(settingName, roomId) { - return this._handler.canSetValue(settingName, roomId); + public canSetValue(settingName, roomId): boolean { + return this.handler.canSetValue(settingName, roomId); } - isSupported() { - return this._handler.isSupported(); + public isSupported(): boolean { + return this.handler.isSupported(); } } From c4f4e2aa0a67b65a9ce5d8b09462bd3943f9a6c0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 21:59:47 -0600 Subject: [PATCH 072/136] Convert RoomAccountSettingsHandler to TS --- ...ndler.js => RoomAccountSettingsHandler.ts} | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) rename src/settings/handlers/{RoomAccountSettingsHandler.js => RoomAccountSettingsHandler.ts} (65%) diff --git a/src/settings/handlers/RoomAccountSettingsHandler.js b/src/settings/handlers/RoomAccountSettingsHandler.ts similarity index 65% rename from src/settings/handlers/RoomAccountSettingsHandler.js rename to src/settings/handlers/RoomAccountSettingsHandler.ts index 86fd5cb61f..e3449e76c3 100644 --- a/src/settings/handlers/RoomAccountSettingsHandler.js +++ b/src/settings/handlers/RoomAccountSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -15,10 +15,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClientPeg} from '../../MatrixClientPeg'; +import { MatrixClientPeg } from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; -import {objectClone, objectKeyChanges} from "../../utils/objects"; -import {SettingLevel} from "../SettingLevel"; +import { objectClone, objectKeyChanges } from "../../utils/objects"; +import { SettingLevel } from "../SettingLevel"; +import { WatchManager } from "../WatchManager"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { Room } from "matrix-js-sdk/src/models/room"; const ALLOWED_WIDGETS_EVENT_TYPE = "im.vector.setting.allowed_widgets"; @@ -26,22 +30,19 @@ const ALLOWED_WIDGETS_EVENT_TYPE = "im.vector.setting.allowed_widgets"; * Gets and sets settings at the "room-account" level for the current user. */ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettingsHandler { - constructor(watchManager) { + constructor(private watchers: WatchManager) { super(); - - this._watchers = watchManager; - this._onAccountData = this._onAccountData.bind(this); } - initMatrixClient(oldClient, newClient) { + protected initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { if (oldClient) { - oldClient.removeListener("Room.accountData", this._onAccountData); + oldClient.removeListener("Room.accountData", this.onAccountData); } - newClient.on("Room.accountData", this._onAccountData); + newClient.on("Room.accountData", this.onAccountData); } - _onAccountData(event, room, prevEvent) { + private onAccountData = (event: MatrixEvent, room: Room, prevEvent: MatrixEvent) => { const roomId = room.roomId; if (event.getType() === "org.matrix.room.preview_urls") { @@ -52,29 +53,29 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin val = !val; } - this._watchers.notifyUpdate("urlPreviewsEnabled", roomId, SettingLevel.ROOM_ACCOUNT, val); + this.watchers.notifyUpdate("urlPreviewsEnabled", roomId, SettingLevel.ROOM_ACCOUNT, val); } else if (event.getType() === "org.matrix.room.color_scheme") { - this._watchers.notifyUpdate("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, event.getContent()); + this.watchers.notifyUpdate("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, event.getContent()); } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; const changedSettings = objectKeyChanges(prevContent, event.getContent()); for (const settingName of changedSettings) { const val = event.getContent()[settingName]; - this._watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_ACCOUNT, val); + this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_ACCOUNT, val); } } else if (event.getType() === ALLOWED_WIDGETS_EVENT_TYPE) { - this._watchers.notifyUpdate("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, event.getContent()); + this.watchers.notifyUpdate("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, event.getContent()); } - } + }; - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings(roomId, "org.matrix.room.preview_urls") || {}; + const content = this.getSettings(roomId, "org.matrix.room.preview_urls") || {}; // Check to make sure that we actually got a boolean - if (typeof(content['disable']) !== "boolean") return null; + if (typeof (content['disable']) !== "boolean") return null; return !content['disable']; } @@ -83,22 +84,22 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin // The event content should already be in an appropriate format, we just need // to get the right value. // don't fallback to {} because thats truthy and would imply there is an event specifying tint - return this._getSettings(roomId, "org.matrix.room.color_scheme"); + return this.getSettings(roomId, "org.matrix.room.color_scheme"); } // Special case allowed widgets if (settingName === "allowedWidgets") { - return this._getSettings(roomId, ALLOWED_WIDGETS_EVENT_TYPE); + return this.getSettings(roomId, ALLOWED_WIDGETS_EVENT_TYPE); } - const settings = this._getSettings(roomId) || {}; + const settings = this.getSettings(roomId) || {}; return settings[settingName]; } - setValue(settingName, roomId, newValue) { + public setValue(settingName: string, roomId: string, newValue: any): Promise { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings(roomId, "org.matrix.room.preview_urls") || {}; + const content = this.getSettings(roomId, "org.matrix.room.preview_urls") || {}; content['disable'] = !newValue; return MatrixClientPeg.get().setRoomAccountData(roomId, "org.matrix.room.preview_urls", content); } @@ -114,24 +115,24 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin return MatrixClientPeg.get().setRoomAccountData(roomId, ALLOWED_WIDGETS_EVENT_TYPE, newValue); } - const content = this._getSettings(roomId) || {}; + const content = this.getSettings(roomId) || {}; content[settingName] = newValue; return MatrixClientPeg.get().setRoomAccountData(roomId, "im.vector.web.settings", content); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { const room = MatrixClientPeg.get().getRoom(roomId); // If they have the room, they can set their own account data return room !== undefined && room !== null; } - isSupported() { + public isSupported(): boolean { const cli = MatrixClientPeg.get(); return cli !== undefined && cli !== null; } - _getSettings(roomId, eventType = "im.vector.web.settings") { + private getSettings(roomId: string, eventType = "im.vector.web.settings"): any { // TODO: [TS] Type return const room = MatrixClientPeg.get().getRoom(roomId); if (!room) return null; From c7b28b456628e069fd0f615af39b1652a609b9df Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 22:14:33 -0600 Subject: [PATCH 073/136] Convert RoomDeviceSettingsHandler to TS --- ...andler.js => RoomDeviceSettingsHandler.ts} | 35 +++++++++---------- src/settings/handlers/SettingsHandler.ts | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) rename src/settings/handlers/{RoomDeviceSettingsHandler.js => RoomDeviceSettingsHandler.ts} (66%) diff --git a/src/settings/handlers/RoomDeviceSettingsHandler.js b/src/settings/handlers/RoomDeviceSettingsHandler.ts similarity index 66% rename from src/settings/handlers/RoomDeviceSettingsHandler.js rename to src/settings/handlers/RoomDeviceSettingsHandler.ts index cd1f9e9265..2fcd58c27c 100644 --- a/src/settings/handlers/RoomDeviceSettingsHandler.js +++ b/src/settings/handlers/RoomDeviceSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -16,71 +16,70 @@ limitations under the License. */ import SettingsHandler from "./SettingsHandler"; -import {SettingLevel} from "../SettingLevel"; +import { SettingLevel } from "../SettingLevel"; +import { WatchManager } from "../WatchManager"; /** * Gets and sets settings at the "room-device" level for the current device in a particular * room. */ export default class RoomDeviceSettingsHandler extends SettingsHandler { - constructor(watchManager) { + constructor(private watchers: WatchManager) { super(); - - this._watchers = watchManager; } - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { // Special case blacklist setting to use legacy values if (settingName === "blacklistUnverifiedDevices") { - const value = this._read("mx_local_settings"); + const value = this.read("mx_local_settings"); if (value && value['blacklistUnverifiedDevicesPerRoom']) { return value['blacklistUnverifiedDevicesPerRoom'][roomId]; } } - const value = this._read(this._getKey(settingName, roomId)); + const value = this.read(this.getKey(settingName, roomId)); if (value) return value.value; return null; } - setValue(settingName, roomId, newValue) { + public setValue(settingName: string, roomId: string, newValue: any): Promise { // Special case blacklist setting for legacy structure if (settingName === "blacklistUnverifiedDevices") { - let value = this._read("mx_local_settings"); + let value = this.read("mx_local_settings"); if (!value) value = {}; if (!value["blacklistUnverifiedDevicesPerRoom"]) value["blacklistUnverifiedDevicesPerRoom"] = {}; value["blacklistUnverifiedDevicesPerRoom"][roomId] = newValue; localStorage.setItem("mx_local_settings", JSON.stringify(value)); - this._watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue); + this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue); return Promise.resolve(); } if (newValue === null) { - localStorage.removeItem(this._getKey(settingName, roomId)); + localStorage.removeItem(this.getKey(settingName, roomId)); } else { newValue = JSON.stringify({value: newValue}); - localStorage.setItem(this._getKey(settingName, roomId), newValue); + localStorage.setItem(this.getKey(settingName, roomId), newValue); } - this._watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue); + this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue); return Promise.resolve(); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { return true; // It's their device, so they should be able to } - isSupported() { + public isSupported(): boolean { return localStorage !== undefined && localStorage !== null; } - _read(key) { + private read(key: string): any { const rawValue = localStorage.getItem(key); if (!rawValue) return null; return JSON.parse(rawValue); } - _getKey(settingName, roomId) { + private getKey(settingName: string, roomId: string): string { return "mx_setting_" + settingName + "_" + roomId; } } diff --git a/src/settings/handlers/SettingsHandler.ts b/src/settings/handlers/SettingsHandler.ts index 53adb9aca6..4b1c3459da 100644 --- a/src/settings/handlers/SettingsHandler.ts +++ b/src/settings/handlers/SettingsHandler.ts @@ -44,7 +44,7 @@ export default abstract class SettingsHandler { * @param {*} newValue The new value for the setting, may be null. * @returns {Promise} Resolves when the setting has been saved. */ - public setValue(settingName: string, roomId: string, newValue): Promise { + public setValue(settingName: string, roomId: string, newValue: any): Promise { console.error("Invalid operation: setValue was not overridden"); return Promise.reject(new Error("Invalid operation: setValue was not overridden")); } From 51968a80726cea02d79d9f882501ab1023e4ed3e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 28 Jul 2020 22:16:54 -0600 Subject: [PATCH 074/136] Convert RoomSettingsHandler to TS --- ...tingsHandler.js => RoomSettingsHandler.ts} | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) rename src/settings/handlers/{RoomSettingsHandler.js => RoomSettingsHandler.ts} (69%) diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.ts similarity index 69% rename from src/settings/handlers/RoomSettingsHandler.js rename to src/settings/handlers/RoomSettingsHandler.ts index 321f329ea0..cc9496c82b 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -15,23 +15,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {MatrixClientPeg} from '../../MatrixClientPeg'; +import { MatrixClientPeg } from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; -import {objectClone, objectKeyChanges} from "../../utils/objects"; -import {SettingLevel} from "../SettingLevel"; +import { objectClone, objectKeyChanges } from "../../utils/objects"; +import { SettingLevel } from "../SettingLevel"; +import { WatchManager } from "../WatchManager"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { RoomState } from "matrix-js-sdk/src/models/room-state"; /** * Gets and sets settings at the "room" level. */ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandler { - constructor(watchManager) { + constructor(private watchers: WatchManager) { super(); - - this._watchers = watchManager; - this._onEvent = this._onEvent.bind(this); } - initMatrixClient(oldClient, newClient) { + protected initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { if (oldClient) { oldClient.removeListener("RoomState.events", this._onEvent); } @@ -39,7 +40,7 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl newClient.on("RoomState.events", this._onEvent); } - _onEvent(event, state, prevEvent) { + private _onEvent = (event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent) => { const roomId = event.getRoomId(); const room = this.client.getRoom(roomId); @@ -60,45 +61,45 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl val = !val; } - this._watchers.notifyUpdate("urlPreviewsEnabled", roomId, SettingLevel.ROOM, val); + this.watchers.notifyUpdate("urlPreviewsEnabled", roomId, SettingLevel.ROOM, val); } else if (event.getType() === "im.vector.web.settings") { // Figure out what changed and fire those updates const prevContent = prevEvent ? prevEvent.getContent() : {}; const changedSettings = objectKeyChanges(prevContent, event.getContent()); for (const settingName of changedSettings) { - this._watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM, event.getContent()[settingName]); + this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM, event.getContent()[settingName]); } } - } + }; - getValue(settingName, roomId) { + public getValue(settingName: string, roomId: string): any { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings(roomId, "org.matrix.room.preview_urls") || {}; + const content = this.getSettings(roomId, "org.matrix.room.preview_urls") || {}; // Check to make sure that we actually got a boolean - if (typeof(content['disable']) !== "boolean") return null; + if (typeof (content['disable']) !== "boolean") return null; return !content['disable']; } - const settings = this._getSettings(roomId) || {}; + const settings = this.getSettings(roomId) || {}; return settings[settingName]; } - setValue(settingName, roomId, newValue) { + public setValue(settingName: string, roomId: string, newValue: any): Promise { // Special case URL previews if (settingName === "urlPreviewsEnabled") { - const content = this._getSettings(roomId, "org.matrix.room.preview_urls") || {}; + const content = this.getSettings(roomId, "org.matrix.room.preview_urls") || {}; content['disable'] = !newValue; return MatrixClientPeg.get().sendStateEvent(roomId, "org.matrix.room.preview_urls", content); } - const content = this._getSettings(roomId) || {}; + const content = this.getSettings(roomId) || {}; content[settingName] = newValue; return MatrixClientPeg.get().sendStateEvent(roomId, "im.vector.web.settings", content, ""); } - canSetValue(settingName, roomId) { + public canSetValue(settingName: string, roomId: string): boolean { const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); @@ -109,12 +110,12 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl return room.currentState.maySendStateEvent(eventType, cli.getUserId()); } - isSupported() { + public isSupported(): boolean { const cli = MatrixClientPeg.get(); return cli !== undefined && cli !== null; } - _getSettings(roomId, eventType = "im.vector.web.settings") { + private getSettings(roomId: string, eventType = "im.vector.web.settings"): any { const room = MatrixClientPeg.get().getRoom(roomId); if (!room) return null; From 50bc7fc62f300a9aac92da91a0084fe35e196193 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 29 Jul 2020 12:00:42 +0100 Subject: [PATCH 075/136] Remove redundant lint dependencies These are no longer needed with the new standard lint repo. --- .eslintrc.js | 16 ++-------------- package.json | 2 -- yarn.lock | 30 ------------------------------ 3 files changed, 2 insertions(+), 46 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 069a67e511..fc82e75ce2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,15 +1,3 @@ -const path = require('path'); - -// get the path of the js-sdk so we can extend the config -// eslint supports loading extended configs by module, -// but only if they come from a module that starts with eslint-config- -// So we load the filename directly (and it could be in node_modules/ -// or or ../node_modules/ etc) -// -// We add a `..` to the end because the js-sdk lives out of lib/, but the eslint -// config is at the project root. -const matrixJsSdkPath = path.join(path.dirname(require.resolve('matrix-js-sdk')), '..'); - module.exports = { extends: ["matrix-org", "matrix-org/react-legacy"], parser: "babel-eslint", @@ -31,7 +19,7 @@ module.exports = { }, overrides: [{ - files: ["src/**/*.{ts, tsx}"], + "files": ["src/**/*.{ts, tsx}"], "extends": ["matrix-org/ts"], "rules": { // We disable this while we're transitioning @@ -41,6 +29,6 @@ module.exports = { "quotes": "off", "no-extra-boolean-cast": "off", - } + }, }], }; diff --git a/package.json b/package.json index 02ef89be39..0808f5aecd 100644 --- a/package.json +++ b/package.json @@ -140,11 +140,9 @@ "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "eslint": "7.5.0", - "eslint-config-google": "^0.14.0", "eslint-config-matrix-org": "^0.1.2", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-flowtype": "^2.50.3", - "eslint-plugin-jest": "^23.18.0", "eslint-plugin-react": "^7.20.3", "eslint-plugin-react-hooks": "^2.5.1", "estree-walker": "^0.9.0", diff --git a/yarn.lock b/yarn.lock index 7ab5e24145..ffea2bbbe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1722,16 +1722,6 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/experimental-utils@^2.5.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - "@typescript-eslint/parser@^3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.4.0.tgz#fe52b68c5cb3bba3f5d875bd17adb70420d49d8d" @@ -1758,19 +1748,6 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.7.0.tgz#09897fab0cb95479c01166b10b2c03c224821077" integrity sha512-reCaK+hyKkKF+itoylAnLzFeNYAEktB0XVfSQvf0gcVgpz1l49Lt6Vo9x4MVCCxiDydA0iLAjTF/ODH0pbfnpg== -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - "@typescript-eslint/typescript-estree@3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.4.0.tgz#6a787eb70b48969e4cd1ea67b057083f96dfee29" @@ -3983,13 +3960,6 @@ eslint-plugin-import@^2.14.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^23.18.0: - version "23.18.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.18.0.tgz#4813eacb181820ed13c5505f400956d176b25af8" - integrity sha512-wLPM/Rm1SGhxrFQ2TKM/BYsYPhn7ch6ZEK92S2o/vGkAAnDXM0I4nTIo745RIX+VlCRMFgBuJEax6XfTHMdeKg== - dependencies: - "@typescript-eslint/experimental-utils" "^2.5.0" - eslint-plugin-react-hooks@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz#4ef5930592588ce171abeb26f400c7fbcbc23cd0" From bf450ad075aeb4cebc84f73aca6afda699a260cd Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 29 Jul 2020 16:59:29 +0530 Subject: [PATCH 076/136] Fix the type for SetRightPanelPhasePayload Fix uses of it as well --- src/components/views/right_panel/GroupHeaderButtons.tsx | 4 ++-- src/components/views/right_panel/RoomHeaderButtons.tsx | 2 +- src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/right_panel/GroupHeaderButtons.tsx b/src/components/views/right_panel/GroupHeaderButtons.tsx index 7bd6d90038..b15a93e5a0 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.tsx +++ b/src/components/views/right_panel/GroupHeaderButtons.tsx @@ -50,7 +50,7 @@ export default class GroupHeaderButtons extends HeaderButtons { if (payload.action === Action.ViewUser) { if ((payload as ViewUserPayload).member) { - this.setPhase(RightPanelPhases.RoomMemberInfo, {members: payload.member}); + this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); } else { this.setPhase(RightPanelPhases.GroupMemberList); } @@ -66,7 +66,7 @@ export default class GroupHeaderButtons extends HeaderButtons { } else if (payload.action === "view_group_member_list") { this.setPhase(RightPanelPhases.GroupMemberList); } else if (payload.action === "view_group_user") { - this.setPhase(RightPanelPhases.GroupMemberInfo, {members: payload.member}); + this.setPhase(RightPanelPhases.GroupMemberInfo, {member: payload.member}); } } diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 74bd2c51fc..8620d5b485 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -45,7 +45,7 @@ export default class RoomHeaderButtons extends HeaderButtons { super.onAction(payload); if (payload.action === Action.ViewUser) { if (payload.member) { - this.setPhase(RightPanelPhases.RoomMemberInfo, {members: payload.member}); + this.setPhase(RightPanelPhases.RoomMemberInfo, {member: payload.member}); } else { this.setPhase(RightPanelPhases.RoomMemberList); } diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 49e3032d4f..5b78863b35 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -30,7 +30,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload { export interface SetRightPanelPhaseRefireParams { // XXX: Fix after the types are defiend in matrix-js-sdk // No appropriate types exist yet for the fields - members?: RoomMember; + member?: RoomMember; verificationRequest?: typeof VerificationRequest; groupId?: string; groupRoomId?: string; From aa160095fa0413dfa1e43b9dcfaf5f54f1ed31ad Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 29 Jul 2020 17:26:51 +0530 Subject: [PATCH 077/136] Cleanup --- src/components/views/right_panel/VerificationPanel.tsx | 2 +- src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index a4180bcd8a..0781bcf491 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -57,7 +57,7 @@ interface IProps { onClose: () => void; isRoomEncrypted: boolean; inDialog: boolean; - key: any; + key: number; } interface IState { diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 5b78863b35..b7dd5d85fc 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -24,16 +24,14 @@ export interface SetRightPanelPhasePayload extends ActionPayload { action: Action.SetRightPanelPhase; phase: RightPanelPhases; - refireParams?: SetRightPanelPhaseRefireParams; + refireParams: SetRightPanelPhaseRefireParams; } export interface SetRightPanelPhaseRefireParams { - // XXX: Fix after the types are defiend in matrix-js-sdk - // No appropriate types exist yet for the fields member?: RoomMember; - verificationRequest?: typeof VerificationRequest; + verificationRequest?: VerificationRequest; groupId?: string; groupRoomId?: string; - // XXX: 'view_3pid_invite' action's payload + // XXX: The type for event should 'view_3pid_invite' action's payload event?: any; } From 8120a26135390c341b1c1e74587b1439bdbf0ce4 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 29 Jul 2020 17:35:55 +0530 Subject: [PATCH 078/136] A bit more cleanup --- src/components/views/toasts/VerificationRequestToast.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/toasts/VerificationRequestToast.tsx b/src/components/views/toasts/VerificationRequestToast.tsx index 89c826eac4..8c8a74b2be 100644 --- a/src/components/views/toasts/VerificationRequestToast.tsx +++ b/src/components/views/toasts/VerificationRequestToast.tsx @@ -20,6 +20,7 @@ import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; +import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload" import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; import dis from "../../../dispatcher/dispatcher"; import ToastStore from "../../../stores/ToastStore"; @@ -105,7 +106,7 @@ export default class VerificationRequestToast extends React.PureComponent({ action: Action.SetRightPanelPhase, phase: RightPanelPhases.EncryptionPanel, refireParams: { From d8baad31da17a4f9d493c4dc073627fff6038d60 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 29 Jul 2020 16:51:37 +0200 Subject: [PATCH 079/136] provide nicer error for no known servers error when accepting an invite --- src/i18n/strings/en_EN.json | 2 ++ src/stores/RoomViewStore.js | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 18e2da9a31..ad33823c96 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -423,6 +423,8 @@ "Upgrade your %(brand)s": "Upgrade your %(brand)s", "A new version of %(brand)s is available!": "A new version of %(brand)s is available!", "Guest": "Guest", + "The person who invited you already left the room.": "The person who invited you already left the room.", + "The person who invited you already left the room, or their server is offline.": "The person who invited you already left the room, or their server is offline.", "There was an error joining the room": "There was an error joining the room", "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 6e5007895c..4f560e1fab 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -265,10 +265,20 @@ class RoomViewStore extends Store { }); let msg = err.message ? err.message : JSON.stringify(err); console.log("Failed to join room:", msg); - if (err.name === "ConnectionError") { + if (err.httpStatus === 404) { + const invitingUserId = this._getInvitingUserId(this._state.roomId); + // only provide a better error message for invites + if (invitingUserId) { + // if the inviting user is on the same HS, there can only be one cause: they left. + if (invitingUserId.endsWith(`:${MatrixClientPeg.get().getDomain()}`)) { + msg = _t("The person who invited you already left the room."); + } else { + msg = _t("The person who invited you already left the room, or their server is offline."); + } + } + } else if (err.name === "ConnectionError") { msg = _t("There was an error joining the room"); - } - if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { + } else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { msg =
{_t("Sorry, your homeserver is too old to participate in this room.")}
{_t("Please contact your homeserver administrator.")} @@ -282,6 +292,16 @@ class RoomViewStore extends Store { }); } + _getInvitingUserId(roomId) { + const cli = MatrixClientPeg.get(); + const room = cli.getRoom(roomId); + if (room && room.getMyMembership() === "invite") { + const myMember = room.getMember(cli.getUserId()); + const inviteEvent = myMember ? myMember.events.member : null; + return inviteEvent && inviteEvent.getSender(); + } + } + _joinRoomError(payload) { this._setState({ joining: false, From a3e01fc92ccc9edc5490900b69676056a4821c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 28 Jul 2020 21:57:35 +0000 Subject: [PATCH 080/136] Translated using Weblate (Estonian) Currently translated at 99.8% (2317 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 17daca64bc..cdb60979ad 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -2368,5 +2368,20 @@ "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "Sa oled selle sessiooni jaoks varem kasutanud %(brand)s'i uuemat versiooni. Selle versiooni kasutamiseks läbiva krüptimisega, pead sa esmalt logima välja ja siis uuesti logima tagasi sisse.", "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Kui sa pole seadistanud krüptitud sõnumite taastamise meetodeid, siis väljalogimisel sa kaotad võimaluse neid krüptitud sõnumeid lugeda.", "If you don't want to set this up now, you can later in Settings.": "Kui sa ei soovi seda teha kohe, siis vastava võimaluse leiad hiljem rakenduse seadistustest.", - "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Kui sa ei ole ise uusi taastamise meetodeid lisanud, siis võib olla tegemist ründega sinu konto vastu. Palun vaheta koheselt oma kasutajakonto salasõna ning määra seadistustes uus taastemeetod." + "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Kui sa ei ole ise uusi taastamise meetodeid lisanud, siis võib olla tegemist ründega sinu konto vastu. Palun vaheta koheselt oma kasutajakonto salasõna ning määra seadistustes uus taastemeetod.", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s võttis selles jututoas kasutusele %(groups)s kogukonna rinnamärgi.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s eemaldas selles jututoas kasutuselt %(groups)s kogukonna rinnamärgi.", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s võttis selles jututoas kasutusele %(newGroups)s kogukonna rinnamärgi ning eemaldas rinnamärgi %(oldGroups)s kogukonnalt.", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.": "Enne väljalogimist seo see sessioon krüptovõtmete varundusega. Kui sa seda ei tee, siis võid kaotada võtmed, mida kasutatakse vaid siin sessioonis.", + "Flair": "Kogukonna rinnasilt", + "Error updating flair": "Viga kogukonna rinnasildi uuendamisel", + "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Kogukonna rinnasildi uuendamisel tekkis viga. See kas on serveri poolt keelatud või tekkis mingi ajutine viga.", + "Showing flair for these communities:": "Näidatakse nende kogukondade rinnasilte:", + "This room is not showing flair for any communities": "Sellele jututoale ei ole jagatud ühtegi kogukonna rinnasilti", + "Display your community flair in rooms configured to show it.": "Näita oma kogukonna rinnasilti nendes jututubades, kus selle kuvamine on seadistatud.", + "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use %(brand)s with an existing Matrix account on a different homeserver.": "Kohandatud serveriseadistusi saad kasutada selleks, et logida sisse sinu valitud koduserverisse. See võimaldab sinul kasutada %(brand)s'i mõnes teises koduserveri hallatava kasutajakontoga.", + "Did you know: you can use communities to filter your %(brand)s experience!": "Kas sa teadsid, et sa võid %(brand)s'i parema kasutuskogemuse nimel pruukida kogukondi!", + "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Kui sa pole seadistanud krüptitud sõnumite taastamise meetodeid, siis väljalogimisel või muu sessiooni kasutamisel sa kaotad võimaluse oma krüptitud sõnumeid lugeda.", + "Set up Secure Message Recovery": "Võta kasutusele turvaline sõnumivõtmete varundus", + "Secure your backup with a recovery passphrase": "Krüpti oma varukoopia taastamiseks mõeldud paroolifraasiga" } From 284e4c9c1a5af2b6ec74b58ec90a0ddefa9aeaef Mon Sep 17 00:00:00 2001 From: Christopher May-Townsend Date: Wed, 29 Jul 2020 15:53:59 +0000 Subject: [PATCH 081/136] Translated using Weblate (Icelandic) Currently translated at 19.6% (455 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/is/ --- src/i18n/strings/is.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/is.json b/src/i18n/strings/is.json index 43b286aa2a..cbf05b89f0 100644 --- a/src/i18n/strings/is.json +++ b/src/i18n/strings/is.json @@ -456,5 +456,6 @@ "Notify the whole room": "Tilkynna öllum á spjallrásinni", "Room Notification": "Tilkynning á spjallrás", "Passphrases must match": "Lykilfrasar verða að stemma", - "Passphrase must not be empty": "Lykilfrasi má ekki vera auður" + "Passphrase must not be empty": "Lykilfrasi má ekki vera auður", + "Create Account": "Stofna Reikning" } From 9d124ff09b8e6b1439a3c8ac5a6e9d1c23ed2b04 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 10:57:14 -0600 Subject: [PATCH 082/136] Convert SettingsStore to TS --- src/@types/global.d.ts | 2 + src/settings/Settings.ts | 2 +- .../{SettingsStore.js => SettingsStore.ts} | 174 +++++++++++------- 3 files changed, 107 insertions(+), 71 deletions(-) rename src/settings/{SettingsStore.js => SettingsStore.ts} (79%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 080cdacafd..6510c02160 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -25,6 +25,7 @@ import { PlatformPeg } from "../PlatformPeg"; import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; import {IntegrationManagers} from "../integrations/IntegrationManagers"; import {ModalManager} from "../Modal"; +import SettingsStore from "../settings/SettingsStore"; declare global { interface Window { @@ -43,6 +44,7 @@ declare global { mxPlatformPeg: PlatformPeg; mxIntegrationManagers: typeof IntegrationManagers; singletonModalManager: ModalManager; + mxSettingsStore: SettingsStore; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 289a67cb57..a012bac257 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -71,7 +71,7 @@ const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = [ SettingLevel.CONFIG, ]; -interface ISetting { +export interface ISetting { // Must be set to true for features. Default is 'false'. isFeature?: boolean; diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.ts similarity index 79% rename from src/settings/SettingsStore.js rename to src/settings/SettingsStore.ts index a487f4e459..6bc1cecb73 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.ts @@ -1,6 +1,6 @@ /* Copyright 2017 Travis Ralston -Copyright 2019 New Vector Ltd. +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. @@ -22,12 +22,14 @@ import RoomAccountSettingsHandler from "./handlers/RoomAccountSettingsHandler"; import AccountSettingsHandler from "./handlers/AccountSettingsHandler"; import RoomSettingsHandler from "./handlers/RoomSettingsHandler"; import ConfigSettingsHandler from "./handlers/ConfigSettingsHandler"; -import {_t} from '../languageHandler'; +import { _t } from '../languageHandler'; import SdkConfig from "../SdkConfig"; import dis from '../dispatcher/dispatcher'; -import {SETTINGS} from "./Settings"; +import { ISetting, SETTINGS } from "./Settings"; import LocalEchoWrapper from "./handlers/LocalEchoWrapper"; -import {WatchManager} from "./WatchManager"; +import { WatchManager } from "./WatchManager"; +import { SettingLevel } from "./SettingLevel"; +import SettingsHandler from "./handlers/SettingsHandler"; const defaultWatchManager = new WatchManager(); @@ -46,13 +48,13 @@ for (const key of Object.keys(SETTINGS)) { } const LEVEL_HANDLERS = { - "device": new DeviceSettingsHandler(featureNames, defaultWatchManager), - "room-device": new RoomDeviceSettingsHandler(defaultWatchManager), - "room-account": new RoomAccountSettingsHandler(defaultWatchManager), - "account": new AccountSettingsHandler(defaultWatchManager), - "room": new RoomSettingsHandler(defaultWatchManager), - "config": new ConfigSettingsHandler(), - "default": new DefaultSettingsHandler(defaultSettings, invertedDefaultSettings), + [SettingLevel.DEVICE]: new DeviceSettingsHandler(featureNames, defaultWatchManager), + [SettingLevel.ROOM_DEVICE]: new RoomDeviceSettingsHandler(defaultWatchManager), + [SettingLevel.ROOM_ACCOUNT]: new RoomAccountSettingsHandler(defaultWatchManager), + [SettingLevel.ACCOUNT]: new AccountSettingsHandler(defaultWatchManager), + [SettingLevel.ROOM]: new RoomSettingsHandler(defaultWatchManager), + [SettingLevel.CONFIG]: new ConfigSettingsHandler(), + [SettingLevel.DEFAULT]: new DefaultSettingsHandler(defaultSettings, invertedDefaultSettings), }; // Wrap all the handlers with local echo @@ -61,20 +63,39 @@ for (const key of Object.keys(LEVEL_HANDLERS)) { } const LEVEL_ORDER = [ - 'device', 'room-device', 'room-account', 'account', 'room', 'config', 'default', + SettingLevel.DEVICE, + SettingLevel.ROOM_DEVICE, + SettingLevel.ROOM_ACCOUNT, + SettingLevel.ACCOUNT, + SettingLevel.ROOM, + SettingLevel.CONFIG, + SettingLevel.DEFAULT, ]; +export type CallbackFn = ( + settingName: string, + roomId: string, + atLevel: SettingLevel, + newValAtLevel: any, + newVal: any, +) => void; + +interface HandlerMap { + // @ts-ignore - TS wants this to be a string key but we know better + [level: SettingLevel]: SettingsHandler; +} + /** * Controls and manages application settings by providing varying levels at which the * setting value may be specified. The levels are then used to determine what the setting * value should be given a set of circumstances. The levels, in priority order, are: - * - "device" - Values are determined by the current device - * - "room-device" - Values are determined by the current device for a particular room - * - "room-account" - Values are determined by the current account for a particular room - * - "account" - Values are determined by the current account - * - "room" - Values are determined by a particular room (by the room admins) - * - "config" - Values are determined by the config.json - * - "default" - Values are determined by the hardcoded defaults + * - SettingLevel.DEVICE - Values are determined by the current device + * - SettingLevel.ROOM_DEVICE - Values are determined by the current device for a particular room + * - SettingLevel.ROOM_ACCOUNT - Values are determined by the current account for a particular room + * - SettingLevel.ACCOUNT - Values are determined by the current account + * - SettingLevel.ROOM - Values are determined by a particular room (by the room admins) + * - SettingLevel.CONFIG - Values are determined by the config.json + * - SettingLevel.DEFAULT - Values are determined by the hardcoded defaults * * Each level has a different method to storing the setting value. For implementation * specific details, please see the handlers. The "config" and "default" levels are @@ -95,11 +116,11 @@ export default class SettingsStore { // We also maintain a list of monitors which are special watchers: they cause dispatches // when the setting changes. We track which rooms we're monitoring though to ensure we // don't duplicate updates on the bus. - static _watchers = {}; // { callbackRef => { callbackFn } } - static _monitors = {}; // { settingName => { roomId => callbackRef } } + private static watchers = {}; // { callbackRef => { callbackFn } } + private static monitors = {}; // { settingName => { roomId => callbackRef } } // Counter used for generation of watcher IDs - static _watcherCount = 1; + private static watcherCount = 1; /** * Watches for changes in a particular setting. This is done without any local echo @@ -117,7 +138,7 @@ export default class SettingsStore { * if the change in value is worthwhile enough to react upon. * @returns {string} A reference to the watcher that was employed. */ - static watchSetting(settingName, roomId, callbackFn) { + public static watchSetting(settingName: string, roomId: string, callbackFn: CallbackFn): string { const setting = SETTINGS[settingName]; const originalSettingName = settingName; if (!setting) throw new Error(`${settingName} is not a setting`); @@ -126,14 +147,14 @@ export default class SettingsStore { settingName = setting.invertedSettingName; } - const watcherId = `${new Date().getTime()}_${SettingsStore._watcherCount++}_${settingName}_${roomId}`; + const watcherId = `${new Date().getTime()}_${SettingsStore.watcherCount++}_${settingName}_${roomId}`; const localizedCallback = (changedInRoomId, atLevel, newValAtLevel) => { const newValue = SettingsStore.getValue(originalSettingName); callbackFn(originalSettingName, changedInRoomId, atLevel, newValAtLevel, newValue); }; - SettingsStore._watchers[watcherId] = localizedCallback; + SettingsStore.watchers[watcherId] = localizedCallback; defaultWatchManager.watchSetting(settingName, roomId, localizedCallback); return watcherId; @@ -145,14 +166,14 @@ export default class SettingsStore { * @param {string} watcherReference The watcher reference (received from #watchSetting) * to cancel. */ - static unwatchSetting(watcherReference) { - if (!SettingsStore._watchers[watcherReference]) { + public static unwatchSetting(watcherReference: string) { + if (!SettingsStore.watchers[watcherReference]) { console.warn(`Ending non-existent watcher ID ${watcherReference}`); return; } - defaultWatchManager.unwatchSetting(SettingsStore._watchers[watcherReference]); - delete SettingsStore._watchers[watcherReference]; + defaultWatchManager.unwatchSetting(SettingsStore.watchers[watcherReference]); + delete SettingsStore.watchers[watcherReference]; } /** @@ -163,13 +184,13 @@ export default class SettingsStore { * @param {string} settingName The setting name to monitor. * @param {String} roomId The room ID to monitor for changes in. Use null for all rooms. */ - static monitorSetting(settingName, roomId) { + public static monitorSetting(settingName: string, roomId: string) { roomId = roomId || null; // the thing wants null specifically to work, so appease it. - if (!this._monitors[settingName]) this._monitors[settingName] = {}; + if (!this.monitors[settingName]) this.monitors[settingName] = {}; const registerWatcher = () => { - this._monitors[settingName][roomId] = SettingsStore.watchSetting( + this.monitors[settingName][roomId] = SettingsStore.watchSetting( settingName, roomId, (settingName, inRoomId, level, newValueAtLevel, newValue) => { dis.dispatch({ action: 'setting_updated', @@ -183,16 +204,16 @@ export default class SettingsStore { ); }; - const hasRoom = Object.keys(this._monitors[settingName]).find((r) => r === roomId || r === null); + const hasRoom = Object.keys(this.monitors[settingName]).find((r) => r === roomId || r === null); if (!hasRoom) { registerWatcher(); } else { if (roomId === null) { // Unregister all existing watchers and register the new one - for (const roomId of Object.keys(this._monitors[settingName])) { - SettingsStore.unwatchSetting(this._monitors[settingName][roomId]); + for (const roomId of Object.keys(this.monitors[settingName])) { + SettingsStore.unwatchSetting(this.monitors[settingName][roomId]); } - this._monitors[settingName] = {}; + this.monitors[settingName] = {}; registerWatcher(); } // else a watcher is already registered for the room, so don't bother registering it again } @@ -201,11 +222,11 @@ export default class SettingsStore { /** * Gets the translated display name for a given setting * @param {string} settingName The setting to look up. - * @param {"device"|"room-device"|"room-account"|"account"|"room"|"config"|"default"} atLevel + * @param {SettingLevel} atLevel * The level to get the display name for; Defaults to 'default'. * @return {String} The display name for the setting, or null if not found. */ - static getDisplayName(settingName, atLevel = "default") { + public static getDisplayName(settingName: string, atLevel = SettingLevel.DEFAULT) { if (!SETTINGS[settingName] || !SETTINGS[settingName].displayName) return null; let displayName = SETTINGS[settingName].displayName; @@ -214,20 +235,20 @@ export default class SettingsStore { else displayName = displayName["default"]; } - return _t(displayName); + return _t(displayName as string); } /** * Returns a list of all available labs feature names * @returns {string[]} The list of available feature names */ - static getLabsFeatures() { + public static getLabsFeatures(): string[] { const possibleFeatures = Object.keys(SETTINGS).filter((s) => SettingsStore.isFeature(s)); const enableLabs = SdkConfig.get()["enableLabs"]; if (enableLabs) return possibleFeatures; - return possibleFeatures.filter((s) => SettingsStore._getFeatureState(s) === "labs"); + return possibleFeatures.filter((s) => SettingsStore.getFeatureState(s) === "labs"); } /** @@ -235,7 +256,7 @@ export default class SettingsStore { * @param {string} settingName The setting to look up. * @return {boolean} True if the setting is a feature. */ - static isFeature(settingName) { + public static isFeature(settingName: string) { if (!SETTINGS[settingName]) return false; return SETTINGS[settingName].isFeature; } @@ -247,7 +268,7 @@ export default class SettingsStore { * @param {String} roomId The optional room ID to validate in, may be null. * @return {boolean} True if the feature is enabled, false otherwise */ - static isFeatureEnabled(settingName, roomId = null) { + public static isFeatureEnabled(settingName: string, roomId: string = null) { if (!SettingsStore.isFeature(settingName)) { throw new Error("Setting " + settingName + " is not a feature"); } @@ -261,7 +282,7 @@ export default class SettingsStore { * @param {boolean} value True to enable the feature, false otherwise. * @returns {Promise} Resolves when the setting has been set. */ - static setFeatureEnabled(settingName, value) { + public static setFeatureEnabled(settingName: string, value: any): Promise { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); @@ -281,7 +302,7 @@ export default class SettingsStore { * @param {boolean} excludeDefault True to disable using the default value. * @return {*} The value, or null if not found */ - static getValue(settingName, roomId = null, excludeDefault = false) { + public static getValue(settingName: string, roomId: string = null, excludeDefault = false): any { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); @@ -295,7 +316,7 @@ export default class SettingsStore { /** * Gets a setting's value at a particular level, ignoring all levels that are more specific. - * @param {"device"|"room-device"|"room-account"|"account"|"room"|"config"|"default"} level The + * @param {SettingLevel|"config"|"default"} level The * level to look at. * @param {string} settingName The name of the setting to read. * @param {String} roomId The room ID to read the setting value in, may be null. @@ -304,7 +325,13 @@ export default class SettingsStore { * @param {boolean} excludeDefault True to disable using the default value. * @return {*} The value, or null if not found. */ - static getValueAt(level, settingName, roomId = null, explicit = false, excludeDefault = false) { + public static getValueAt( + level: SettingLevel, + settingName: string, + roomId: string = null, + explicit = false, + excludeDefault = false, + ): any { // Verify that the setting is actually a setting const setting = SETTINGS[settingName]; if (!setting) { @@ -312,19 +339,19 @@ export default class SettingsStore { } const levelOrder = (setting.supportedLevelsAreOrdered ? setting.supportedLevels : LEVEL_ORDER); - if (!levelOrder.includes("default")) levelOrder.push("default"); // always include default + if (!levelOrder.includes(SettingLevel.DEFAULT)) levelOrder.push(SettingLevel.DEFAULT); // always include default const minIndex = levelOrder.indexOf(level); if (minIndex === -1) throw new Error("Level " + level + " is not prioritized"); if (SettingsStore.isFeature(settingName)) { - const configValue = SettingsStore._getFeatureState(settingName); + const configValue = SettingsStore.getFeatureState(settingName); if (configValue === "enable") return true; if (configValue === "disable") return false; // else let it fall through the default process } - const handlers = SettingsStore._getHandlers(settingName); + const handlers = SettingsStore.getHandlers(settingName); // Check if we need to invert the setting at all. Do this after we get the setting // handlers though, otherwise we'll fail to read the value. @@ -336,10 +363,10 @@ export default class SettingsStore { if (explicit) { const handler = handlers[level]; if (!handler) { - return SettingsStore._getFinalValue(setting, level, roomId, null, null); + return SettingsStore.getFinalValue(setting, level, roomId, null, null); } const value = handler.getValue(settingName, roomId); - return SettingsStore._getFinalValue(setting, level, roomId, value, level); + return SettingsStore.getFinalValue(setting, level, roomId, value, level); } for (let i = minIndex; i < levelOrder.length; i++) { @@ -349,10 +376,10 @@ export default class SettingsStore { const value = handler.getValue(settingName, roomId); if (value === null || value === undefined) continue; - return SettingsStore._getFinalValue(setting, level, roomId, value, levelOrder[i]); + return SettingsStore.getFinalValue(setting, level, roomId, value, levelOrder[i]); } - return SettingsStore._getFinalValue(setting, level, roomId, null, null); + return SettingsStore.getFinalValue(setting, level, roomId, null, null); } /** @@ -361,7 +388,7 @@ export default class SettingsStore { * @param {String} roomId The room ID to read the setting value in, may be null. * @return {*} The default value */ - static getDefaultValue(settingName) { + public static getDefaultValue(settingName: string): any { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); @@ -370,7 +397,13 @@ export default class SettingsStore { return SETTINGS[settingName].default; } - static _getFinalValue(setting, level, roomId, calculatedValue, calculatedAtLevel) { + private static getFinalValue( + setting: ISetting, + level: SettingLevel, + roomId: string, + calculatedValue: any, + calculatedAtLevel: any, + ): any { let resultingValue = calculatedValue; if (setting.controller) { @@ -389,20 +422,21 @@ export default class SettingsStore { * to indicate that the level should no longer have an override. * @param {string} settingName The name of the setting to change. * @param {String} roomId The room ID to change the value in, may be null. - * @param {"device"|"room-device"|"room-account"|"account"|"room"} level The level + * @param {SettingLevel} level The level * to change the value at. * @param {*} value The new value of the setting, may be null. * @return {Promise} Resolves when the setting has been changed. */ + /* eslint-enable valid-jsdoc */ - static async setValue(settingName, roomId, level, value) { + public static async setValue(settingName: string, roomId: string, level: SettingLevel, value: any): Promise { // Verify that the setting is actually a setting const setting = SETTINGS[settingName]; if (!setting) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); } - const handler = SettingsStore._getHandler(settingName, level); + const handler = SettingsStore.getHandler(settingName, level); if (!handler) { throw new Error("Setting " + settingName + " does not have a handler for " + level); } @@ -434,28 +468,28 @@ export default class SettingsStore { * set for a particular room, otherwise it should be supplied. * @param {string} settingName The name of the setting to check. * @param {String} roomId The room ID to check in, may be null. - * @param {"device"|"room-device"|"room-account"|"account"|"room"} level The level to + * @param {SettingLevel} level The level to * check at. * @return {boolean} True if the user may set the setting, false otherwise. */ - static canSetValue(settingName, roomId, level) { + public static canSetValue(settingName: string, roomId: string, level: SettingLevel): boolean { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); } - const handler = SettingsStore._getHandler(settingName, level); + const handler = SettingsStore.getHandler(settingName, level); if (!handler) return false; return handler.canSetValue(settingName, roomId); } /** * Determines if the given level is supported on this device. - * @param {"device"|"room-device"|"room-account"|"account"|"room"} level The level + * @param {SettingLevel} level The level * to check the feasibility of. * @return {boolean} True if the level is supported, false otherwise. */ - static isLevelSupported(level) { + public static isLevelSupported(level: SettingLevel): boolean { if (!LEVEL_HANDLERS[level]) return false; return LEVEL_HANDLERS[level].isSupported(); } @@ -467,7 +501,7 @@ export default class SettingsStore { * @param {string} realSettingName The setting name to try and read. * @param {string} roomId Optional room ID to test the setting in. */ - static debugSetting(realSettingName, roomId) { + public static debugSetting(realSettingName: string, roomId: string) { console.log(`--- DEBUG ${realSettingName}`); // Note: we intentionally use JSON.stringify here to avoid the console masking the @@ -555,13 +589,13 @@ export default class SettingsStore { console.log(`--- END DEBUG`); } - static _getHandler(settingName, level) { - const handlers = SettingsStore._getHandlers(settingName); + private static getHandler(settingName: string, level: SettingLevel): SettingsHandler { + const handlers = SettingsStore.getHandlers(settingName); if (!handlers[level]) return null; return handlers[level]; } - static _getHandlers(settingName) { + private static getHandlers(settingName: string): HandlerMap { if (!SETTINGS[settingName]) return {}; const handlers = {}; @@ -576,7 +610,7 @@ export default class SettingsStore { return handlers; } - static _getFeatureState(settingName) { + private static getFeatureState(settingName: string): "labs" | "disable" | "enable" | string { const featuresConfig = SdkConfig.get()['features']; const enableLabs = SdkConfig.get()['enableLabs']; // we'll honour the old flag @@ -596,4 +630,4 @@ export default class SettingsStore { } // For debugging purposes -global.mxSettingsStore = SettingsStore; +window.mxSettingsStore = SettingsStore; From 9c8682428f3a85b0295049744ebfeeda7a17729a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 11:03:43 -0600 Subject: [PATCH 083/136] Fix various TypeScript linting issues --- src/MatrixClientPeg.ts | 2 +- src/components/views/elements/SettingsFlag.tsx | 11 ++++++----- src/rageshake/submit-rageshake.ts | 2 +- src/settings/SettingsStore.ts | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 5f334a639c..be16f5fe10 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -256,7 +256,7 @@ class _MatrixClientPeg implements IMatrixClientPeg { deviceId: creds.deviceId, pickleKey: creds.pickleKey, timelineSupport: true, - forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), + forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer'), fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'), verificationMethods: [ verificationMethods.SAS, diff --git a/src/components/views/elements/SettingsFlag.tsx b/src/components/views/elements/SettingsFlag.tsx index 4f41db51e2..03e91fac62 100644 --- a/src/components/views/elements/SettingsFlag.tsx +++ b/src/components/views/elements/SettingsFlag.tsx @@ -20,11 +20,12 @@ import SettingsStore from "../../../settings/SettingsStore"; import { _t } from '../../../languageHandler'; import ToggleSwitch from "./ToggleSwitch"; import StyledCheckbox from "./StyledCheckbox"; +import { SettingLevel } from "../../../settings/SettingLevel"; interface IProps { // The setting must be a boolean name: string; - level: string; + level: SettingLevel; roomId?: string; // for per-room settings label?: string; // untranslated isExplicit?: boolean; @@ -52,8 +53,8 @@ export default class SettingsFlag extends React.Component { }; } - private onChange = (checked: boolean): void => { - this.save(checked); + private onChange = async (checked: boolean) => { + await this.save(checked); this.setState({ value: checked }); if (this.props.onChange) this.props.onChange(checked); }; @@ -62,8 +63,8 @@ export default class SettingsFlag extends React.Component { this.onChange(e.target.checked); }; - private save = (val?: boolean): void => { - return SettingsStore.setValue( + private save = async (val?: boolean) => { + await SettingsStore.setValue( this.props.name, this.props.roomId, this.props.level, diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index 64a1ea0c33..350602aa5d 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -141,7 +141,7 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp } // add labs options - const enabledLabs = SettingsStore.getLabsFeatures().filter(SettingsStore.isFeatureEnabled); + const enabledLabs = SettingsStore.getLabsFeatures().filter(f => SettingsStore.isFeatureEnabled(f)); if (enabledLabs.length) { body.append('enabled_labs', enabledLabs.join(', ')); } diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index 6bc1cecb73..25ed99477f 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -291,7 +291,7 @@ export default class SettingsStore { throw new Error("Setting " + settingName + " is not a feature"); } - return SettingsStore.setValue(settingName, null, "device", value); + return SettingsStore.setValue(settingName, null, SettingLevel.DEVICE, value); } /** From ae3eb3da9c6d6fa6aeb8230f84fc75b73176a3d9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 11:51:42 -0600 Subject: [PATCH 084/136] Fix MatrixClient access in settings --- src/settings/handlers/MatrixClientBackedSettingsHandler.ts | 2 +- src/settings/handlers/RoomSettingsHandler.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/settings/handlers/MatrixClientBackedSettingsHandler.ts b/src/settings/handlers/MatrixClientBackedSettingsHandler.ts index c9c670f5d9..76825d1335 100644 --- a/src/settings/handlers/MatrixClientBackedSettingsHandler.ts +++ b/src/settings/handlers/MatrixClientBackedSettingsHandler.ts @@ -44,7 +44,7 @@ export default abstract class MatrixClientBackedSettingsHandler extends Settings } public get client(): MatrixClient { - return MatrixClientBackedSettingsHandler.matrixClient; + return MatrixClientBackedSettingsHandler._matrixClient; } protected initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { diff --git a/src/settings/handlers/RoomSettingsHandler.ts b/src/settings/handlers/RoomSettingsHandler.ts index cc9496c82b..4b4d4c4ad9 100644 --- a/src/settings/handlers/RoomSettingsHandler.ts +++ b/src/settings/handlers/RoomSettingsHandler.ts @@ -34,13 +34,13 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl protected initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient) { if (oldClient) { - oldClient.removeListener("RoomState.events", this._onEvent); + oldClient.removeListener("RoomState.events", this.onEvent); } - newClient.on("RoomState.events", this._onEvent); + newClient.on("RoomState.events", this.onEvent); } - private _onEvent = (event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent) => { + private onEvent = (event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent) => { const roomId = event.getRoomId(); const room = this.client.getRoom(roomId); From b8e51076a87f73d2cd9e2ca47baaa3b97191e25d Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Wed, 29 Jul 2020 23:58:32 +0530 Subject: [PATCH 085/136] Fix private functions for RoomHeaderButtons --- .../views/right_panel/RoomHeaderButtons.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 8620d5b485..1b3117d67e 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -36,9 +36,9 @@ const MEMBER_PHASES = [ export default class RoomHeaderButtons extends HeaderButtons { constructor(props) { super(props, HeaderKind.Room); - this._onMembersClicked = this._onMembersClicked.bind(this); - this._onFilesClicked = this._onFilesClicked.bind(this); - this._onNotificationsClicked = this._onNotificationsClicked.bind(this); + this.onMembersClicked = this.onMembersClicked.bind(this); + this.onFilesClicked = this.onFilesClicked.bind(this); + this.onNotificationsClicked = this.onNotificationsClicked.bind(this); } onAction(payload: ActionPayload) { @@ -58,7 +58,7 @@ export default class RoomHeaderButtons extends HeaderButtons { } } - _onMembersClicked() { + private onMembersClicked() { if (this.state.phase === RightPanelPhases.RoomMemberInfo) { // send the active phase to trigger a toggle // XXX: we should pass refireParams here but then it won't collapse as we desire it to @@ -69,12 +69,12 @@ export default class RoomHeaderButtons extends HeaderButtons { } } - _onFilesClicked() { + private onFilesClicked() { // This toggles for us, if needed this.setPhase(RightPanelPhases.FilePanel); } - _onNotificationsClicked() { + private onNotificationsClicked() { // This toggles for us, if needed this.setPhase(RightPanelPhases.NotificationPanel); } @@ -84,19 +84,19 @@ export default class RoomHeaderButtons extends HeaderButtons { , , , ]; From 14757cacd54258c4a648bb9c8e4d86103b605f56 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 12:43:35 -0600 Subject: [PATCH 086/136] Introduce a concept of "non-urgent" toasts This is somewhat expected to be temporary. --- res/css/_components.scss | 1 + .../structures/_NonUrgentToastContainer.scss | 35 +++++++++++ src/@types/common.ts | 4 ++ src/components/structures/LoggedInView.tsx | 2 + .../structures/NonUrgentToastContainer.tsx | 63 +++++++++++++++++++ src/stores/NonUrgentToastStore.ts | 50 +++++++++++++++ src/stores/ToastStore.ts | 7 ++- 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 res/css/structures/_NonUrgentToastContainer.scss create mode 100644 src/components/structures/NonUrgentToastContainer.tsx create mode 100644 src/stores/NonUrgentToastStore.ts diff --git a/res/css/_components.scss b/res/css/_components.scss index 23e4af780a..28e1332d08 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -15,6 +15,7 @@ @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; +@import "./structures/_NonUrgentToastContainer.scss"; @import "./structures/_NotificationPanel.scss"; @import "./structures/_RightPanel.scss"; @import "./structures/_RoomDirectory.scss"; diff --git a/res/css/structures/_NonUrgentToastContainer.scss b/res/css/structures/_NonUrgentToastContainer.scss new file mode 100644 index 0000000000..26715c6048 --- /dev/null +++ b/res/css/structures/_NonUrgentToastContainer.scss @@ -0,0 +1,35 @@ +/* +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_NonUrgentToastContainer { + position: absolute; + bottom: 30px; + left: 28px; + z-index: 101; // same level as other toasts + + .mx_NonUrgentToastContainer_toast { + padding: 10px 12px; + border-radius: 8px; + width: 320px; + font-size: $font-13px; + margin-top: 8px; + + // We don't use variables on the colours because we want it to be the same + // in all themes. + background-color: #17191C; + color: #fff; + } +} diff --git a/src/@types/common.ts b/src/@types/common.ts index a24d47ac9e..b887bd4090 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -14,7 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { JSXElementConstructor } from "react"; + // Based on https://stackoverflow.com/a/53229857/3532235 export type Without = {[P in Exclude] ? : never}; export type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U; export type Writeable = { -readonly [P in keyof T]: T[P] }; + +export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor; diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1f561e68ef..431b7307d8 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -54,6 +54,7 @@ import LeftPanel from "./LeftPanel"; import CallContainer from '../views/voip/CallContainer'; import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload"; import RoomListStore from "../../stores/room-list/RoomListStore"; +import NonUrgentToastContainer from "./NonUrgentToastContainer"; // 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. @@ -687,6 +688,7 @@ class LoggedInView extends React.Component {
+ ); } diff --git a/src/components/structures/NonUrgentToastContainer.tsx b/src/components/structures/NonUrgentToastContainer.tsx new file mode 100644 index 0000000000..8d415df4dd --- /dev/null +++ b/src/components/structures/NonUrgentToastContainer.tsx @@ -0,0 +1,63 @@ +/* +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 { ComponentClass } from "../../@types/common"; +import NonUrgentToastStore from "../../stores/NonUrgentToastStore"; +import { UPDATE_EVENT } from "../../stores/AsyncStore"; + +interface IProps { +} + +interface IState { + toasts: ComponentClass[], +} + +export default class NonUrgentToastContainer extends React.PureComponent { + public constructor(props, context) { + super(props, context); + + this.state = { + toasts: NonUrgentToastStore.instance.components, + }; + + NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts); + } + + public componentWillUnmount() { + NonUrgentToastStore.instance.off(UPDATE_EVENT, this.onUpdateToasts); + } + + private onUpdateToasts = () => { + this.setState({toasts: NonUrgentToastStore.instance.components}); + }; + + public render() { + const toasts = this.state.toasts.map((t, i) => { + return ( +
+ {React.createElement(t, {})} +
+ ); + }); + + return ( +
+ {toasts} +
+ ); + } +} diff --git a/src/stores/NonUrgentToastStore.ts b/src/stores/NonUrgentToastStore.ts new file mode 100644 index 0000000000..72f896749c --- /dev/null +++ b/src/stores/NonUrgentToastStore.ts @@ -0,0 +1,50 @@ +/* +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 EventEmitter from "events"; +import { ComponentClass } from "../@types/common"; +import { UPDATE_EVENT } from "./AsyncStore"; + +export type ToastReference = symbol; + +export default class NonUrgentToastStore extends EventEmitter { + private static _instance: NonUrgentToastStore; + + private toasts = new Map(); + + public static get instance(): NonUrgentToastStore { + if (!NonUrgentToastStore._instance) { + NonUrgentToastStore._instance = new NonUrgentToastStore(); + } + return NonUrgentToastStore._instance; + } + + public get components(): ComponentClass[] { + return Array.from(this.toasts.values()); + } + + public addToast(c: ComponentClass): ToastReference { + const ref: ToastReference = Symbol(); + this.toasts.set(ref, c); + this.emit(UPDATE_EVENT); + return ref; + } + + public removeToast(ref: ToastReference) { + this.toasts.delete(ref); + this.emit(UPDATE_EVENT); + } +} diff --git a/src/stores/ToastStore.ts b/src/stores/ToastStore.ts index afb9fe1f8c..038aebc7c9 100644 --- a/src/stores/ToastStore.ts +++ b/src/stores/ToastStore.ts @@ -15,9 +15,10 @@ limitations under the License. */ import EventEmitter from "events"; -import React, {JSXElementConstructor} from "react"; +import React from "react"; +import { ComponentClass } from "../@types/common"; -export interface IToast> { +export interface IToast { key: string; // higher priority number will be shown on top of lower priority priority: number; @@ -55,7 +56,7 @@ export default class ToastStore extends EventEmitter { * * @param {object} newToast The new toast */ - addOrReplaceToast>(newToast: IToast) { + addOrReplaceToast(newToast: IToast) { const oldIndex = this.toasts.findIndex(t => t.key === newToast.key); if (oldIndex === -1) { let newIndex = this.toasts.length; From 75f53e411841703a8b2d1dbb6fac51752ea3aa59 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 16:52:20 -0600 Subject: [PATCH 087/136] Ensure AsyncStoreWithClient can start mid-lifecycle In some cases we're likely to miss the PREPARED sync, so just handle ourselves as ready if we have a client set. --- src/stores/AsyncStoreWithClient.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/stores/AsyncStoreWithClient.ts b/src/stores/AsyncStoreWithClient.ts index 5b9f95f991..4accef6f77 100644 --- a/src/stores/AsyncStoreWithClient.ts +++ b/src/stores/AsyncStoreWithClient.ts @@ -17,12 +17,25 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/client"; import { AsyncStore } from "./AsyncStore"; import { ActionPayload } from "../dispatcher/payloads"; +import { Dispatcher } from "flux"; +import { MatrixClientPeg } from "../MatrixClientPeg"; export abstract class AsyncStoreWithClient extends AsyncStore { protected matrixClient: MatrixClient; protected abstract async onAction(payload: ActionPayload); + protected constructor(dispatcher: Dispatcher, initialState: T = {}) { + super(dispatcher, initialState); + + if (MatrixClientPeg.get()) { + this.matrixClient = MatrixClientPeg.get(); + + // noinspection JSIgnoredPromiseFromCall + this.onReady(); + } + } + protected async onReady() { // Default implementation is to do nothing. } @@ -40,8 +53,13 @@ export abstract class AsyncStoreWithClient extends AsyncStore< return; } - this.matrixClient = payload.matrixClient; - await this.onReady(); + if (this.matrixClient !== payload.matrixClient) { + if (this.matrixClient) { + await this.onNotReady(); + } + 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(); From 8f1af4be14028e91fdf807d7f01f1b57170d0116 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 16:53:26 -0600 Subject: [PATCH 088/136] Add local echo capabilities for rooms The structure here might need some documentation and work, but overall the idea is that all calls pass through a CachedEcho instance, which are self-updating. --- src/stores/local-echo/CachedEcho.ts | 72 ++++++++++++++++++++ src/stores/local-echo/EchoChamber.ts | 31 +++++++++ src/stores/local-echo/EchoContext.ts | 68 +++++++++++++++++++ src/stores/local-echo/EchoStore.ts | 72 ++++++++++++++++++++ src/stores/local-echo/EchoTransaction.ts | 65 ++++++++++++++++++ src/stores/local-echo/RoomCachedEcho.ts | 77 +++++++++++++++++++++ src/stores/local-echo/RoomEchoContext.ts | 24 +++++++ src/utils/Whenable.ts | 86 ++++++++++++++++++++++++ 8 files changed, 495 insertions(+) create mode 100644 src/stores/local-echo/CachedEcho.ts create mode 100644 src/stores/local-echo/EchoChamber.ts create mode 100644 src/stores/local-echo/EchoContext.ts create mode 100644 src/stores/local-echo/EchoStore.ts create mode 100644 src/stores/local-echo/EchoTransaction.ts create mode 100644 src/stores/local-echo/RoomCachedEcho.ts create mode 100644 src/stores/local-echo/RoomEchoContext.ts create mode 100644 src/utils/Whenable.ts diff --git a/src/stores/local-echo/CachedEcho.ts b/src/stores/local-echo/CachedEcho.ts new file mode 100644 index 0000000000..caa7ad1d48 --- /dev/null +++ b/src/stores/local-echo/CachedEcho.ts @@ -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. +*/ + +import { EchoContext } from "./EchoContext"; +import { RunFn, TransactionStatus } from "./EchoTransaction"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { EventEmitter } from "events"; + +export async function implicitlyReverted() { + // do nothing :D +} + +export const PROPERTY_UPDATED = "property_updated"; + +export abstract class CachedEcho extends EventEmitter { + private cache = new Map(); + protected matrixClient: MatrixClient; + + protected constructor(protected context: C, private lookupFn: (key: K) => V) { + super(); + } + + public setClient(client: MatrixClient) { + const oldClient = this.matrixClient; + this.matrixClient = client; + this.onClientChanged(oldClient, client); + } + + protected abstract onClientChanged(oldClient: MatrixClient, newClient: MatrixClient); + + /** + * Gets a value. If the key is in flight, the cached value will be returned. If + * the key is not in flight then the lookupFn provided to this class will be + * called instead. + * @param key The key to look up. + * @returns The value for the key. + */ + public getValue(key: K): V { + return this.cache.has(key) ? this.cache.get(key) : this.lookupFn(key); + } + + private cacheVal(key: K, val: V) { + this.cache.set(key, val); + this.emit(PROPERTY_UPDATED, key); + } + + private decacheKey(key: K) { + this.cache.delete(key); + this.emit(PROPERTY_UPDATED, key); + } + + public setValue(auditName: string, key: K, targetVal: V, runFn: RunFn, revertFn: RunFn) { + this.cacheVal(key, targetVal); // set the cache now as it won't be updated by the .when() ladder below. + this.context.beginTransaction(auditName, runFn) + .when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal)) + .whenAnyOf([TransactionStatus.DoneError, TransactionStatus.DoneSuccess], () => this.decacheKey(key)) + .when(TransactionStatus.DoneError, () => revertFn()); + } +} diff --git a/src/stores/local-echo/EchoChamber.ts b/src/stores/local-echo/EchoChamber.ts new file mode 100644 index 0000000000..4c5109da2d --- /dev/null +++ b/src/stores/local-echo/EchoChamber.ts @@ -0,0 +1,31 @@ +/* +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 { RoomCachedEcho } from "./RoomCachedEcho"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { EchoStore } from "./EchoStore"; + +/** + * Semantic access to local echo + */ +export class EchoChamber { + private constructor() { + } + + public static forRoom(room: Room): RoomCachedEcho { + return EchoStore.instance.getOrCreateEchoForRoom(room); + } +} diff --git a/src/stores/local-echo/EchoContext.ts b/src/stores/local-echo/EchoContext.ts new file mode 100644 index 0000000000..0d5eb961c3 --- /dev/null +++ b/src/stores/local-echo/EchoContext.ts @@ -0,0 +1,68 @@ +/* +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 { EchoTransaction, RunFn, TransactionStatus } from "./EchoTransaction"; +import { arrayFastClone } from "../../utils/arrays"; +import { IDestroyable } from "../../utils/IDestroyable"; +import { Whenable } from "../../utils/Whenable"; + +export enum ContextTransactionState { + NotStarted, + PendingErrors, + AllSuccessful +} + +export abstract class EchoContext extends Whenable implements IDestroyable { + private _transactions: EchoTransaction[] = []; + public readonly startTime: Date = new Date(); + + public get transactions(): EchoTransaction[] { + return arrayFastClone(this._transactions); + } + + public beginTransaction(auditName: string, runFn: RunFn): EchoTransaction { + const txn = new EchoTransaction(auditName, runFn); + this._transactions.push(txn); + txn.whenAnything(this.checkTransactions); + + // We have no intent to call the transaction again if it succeeds (in fact, it'll + // be really angry at us if we do), so call that the end of the road for the events. + txn.when(TransactionStatus.DoneSuccess, () => txn.destroy()); + + return txn; + } + + private checkTransactions = () => { + let status = ContextTransactionState.AllSuccessful; + for (const txn of this.transactions) { + if (txn.status === TransactionStatus.DoneError) { + status = ContextTransactionState.PendingErrors; + break; + } else if (txn.status === TransactionStatus.Pending) { + status = ContextTransactionState.NotStarted; + // no break as we might hit something which broke + } + } + this.notifyCondition(status); + }; + + public destroy() { + for (const txn of this.transactions) { + txn.destroy(); + } + super.destroy(); + } +} diff --git a/src/stores/local-echo/EchoStore.ts b/src/stores/local-echo/EchoStore.ts new file mode 100644 index 0000000000..80c669e5c6 --- /dev/null +++ b/src/stores/local-echo/EchoStore.ts @@ -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. +*/ + +import { EventEmitter } from "events"; +import { CachedEcho } from "./CachedEcho"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { RoomCachedEcho } from "./RoomCachedEcho"; +import { RoomEchoContext } from "./RoomEchoContext"; +import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; +import defaultDispatcher from "../../dispatcher/dispatcher"; +import { ActionPayload } from "../../dispatcher/payloads"; + +type ContextKey = string; + +const roomContextKey = (room: Room): ContextKey => `room-${room.roomId}`; + +export class EchoStore extends AsyncStoreWithClient { + private static _instance: EchoStore; + + private caches = new Map>(); + + constructor() { + super(defaultDispatcher); + } + + public static get instance(): EchoStore { + if (!EchoStore._instance) { + EchoStore._instance = new EchoStore(); + } + return EchoStore._instance; + } + + public getOrCreateEchoForRoom(room: Room): RoomCachedEcho { + if (this.caches.has(roomContextKey(room))) { + return this.caches.get(roomContextKey(room)) as RoomCachedEcho; + } + const echo = new RoomCachedEcho(new RoomEchoContext(room)); + echo.setClient(this.matrixClient); + this.caches.set(roomContextKey(room), echo); + return echo; + } + + protected async onReady(): Promise { + for (const echo of this.caches.values()) { + echo.setClient(this.matrixClient); + } + } + + protected async onNotReady(): Promise { + for (const echo of this.caches.values()) { + echo.setClient(null); + } + } + + protected async onAction(payload: ActionPayload): Promise { + // We have nothing to actually listen for + return Promise.resolve(); + } +} diff --git a/src/stores/local-echo/EchoTransaction.ts b/src/stores/local-echo/EchoTransaction.ts new file mode 100644 index 0000000000..b2125aac08 --- /dev/null +++ b/src/stores/local-echo/EchoTransaction.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. +*/ + +import { Whenable } from "../../utils/Whenable"; + +export type RunFn = () => Promise; + +export enum TransactionStatus { + Pending, + DoneSuccess, + DoneError, +} + +export class EchoTransaction extends Whenable { + private _status = TransactionStatus.Pending; + private didFail = false; + + public constructor( + public readonly auditName, + public runFn: RunFn, + ) { + super(); + } + + public get didPreviouslyFail(): boolean { + return this.didFail; + } + + public get status(): TransactionStatus { + return this._status; + } + + public run() { + if (this.status === TransactionStatus.DoneSuccess) { + throw new Error("Cannot re-run a successful echo transaction"); + } + this.setStatus(TransactionStatus.Pending); + this.runFn() + .then(() => this.setStatus(TransactionStatus.DoneSuccess)) + .catch(() => this.setStatus(TransactionStatus.DoneError)); + } + + private setStatus(status: TransactionStatus) { + this._status = status; + if (status === TransactionStatus.DoneError) { + this.didFail = true; + } else if (status === TransactionStatus.DoneSuccess) { + this.didFail = false; + } + this.notifyCondition(status); + } +} diff --git a/src/stores/local-echo/RoomCachedEcho.ts b/src/stores/local-echo/RoomCachedEcho.ts new file mode 100644 index 0000000000..0aec4a4e1c --- /dev/null +++ b/src/stores/local-echo/RoomCachedEcho.ts @@ -0,0 +1,77 @@ +/* +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 { CachedEcho, implicitlyReverted, PROPERTY_UPDATED } from "./CachedEcho"; +import { getRoomNotifsState, setRoomNotifsState } from "../../RoomNotifs"; +import { RoomEchoContext } from "./RoomEchoContext"; +import { _t } from "../../languageHandler"; +import { Volume } from "../../RoomNotifsTypes"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + +export type CachedRoomValues = Volume; + +export enum CachedRoomKey { + NotificationVolume, +} + +export class RoomCachedEcho extends CachedEcho { + private properties = new Map(); + + public constructor(context: RoomEchoContext) { + super(context, (k) => this.properties.get(k)); + } + + protected onClientChanged(oldClient, newClient) { + this.properties.clear(); + if (oldClient) { + oldClient.removeListener("accountData", this.onAccountData); + } + if (newClient) { + // Register the listeners first + newClient.on("accountData", this.onAccountData); + + // Then populate the properties map + this.updateNotificationVolume(); + } + } + + private onAccountData = (event: MatrixEvent) => { + if (event.getType() === "m.push_rules") { + const currentVolume = this.properties.get(CachedRoomKey.NotificationVolume) as Volume; + const newVolume = getRoomNotifsState(this.context.room.roomId) as Volume; + if (currentVolume !== newVolume) { + this.updateNotificationVolume(); + } + } + }; + + private updateNotificationVolume() { + this.properties.set(CachedRoomKey.NotificationVolume, getRoomNotifsState(this.context.room.roomId)); + this.emit(PROPERTY_UPDATED, CachedRoomKey.NotificationVolume); + } + + // ---- helpers below here ---- + + public get notificationVolume(): Volume { + return this.getValue(CachedRoomKey.NotificationVolume); + } + + public set notificationVolume(v: Volume) { + this.setValue(_t("Change notification settings"), CachedRoomKey.NotificationVolume, v, async () => { + setRoomNotifsState(this.context.room.roomId, v); + }, implicitlyReverted); + } +} diff --git a/src/stores/local-echo/RoomEchoContext.ts b/src/stores/local-echo/RoomEchoContext.ts new file mode 100644 index 0000000000..4105f728c3 --- /dev/null +++ b/src/stores/local-echo/RoomEchoContext.ts @@ -0,0 +1,24 @@ +/* +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 { EchoContext } from "./EchoContext"; +import { Room } from "matrix-js-sdk/src/models/room"; + +export class RoomEchoContext extends EchoContext { + constructor(public readonly room: Room) { + super(); + } +} diff --git a/src/utils/Whenable.ts b/src/utils/Whenable.ts new file mode 100644 index 0000000000..afa220fe82 --- /dev/null +++ b/src/utils/Whenable.ts @@ -0,0 +1,86 @@ +/* +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 { IDestroyable } from "./IDestroyable"; +import { arrayFastClone } from "./arrays"; + +export type WhenFn = (w: Whenable) => void; + +/** + * Whenables are a cheap way to have Observable patterns mixed with typical + * usage of Promises, without having to tear down listeners or calls. Whenables + * are intended to be used when a condition will be met multiple times and + * the consumer needs to know *when* that happens. + */ +export abstract class Whenable implements IDestroyable { + private listeners: {condition: T | null, fn: WhenFn}[] = []; + + /** + * Sets up a call to `fn` *when* the `condition` is met. + * @param condition The condition to match. + * @param fn The function to call. + * @returns This. + */ + public when(condition: T, fn: WhenFn): Whenable { + this.listeners.push({condition, fn}); + return this; + } + + /** + * Sets up a fall to `fn` *when* any of the `conditions` are met. + * @param conditions The conditions to match. + * @param fn The function to call. + * @returns This. + */ + public whenAnyOf(conditions: T[], fn: WhenFn): Whenable { + for (const condition of conditions) { + this.when(condition, fn); + } + return this; + } + + /** + * Sets up a call to `fn` *when* any condition is met. + * @param fn The function to call. + * @returns This. + */ + public whenAnything(fn: WhenFn): Whenable { + this.listeners.push({condition: null, fn}); + return this; + } + + /** + * Notifies all the whenables of a given condition. + * @param condition The new condition that has been met. + */ + protected notifyCondition(condition: T) { + const listeners = arrayFastClone(this.listeners); // clone just in case the handler modifies us + for (const listener of listeners) { + if (listener.condition === null || listener.condition === condition) { + try { + listener.fn(this); + } catch (e) { + console.error(`Error calling whenable listener for ${condition}:`, e); + } + } + } + } + + public destroy() { + this.listeners = []; + } +} From 0f1b9937a95b6f7142e99c96b64d513ce8a66fb5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 16:54:04 -0600 Subject: [PATCH 089/136] Connect RoomTile's notification volume to local echo This presumes success as we don't yet have a UI for failures. --- src/components/views/rooms/RoomTile.tsx | 44 +++++++++++-------------- src/i18n/strings/en_EN.json | 1 + 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index fc7d3a528b..4b051cf795 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -17,12 +17,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {createRef} from "react"; +import React, { createRef } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import classNames from "classnames"; import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex"; import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton"; import dis from '../../../dispatcher/dispatcher'; +import defaultDispatcher from '../../../dispatcher/dispatcher'; import { Key } from "../../../Keyboard"; import ActiveRoomObserver from "../../../ActiveRoomObserver"; import { _t } from "../../../languageHandler"; @@ -30,31 +31,26 @@ import { ChevronFace, ContextMenu, ContextMenuTooltipButton, - MenuItemRadio, - MenuItemCheckbox, MenuItem, + MenuItemCheckbox, + MenuItemRadio, } from "../../structures/ContextMenu"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; -import { - getRoomNotifsState, - setRoomNotifsState, - ALL_MESSAGES, - ALL_MESSAGES_LOUD, - MENTIONS_ONLY, - MUTE, -} from "../../../RoomNotifs"; +import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE, } from "../../../RoomNotifs"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import NotificationBadge from "./NotificationBadge"; import { Volume } from "../../../RoomNotifsTypes"; import RoomListStore from "../../../stores/room-list/RoomListStore"; import RoomListActions from "../../../actions/RoomListActions"; -import defaultDispatcher from "../../../dispatcher/dispatcher"; -import {ActionPayload} from "../../../dispatcher/payloads"; +import { ActionPayload } from "../../../dispatcher/payloads"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; +import { EchoChamber } from "../../../stores/local-echo/EchoChamber"; +import { CachedRoomKey, RoomCachedEcho } from "../../../stores/local-echo/RoomCachedEcho"; +import { PROPERTY_UPDATED } from "../../../stores/local-echo/CachedEcho"; interface IProps { room: Room; @@ -112,6 +108,7 @@ export default class RoomTile extends React.PureComponent { private dispatcherRef: string; private roomTileRef = createRef(); private notificationState: NotificationState; + private roomProps: RoomCachedEcho; constructor(props: IProps) { super(props); @@ -130,12 +127,19 @@ export default class RoomTile extends React.PureComponent { MessagePreviewStore.instance.on(ROOM_PREVIEW_CHANGED, this.onRoomPreviewChanged); this.notificationState = RoomNotificationStateStore.instance.getRoomState(this.props.room); this.notificationState.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate); + this.roomProps = EchoChamber.forRoom(this.props.room); + this.roomProps.on(PROPERTY_UPDATED, this.onRoomPropertyUpdate); } private onNotificationUpdate = () => { this.forceUpdate(); // notification state changed - update }; + private onRoomPropertyUpdate = (property: CachedRoomKey) => { + if (property === CachedRoomKey.NotificationVolume) this.onNotificationUpdate(); + // else ignore - not important for this tile + }; + private get showContextMenu(): boolean { return !this.props.isMinimized && this.props.tag !== DefaultTagID.Invite; } @@ -307,17 +311,9 @@ export default class RoomTile extends React.PureComponent { ev.stopPropagation(); if (MatrixClientPeg.get().isGuest()) return; - // get key before we go async and React discards the nativeEvent - const key = (ev as React.KeyboardEvent).key; - try { - // TODO add local echo - https://github.com/vector-im/riot-web/issues/14280 - await setRoomNotifsState(this.props.room.roomId, newState); - } catch (error) { - // TODO: some form of error notification to the user to inform them that their state change failed. - // See https://github.com/vector-im/riot-web/issues/14281 - console.error(error); - } + this.roomProps.notificationVolume = newState; + const key = (ev as React.KeyboardEvent).key; if (key === Key.ENTER) { // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12 this.setState({notificationsMenuPosition: null}); // hide the menu @@ -335,7 +331,7 @@ export default class RoomTile extends React.PureComponent { return null; } - const state = getRoomNotifsState(this.props.room.roomId); + const state = this.roomProps.notificationVolume; let contextMenu = null; if (this.state.notificationsMenuPosition) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 18e2da9a31..6433285d20 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -441,6 +441,7 @@ "%(senderName)s: %(message)s": "%(senderName)s: %(message)s", "%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s", "%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s", + "Change notification settings": "Change notification settings", "New spinner design": "New spinner design", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", From 14b0def14332f723348d66051b9792bc3adf617f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 19:11:24 -0600 Subject: [PATCH 090/136] Fix echo handling and show a barebones toast on error The EchoTransaction was wrongly assuming that it knew better than the caller for when the success condition was met, so the echo marking has been left an exercise for the caller. In this case, we mark when we finally receive the sync with the updated rules. We also have to cancel previous transactions otherwise if the user mashes buttons we could forever show the toast, and that would be bad. --- .../toasts/NonUrgentEchoFailureToast.tsx | 30 ++++++++++++++ src/i18n/strings/en_EN.json | 1 + src/stores/local-echo/CachedEcho.ts | 39 +++++++++++++------ src/stores/local-echo/EchoContext.ts | 9 ++++- src/stores/local-echo/EchoStore.ts | 33 +++++++++++++++- src/stores/local-echo/EchoTransaction.ts | 5 +++ src/stores/local-echo/RoomCachedEcho.ts | 3 +- 7 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 src/components/views/toasts/NonUrgentEchoFailureToast.tsx diff --git a/src/components/views/toasts/NonUrgentEchoFailureToast.tsx b/src/components/views/toasts/NonUrgentEchoFailureToast.tsx new file mode 100644 index 0000000000..c9a5037045 --- /dev/null +++ b/src/components/views/toasts/NonUrgentEchoFailureToast.tsx @@ -0,0 +1,30 @@ +/* +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 { _t } from "../../../languageHandler"; + +export default class NonUrgentEchoFailureToast extends React.PureComponent { + render() { + return ( +
+ {_t("Your server isn't responding to some requests", {}, { + 'a': (sub) => {sub} + })} +
+ ) + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6433285d20..a281834628 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -612,6 +612,7 @@ "Headphones": "Headphones", "Folder": "Folder", "Pin": "Pin", + "Your server isn't responding to some requests": "Your server isn't responding to some requests", "From %(deviceName)s (%(deviceId)s)": "From %(deviceName)s (%(deviceId)s)", "Decline (%(counter)s)": "Decline (%(counter)s)", "Accept to continue:": "Accept to continue:", diff --git a/src/stores/local-echo/CachedEcho.ts b/src/stores/local-echo/CachedEcho.ts index caa7ad1d48..2d1f3d8848 100644 --- a/src/stores/local-echo/CachedEcho.ts +++ b/src/stores/local-echo/CachedEcho.ts @@ -15,7 +15,7 @@ limitations under the License. */ import { EchoContext } from "./EchoContext"; -import { RunFn, TransactionStatus } from "./EchoTransaction"; +import { EchoTransaction, RunFn, TransactionStatus } from "./EchoTransaction"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { EventEmitter } from "events"; @@ -26,10 +26,10 @@ export async function implicitlyReverted() { export const PROPERTY_UPDATED = "property_updated"; export abstract class CachedEcho extends EventEmitter { - private cache = new Map(); + private cache = new Map(); protected matrixClient: MatrixClient; - protected constructor(protected context: C, private lookupFn: (key: K) => V) { + protected constructor(public readonly context: C, private lookupFn: (key: K) => V) { super(); } @@ -49,24 +49,39 @@ export abstract class CachedEcho extends EventEmitt * @returns The value for the key. */ public getValue(key: K): V { - return this.cache.has(key) ? this.cache.get(key) : this.lookupFn(key); + return this.cache.has(key) ? this.cache.get(key).val : this.lookupFn(key); } - private cacheVal(key: K, val: V) { - this.cache.set(key, val); + private cacheVal(key: K, val: V, txn: EchoTransaction) { + this.cache.set(key, {txn, val}); this.emit(PROPERTY_UPDATED, key); } private decacheKey(key: K) { - this.cache.delete(key); - this.emit(PROPERTY_UPDATED, key); + if (this.cache.has(key)) { + this.cache.get(key).txn.cancel(); // should be safe to call + this.cache.delete(key); + this.emit(PROPERTY_UPDATED, key); + } + } + + protected markEchoReceived(key: K) { + this.decacheKey(key); } public setValue(auditName: string, key: K, targetVal: V, runFn: RunFn, revertFn: RunFn) { - this.cacheVal(key, targetVal); // set the cache now as it won't be updated by the .when() ladder below. - this.context.beginTransaction(auditName, runFn) - .when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal)) - .whenAnyOf([TransactionStatus.DoneError, TransactionStatus.DoneSuccess], () => this.decacheKey(key)) + // Cancel any pending transactions for the same key + if (this.cache.has(key)) { + this.cache.get(key).txn.cancel(); + } + + const txn = this.context.beginTransaction(auditName, runFn); + this.cacheVal(key, targetVal, txn); // set the cache now as it won't be updated by the .when() ladder below. + + txn.when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal, txn)) + .when(TransactionStatus.DoneError, () => this.decacheKey(key)) .when(TransactionStatus.DoneError, () => revertFn()); + + txn.run(); } } diff --git a/src/stores/local-echo/EchoContext.ts b/src/stores/local-echo/EchoContext.ts index 0d5eb961c3..ffad76b4a6 100644 --- a/src/stores/local-echo/EchoContext.ts +++ b/src/stores/local-echo/EchoContext.ts @@ -27,12 +27,17 @@ export enum ContextTransactionState { export abstract class EchoContext extends Whenable implements IDestroyable { private _transactions: EchoTransaction[] = []; + private _state = ContextTransactionState.NotStarted; public readonly startTime: Date = new Date(); public get transactions(): EchoTransaction[] { return arrayFastClone(this._transactions); } + public get state(): ContextTransactionState { + return this._state; + } + public beginTransaction(auditName: string, runFn: RunFn): EchoTransaction { const txn = new EchoTransaction(auditName, runFn); this._transactions.push(txn); @@ -48,7 +53,7 @@ export abstract class EchoContext extends Whenable impl private checkTransactions = () => { let status = ContextTransactionState.AllSuccessful; for (const txn of this.transactions) { - if (txn.status === TransactionStatus.DoneError) { + if (txn.status === TransactionStatus.DoneError || txn.didPreviouslyFail) { status = ContextTransactionState.PendingErrors; break; } else if (txn.status === TransactionStatus.Pending) { @@ -56,6 +61,7 @@ export abstract class EchoContext extends Whenable impl // no break as we might hit something which broke } } + this._state = status; this.notifyCondition(status); }; @@ -63,6 +69,7 @@ export abstract class EchoContext extends Whenable impl for (const txn of this.transactions) { txn.destroy(); } + this._transactions = []; super.destroy(); } } diff --git a/src/stores/local-echo/EchoStore.ts b/src/stores/local-echo/EchoStore.ts index 80c669e5c6..8514bff731 100644 --- a/src/stores/local-echo/EchoStore.ts +++ b/src/stores/local-echo/EchoStore.ts @@ -22,12 +22,19 @@ import { RoomEchoContext } from "./RoomEchoContext"; import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; import defaultDispatcher from "../../dispatcher/dispatcher"; import { ActionPayload } from "../../dispatcher/payloads"; +import { ContextTransactionState } from "./EchoContext"; +import NonUrgentToastStore, { ToastReference } from "../NonUrgentToastStore"; +import NonUrgentEchoFailureToast from "../../components/views/toasts/NonUrgentEchoFailureToast"; + +interface IState { + toastRef: ToastReference; +} type ContextKey = string; const roomContextKey = (room: Room): ContextKey => `room-${room.roomId}`; -export class EchoStore extends AsyncStoreWithClient { +export class EchoStore extends AsyncStoreWithClient { private static _instance: EchoStore; private caches = new Map>(); @@ -47,13 +54,35 @@ export class EchoStore extends AsyncStoreWithClient { if (this.caches.has(roomContextKey(room))) { return this.caches.get(roomContextKey(room)) as RoomCachedEcho; } - const echo = new RoomCachedEcho(new RoomEchoContext(room)); + + const context = new RoomEchoContext(room); + context.whenAnything(() => this.checkContexts()); + + const echo = new RoomCachedEcho(context); echo.setClient(this.matrixClient); this.caches.set(roomContextKey(room), echo); + return echo; } + private async checkContexts() { + let hasOrHadError = false; + for (const echo of this.caches.values()) { + hasOrHadError = echo.context.state === ContextTransactionState.PendingErrors; + if (hasOrHadError) break; + } + + if (hasOrHadError && !this.state.toastRef) { + const ref = NonUrgentToastStore.instance.addToast(NonUrgentEchoFailureToast); + await this.updateState({toastRef: ref}); + } else if (!hasOrHadError && this.state.toastRef) { + NonUrgentToastStore.instance.removeToast(this.state.toastRef); + await this.updateState({toastRef: null}); + } + } + protected async onReady(): Promise { + if (!this.caches) return; // can only happen during initialization for (const echo of this.caches.values()) { echo.setClient(this.matrixClient); } diff --git a/src/stores/local-echo/EchoTransaction.ts b/src/stores/local-echo/EchoTransaction.ts index b2125aac08..7993a7838b 100644 --- a/src/stores/local-echo/EchoTransaction.ts +++ b/src/stores/local-echo/EchoTransaction.ts @@ -53,6 +53,11 @@ export class EchoTransaction extends Whenable { .catch(() => this.setStatus(TransactionStatus.DoneError)); } + public cancel() { + // Success basically means "done" + this.setStatus(TransactionStatus.DoneSuccess); + } + private setStatus(status: TransactionStatus) { this._status = status; if (status === TransactionStatus.DoneError) { diff --git a/src/stores/local-echo/RoomCachedEcho.ts b/src/stores/local-echo/RoomCachedEcho.ts index 0aec4a4e1c..3ac01d3873 100644 --- a/src/stores/local-echo/RoomCachedEcho.ts +++ b/src/stores/local-echo/RoomCachedEcho.ts @@ -60,6 +60,7 @@ export class RoomCachedEcho extends CachedEcho { - setRoomNotifsState(this.context.room.roomId, v); + return setRoomNotifsState(this.context.room.roomId, v); }, implicitlyReverted); } } From c5574219bba4fbbed4344e0b4d58e9a28139d520 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 20:36:04 -0600 Subject: [PATCH 091/136] Implement dialog for resending local echo transactions --- res/css/_components.scss | 2 + .../views/dialogs/_ServerOfflineDialog.scss | 72 +++++++++++ .../toasts/_NonUrgentEchoFailureToast.scss | 37 ++++++ res/img/element-icons/cloud-off.svg | 3 + .../views/dialogs/ServerOfflineDialog.tsx | 122 ++++++++++++++++++ .../toasts/NonUrgentEchoFailureToast.tsx | 16 ++- src/i18n/strings/en_EN.json | 16 ++- src/stores/local-echo/CachedEcho.ts | 8 +- src/stores/local-echo/EchoContext.ts | 14 +- src/stores/local-echo/EchoStore.ts | 6 +- src/stores/local-echo/EchoTransaction.ts | 2 + 11 files changed, 289 insertions(+), 9 deletions(-) create mode 100644 res/css/views/dialogs/_ServerOfflineDialog.scss create mode 100644 res/css/views/toasts/_NonUrgentEchoFailureToast.scss create mode 100644 res/img/element-icons/cloud-off.svg create mode 100644 src/components/views/dialogs/ServerOfflineDialog.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 28e1332d08..fcc87e2061 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -76,6 +76,7 @@ @import "./views/dialogs/_RoomSettingsDialogBridges.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; +@import "./views/dialogs/_ServerOfflineDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss"; @import "./views/dialogs/_SetMxIdDialog.scss"; @import "./views/dialogs/_SetPasswordDialog.scss"; @@ -216,6 +217,7 @@ @import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss"; @import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss"; @import "./views/terms/_InlineTermsAgreement.scss"; +@import "./views/toasts/_NonUrgentEchoFailureToast.scss"; @import "./views/verification/_VerificationShowSas.scss"; @import "./views/voip/_CallContainer.scss"; @import "./views/voip/_CallView.scss"; diff --git a/res/css/views/dialogs/_ServerOfflineDialog.scss b/res/css/views/dialogs/_ServerOfflineDialog.scss new file mode 100644 index 0000000000..ae4b70beb3 --- /dev/null +++ b/res/css/views/dialogs/_ServerOfflineDialog.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_ServerOfflineDialog { + .mx_ServerOfflineDialog_content { + padding-right: 85px; + color: $primary-fg-color; + + hr { + border-color: $primary-fg-color; + opacity: 0.1; + border-bottom: none; + } + + ul { + padding: 16px; + + li:nth-child(n + 2) { + margin-top: 16px; + } + } + + .mx_ServerOfflineDialog_content_context { + .mx_ServerOfflineDialog_content_context_timestamp { + display: inline-block; + width: 115px; + color: $muted-fg-color; + line-height: 24px; // same as avatar + vertical-align: top; + } + + .mx_ServerOfflineDialog_content_context_timeline { + display: inline-block; + width: calc(100% - 155px); // 115px timestamp width + 40px right margin + + .mx_ServerOfflineDialog_content_context_timeline_header { + span { + margin-left: 8px; + vertical-align: middle; + } + } + + .mx_ServerOfflineDialog_content_context_txn { + position: relative; + margin-top: 8px; + + .mx_ServerOfflineDialog_content_context_txn_desc { + width: calc(100% - 100px); // 100px is an arbitrary margin for the button + } + + .mx_AccessibleButton { + float: right; + padding: 0; + } + } + } + } + } +} diff --git a/res/css/views/toasts/_NonUrgentEchoFailureToast.scss b/res/css/views/toasts/_NonUrgentEchoFailureToast.scss new file mode 100644 index 0000000000..9a8229b38e --- /dev/null +++ b/res/css/views/toasts/_NonUrgentEchoFailureToast.scss @@ -0,0 +1,37 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_NonUrgentEchoFailureToast { + .mx_NonUrgentEchoFailureToast_icon { + display: inline-block; + width: $font-18px; + height: $font-18px; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background-color: #fff; // we know that non-urgent toasts are always styled the same + mask-image: url('$(res)/img/element-icons/cloud-off.svg'); + margin-right: 8px; + } + + span { // includes the i18n block + vertical-align: middle; + } + + .mx_AccessibleButton { + padding: 0; + } +} diff --git a/res/img/element-icons/cloud-off.svg b/res/img/element-icons/cloud-off.svg new file mode 100644 index 0000000000..7faea7d3b5 --- /dev/null +++ b/res/img/element-icons/cloud-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/views/dialogs/ServerOfflineDialog.tsx b/src/components/views/dialogs/ServerOfflineDialog.tsx new file mode 100644 index 0000000000..074ea19d4d --- /dev/null +++ b/src/components/views/dialogs/ServerOfflineDialog.tsx @@ -0,0 +1,122 @@ +/* +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 BaseDialog from './BaseDialog'; +import { _t } from '../../../languageHandler'; +import { EchoStore } from "../../../stores/local-echo/EchoStore"; +import { formatTime } from "../../../DateUtils"; +import SettingsStore from "../../../settings/SettingsStore"; +import { RoomEchoContext } from "../../../stores/local-echo/RoomEchoContext"; +import RoomAvatar from "../avatars/RoomAvatar"; +import { TransactionStatus } from "../../../stores/local-echo/EchoTransaction"; +import Spinner from "../elements/Spinner"; +import AccessibleButton from "../elements/AccessibleButton"; +import { UPDATE_EVENT } from "../../../stores/AsyncStore"; + +interface IProps { + onFinished: (bool) => void; +} + +export default class ServerOfflineDialog extends React.PureComponent { + public componentDidMount() { + EchoStore.instance.on(UPDATE_EVENT, this.onEchosUpdated); + } + + public componentWillUnmount() { + EchoStore.instance.off(UPDATE_EVENT, this.onEchosUpdated); + } + + private onEchosUpdated = () => { + this.forceUpdate(); // no state to worry about + }; + + private renderTimeline(): React.ReactElement[] { + return EchoStore.instance.contexts.map((c, i) => { + if (!c.firstFailedTime) return null; // not useful + if (!(c instanceof RoomEchoContext)) throw new Error("Cannot render unknown context: " + c); + const header = ( +
+ + {c.room.name} +
+ ); + const entries = c.transactions + .filter(t => t.status === TransactionStatus.DoneError || t.didPreviouslyFail) + .map((t, j) => { + let button = ; + if (t.status === TransactionStatus.DoneError) { + button = ( + t.run()}>{_t("Resend")} + ); + } + return ( +
+ + {t.auditName} + + {button} +
+ ); + }); + return ( +
+
+ {formatTime(c.firstFailedTime, SettingsStore.getValue("showTwelveHourTimestamps"))} +
+
+ {header} + {entries} +
+
+ ) + }); + } + + public render() { + let timeline = this.renderTimeline().filter(c => !!c); // remove nulls for next check + if (timeline.length === 0) { + timeline = [
{_t("You're all caught up.")}
]; + } + + return +
+

{_t( + "Your server isn't responding to some of your requests for some reason. " + + "Below are some possible reasons why this happened.", + )}

+
    +
  • {_t("The server took too long to respond.")}
  • +
  • {_t("Your firewall or anti-virus is blocking the request.")}
  • +
  • {_t("A browser extension is preventing the request.")}
  • +
  • {_t("The server is offline.")}
  • +
  • {_t("The server has denied your request.")}
  • +
  • {_t("Your area is experiencing difficulties connecting to the internet.")}
  • +
  • {_t("A connection error occurred while trying to contact the server.")}
  • +
  • {_t("The server is not configured to indicate what the problem is (CORS).")}
  • +
+
+

{_t("Recent changes that have not yet been received")}

+ {timeline} +
+
; + } +} diff --git a/src/components/views/toasts/NonUrgentEchoFailureToast.tsx b/src/components/views/toasts/NonUrgentEchoFailureToast.tsx index c9a5037045..76d0328e8b 100644 --- a/src/components/views/toasts/NonUrgentEchoFailureToast.tsx +++ b/src/components/views/toasts/NonUrgentEchoFailureToast.tsx @@ -16,13 +16,23 @@ limitations under the License. import React from "react"; import { _t } from "../../../languageHandler"; +import AccessibleButton from "../elements/AccessibleButton"; +import Modal from "../../../Modal"; +import ServerOfflineDialog from "../dialogs/ServerOfflineDialog"; export default class NonUrgentEchoFailureToast extends React.PureComponent { - render() { + private openDialog = () => { + Modal.createTrackedDialog('Local Echo Server Error', '', ServerOfflineDialog, {}); + }; + + public render() { return (
- {_t("Your server isn't responding to some requests", {}, { - 'a': (sub) => {sub} + + {_t("Your server isn't responding to some requests.", {}, { + 'a': (sub) => ( + {sub} + ), })}
) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a281834628..d1e940b3be 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -612,7 +612,7 @@ "Headphones": "Headphones", "Folder": "Folder", "Pin": "Pin", - "Your server isn't responding to some requests": "Your server isn't responding to some requests", + "Your server isn't responding to some requests.": "Your server isn't responding to some requests.", "From %(deviceName)s (%(deviceId)s)": "From %(deviceName)s (%(deviceId)s)", "Decline (%(counter)s)": "Decline (%(counter)s)", "Accept to continue:": "Accept to continue:", @@ -1745,6 +1745,19 @@ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.", "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.", "You'll upgrade this room from to .": "You'll upgrade this room from to .", + "Resend": "Resend", + "You're all caught up.": "You're all caught up.", + "Server isn't responding": "Server isn't responding", + "Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.": "Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.", + "The server took too long to respond.": "The server took too long to respond.", + "Your firewall or anti-virus is blocking the request.": "Your firewall or anti-virus is blocking the request.", + "A browser extension is preventing the request.": "A browser extension is preventing the request.", + "The server is offline.": "The server is offline.", + "The server has denied your request.": "The server has denied your request.", + "Your area is experiencing difficulties connecting to the internet.": "Your area is experiencing difficulties connecting to the internet.", + "A connection error occurred while trying to contact the server.": "A connection error occurred while trying to contact the server.", + "The server is not configured to indicate what the problem is (CORS).": "The server is not configured to indicate what the problem is (CORS).", + "Recent changes that have not yet been received": "Recent changes that have not yet been received", "Sign out and remove encryption keys?": "Sign out and remove encryption keys?", "Clear Storage and Sign Out": "Clear Storage and Sign Out", "Send Logs": "Send Logs", @@ -1852,7 +1865,6 @@ "Reject invitation": "Reject invitation", "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", "Unable to reject invite": "Unable to reject invite", - "Resend": "Resend", "Resend edit": "Resend edit", "Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)", "Resend removal": "Resend removal", diff --git a/src/stores/local-echo/CachedEcho.ts b/src/stores/local-echo/CachedEcho.ts index 2d1f3d8848..ce89e639c9 100644 --- a/src/stores/local-echo/CachedEcho.ts +++ b/src/stores/local-echo/CachedEcho.ts @@ -59,13 +59,18 @@ export abstract class CachedEcho extends EventEmitt private decacheKey(key: K) { if (this.cache.has(key)) { - this.cache.get(key).txn.cancel(); // should be safe to call + this.context.disownTransaction(this.cache.get(key).txn); this.cache.delete(key); this.emit(PROPERTY_UPDATED, key); } } protected markEchoReceived(key: K) { + if (this.cache.has(key)) { + const txn = this.cache.get(key).txn; + this.context.disownTransaction(txn); + txn.cancel(); + } this.decacheKey(key); } @@ -79,7 +84,6 @@ export abstract class CachedEcho extends EventEmitt this.cacheVal(key, targetVal, txn); // set the cache now as it won't be updated by the .when() ladder below. txn.when(TransactionStatus.Pending, () => this.cacheVal(key, targetVal, txn)) - .when(TransactionStatus.DoneError, () => this.decacheKey(key)) .when(TransactionStatus.DoneError, () => revertFn()); txn.run(); diff --git a/src/stores/local-echo/EchoContext.ts b/src/stores/local-echo/EchoContext.ts index ffad76b4a6..9ed5cf387f 100644 --- a/src/stores/local-echo/EchoContext.ts +++ b/src/stores/local-echo/EchoContext.ts @@ -28,7 +28,6 @@ export enum ContextTransactionState { export abstract class EchoContext extends Whenable implements IDestroyable { private _transactions: EchoTransaction[] = []; private _state = ContextTransactionState.NotStarted; - public readonly startTime: Date = new Date(); public get transactions(): EchoTransaction[] { return arrayFastClone(this._transactions); @@ -38,6 +37,19 @@ export abstract class EchoContext extends Whenable impl return this._state; } + public get firstFailedTime(): Date { + const failedTxn = this.transactions.find(t => t.didPreviouslyFail || t.status === TransactionStatus.DoneError); + if (failedTxn) return failedTxn.startTime; + return null; + } + + public disownTransaction(txn: EchoTransaction) { + const idx = this._transactions.indexOf(txn); + if (idx >= 0) this._transactions.splice(idx, 1); + txn.destroy(); + this.checkTransactions(); + } + public beginTransaction(auditName: string, runFn: RunFn): EchoTransaction { const txn = new EchoTransaction(auditName, runFn); this._transactions.push(txn); diff --git a/src/stores/local-echo/EchoStore.ts b/src/stores/local-echo/EchoStore.ts index 8514bff731..ccb65fd5d7 100644 --- a/src/stores/local-echo/EchoStore.ts +++ b/src/stores/local-echo/EchoStore.ts @@ -22,7 +22,7 @@ import { RoomEchoContext } from "./RoomEchoContext"; import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; import defaultDispatcher from "../../dispatcher/dispatcher"; import { ActionPayload } from "../../dispatcher/payloads"; -import { ContextTransactionState } from "./EchoContext"; +import { ContextTransactionState, EchoContext } from "./EchoContext"; import NonUrgentToastStore, { ToastReference } from "../NonUrgentToastStore"; import NonUrgentEchoFailureToast from "../../components/views/toasts/NonUrgentEchoFailureToast"; @@ -50,6 +50,10 @@ export class EchoStore extends AsyncStoreWithClient { return EchoStore._instance; } + public get contexts(): EchoContext[] { + return Array.from(this.caches.values()).map(e => e.context); + } + public getOrCreateEchoForRoom(room: Room): RoomCachedEcho { if (this.caches.has(roomContextKey(room))) { return this.caches.get(roomContextKey(room)) as RoomCachedEcho; diff --git a/src/stores/local-echo/EchoTransaction.ts b/src/stores/local-echo/EchoTransaction.ts index 7993a7838b..543c3b4ccd 100644 --- a/src/stores/local-echo/EchoTransaction.ts +++ b/src/stores/local-echo/EchoTransaction.ts @@ -28,6 +28,8 @@ export class EchoTransaction extends Whenable { private _status = TransactionStatus.Pending; private didFail = false; + public readonly startTime = new Date(); + public constructor( public readonly auditName, public runFn: RunFn, From e3765ea8c5ea1d6fb01ec0a8715f8acf46f71a3e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 20:47:32 -0600 Subject: [PATCH 092/136] Appease the linters --- res/css/structures/_NonUrgentToastContainer.scss | 2 +- src/stores/local-echo/EchoStore.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/structures/_NonUrgentToastContainer.scss b/res/css/structures/_NonUrgentToastContainer.scss index 26715c6048..826a812406 100644 --- a/res/css/structures/_NonUrgentToastContainer.scss +++ b/res/css/structures/_NonUrgentToastContainer.scss @@ -29,7 +29,7 @@ limitations under the License. // We don't use variables on the colours because we want it to be the same // in all themes. - background-color: #17191C; + background-color: #17191c; color: #fff; } } diff --git a/src/stores/local-echo/EchoStore.ts b/src/stores/local-echo/EchoStore.ts index ccb65fd5d7..ef2ee2d275 100644 --- a/src/stores/local-echo/EchoStore.ts +++ b/src/stores/local-echo/EchoStore.ts @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventEmitter } from "events"; import { CachedEcho } from "./CachedEcho"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomCachedEcho } from "./RoomCachedEcho"; From 7f404b0fe5760ea642a4416586caec9adbbd7ea4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 29 Jul 2020 21:00:32 -0600 Subject: [PATCH 093/136] Fix levels for settings --- src/settings/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index a012bac257..90ef45f865 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -411,7 +411,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { "blacklistUnverifiedDevices": { // We specifically want to have room-device > device so that users may set a device default // with a per-room override. - supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.ROOM_ACCOUNT], + supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.DEVICE], supportedLevelsAreOrdered: true, displayName: { "default": _td('Never send encrypted messages to unverified sessions from this session'), From 97cef335e8215c26a5df1d28ee6e241f1337ef75 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Thu, 30 Jul 2020 00:28:12 +0530 Subject: [PATCH 094/136] Cleanup types --- src/components/structures/{RightPanel.js => RightPanel.tsx} | 0 src/components/views/right_panel/HeaderButtons.tsx | 3 ++- src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts | 2 ++ src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) rename src/components/structures/{RightPanel.js => RightPanel.tsx} (100%) diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.tsx similarity index 100% rename from src/components/structures/RightPanel.js rename to src/components/structures/RightPanel.tsx diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 8141e8a9cc..499fbbd414 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -24,6 +24,7 @@ import RightPanelStore from "../../../stores/RightPanelStore"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from '../../../dispatcher/actions'; import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; +import {EventSubscription} from "fbemitter"; export enum HeaderKind { Room = "room", @@ -38,7 +39,7 @@ interface IState { interface IProps {} export default class HeaderButtons extends React.Component { - private storeToken: ReturnType; + private storeToken: EventSubscription; private dispatcherRef: string; constructor(props: IProps, kind: HeaderKind) { diff --git a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts index 3193f9043b..cfd4a2d3cc 100644 --- a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts +++ b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts @@ -18,10 +18,12 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { SetRightPanelPhaseRefireParams } from "./SetRightPanelPhasePayload"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; interface AfterRightPanelPhaseChangeAction extends ActionPayload { action: Action.AfterRightPanelPhaseChange; phase: RightPanelPhases; + verificationRequestPromise?: Promise; } export type AfterRightPanelPhaseChangePayload diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index b7dd5d85fc..75dea9f3df 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -24,7 +24,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload { action: Action.SetRightPanelPhase; phase: RightPanelPhases; - refireParams: SetRightPanelPhaseRefireParams; + refireParams?: SetRightPanelPhaseRefireParams; } export interface SetRightPanelPhaseRefireParams { From 9aa128a6e8075a7e27548a075c84e3f8a8e91da4 Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Thu, 30 Jul 2020 11:45:49 +0530 Subject: [PATCH 095/136] Revert "Cleanup types" This reverts commit 97cef335e8215c26a5df1d28ee6e241f1337ef75. --- src/components/structures/{RightPanel.tsx => RightPanel.js} | 0 src/components/views/right_panel/HeaderButtons.tsx | 3 +-- src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts | 2 -- src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) rename src/components/structures/{RightPanel.tsx => RightPanel.js} (100%) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.js similarity index 100% rename from src/components/structures/RightPanel.tsx rename to src/components/structures/RightPanel.js diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 499fbbd414..8141e8a9cc 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -24,7 +24,6 @@ import RightPanelStore from "../../../stores/RightPanelStore"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from '../../../dispatcher/actions'; import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; -import {EventSubscription} from "fbemitter"; export enum HeaderKind { Room = "room", @@ -39,7 +38,7 @@ interface IState { interface IProps {} export default class HeaderButtons extends React.Component { - private storeToken: EventSubscription; + private storeToken: ReturnType; private dispatcherRef: string; constructor(props: IProps, kind: HeaderKind) { diff --git a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts index cfd4a2d3cc..3193f9043b 100644 --- a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts +++ b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts @@ -18,12 +18,10 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { SetRightPanelPhaseRefireParams } from "./SetRightPanelPhasePayload"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; interface AfterRightPanelPhaseChangeAction extends ActionPayload { action: Action.AfterRightPanelPhaseChange; phase: RightPanelPhases; - verificationRequestPromise?: Promise; } export type AfterRightPanelPhaseChangePayload diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index 75dea9f3df..b7dd5d85fc 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -24,7 +24,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload { action: Action.SetRightPanelPhase; phase: RightPanelPhases; - refireParams?: SetRightPanelPhaseRefireParams; + refireParams: SetRightPanelPhaseRefireParams; } export interface SetRightPanelPhaseRefireParams { From fbc341a2f5b43459b8e200d0f7edf92c8a343b6e Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Thu, 30 Jul 2020 11:51:10 +0530 Subject: [PATCH 096/136] Clean up types properly --- src/components/views/right_panel/HeaderButtons.tsx | 3 ++- src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts | 2 ++ src/dispatcher/payloads/SetRightPanelPhasePayload.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 8141e8a9cc..499fbbd414 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -24,6 +24,7 @@ import RightPanelStore from "../../../stores/RightPanelStore"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {Action} from '../../../dispatcher/actions'; import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; +import {EventSubscription} from "fbemitter"; export enum HeaderKind { Room = "room", @@ -38,7 +39,7 @@ interface IState { interface IProps {} export default class HeaderButtons extends React.Component { - private storeToken: ReturnType; + private storeToken: EventSubscription; private dispatcherRef: string; constructor(props: IProps, kind: HeaderKind) { diff --git a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts index 3193f9043b..cfd4a2d3cc 100644 --- a/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts +++ b/src/dispatcher/payloads/AfterRightPanelPhaseChangePayload.ts @@ -18,10 +18,12 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { SetRightPanelPhaseRefireParams } from "./SetRightPanelPhasePayload"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; interface AfterRightPanelPhaseChangeAction extends ActionPayload { action: Action.AfterRightPanelPhaseChange; phase: RightPanelPhases; + verificationRequestPromise?: Promise; } export type AfterRightPanelPhaseChangePayload diff --git a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts index b7dd5d85fc..75dea9f3df 100644 --- a/src/dispatcher/payloads/SetRightPanelPhasePayload.ts +++ b/src/dispatcher/payloads/SetRightPanelPhasePayload.ts @@ -24,7 +24,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload { action: Action.SetRightPanelPhase; phase: RightPanelPhases; - refireParams: SetRightPanelPhaseRefireParams; + refireParams?: SetRightPanelPhaseRefireParams; } export interface SetRightPanelPhaseRefireParams { From bb685cd37a50cbafe644c25842228b02458bef0f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 30 Jul 2020 10:33:36 +0200 Subject: [PATCH 097/136] change message priority --- src/stores/RoomViewStore.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 4f560e1fab..3c37a31174 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -265,7 +265,14 @@ class RoomViewStore extends Store { }); let msg = err.message ? err.message : JSON.stringify(err); console.log("Failed to join room:", msg); - if (err.httpStatus === 404) { + if (err.name === "ConnectionError") { + msg = _t("There was an error joining the room"); + } else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { + msg =
+ {_t("Sorry, your homeserver is too old to participate in this room.")}
+ {_t("Please contact your homeserver administrator.")} +
; + } else if (err.httpStatus === 404) { const invitingUserId = this._getInvitingUserId(this._state.roomId); // only provide a better error message for invites if (invitingUserId) { @@ -276,13 +283,6 @@ class RoomViewStore extends Store { msg = _t("The person who invited you already left the room, or their server is offline."); } } - } else if (err.name === "ConnectionError") { - msg = _t("There was an error joining the room"); - } else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { - msg =
- {_t("Sorry, your homeserver is too old to participate in this room.")}
- {_t("Please contact your homeserver administrator.")} -
; } const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, { From a281c617beb24293b40e8e65f5012ee5266f8e91 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 30 Jul 2020 10:42:46 +0200 Subject: [PATCH 098/136] string order changed --- src/i18n/strings/en_EN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ad33823c96..644cd03daf 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -423,11 +423,11 @@ "Upgrade your %(brand)s": "Upgrade your %(brand)s", "A new version of %(brand)s is available!": "A new version of %(brand)s is available!", "Guest": "Guest", - "The person who invited you already left the room.": "The person who invited you already left the room.", - "The person who invited you already left the room, or their server is offline.": "The person who invited you already left the room, or their server is offline.", "There was an error joining the room": "There was an error joining the room", "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", + "The person who invited you already left the room.": "The person who invited you already left the room.", + "The person who invited you already left the room, or their server is offline.": "The person who invited you already left the room, or their server is offline.", "Failed to join room": "Failed to join room", "You joined the call": "You joined the call", "%(senderName)s joined the call": "%(senderName)s joined the call", From 9c2470659b2c25f7b80ebf66aef96457fd76d730 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Wed, 29 Jul 2020 18:57:58 +0000 Subject: [PATCH 099/136] Translated using Weblate (Ukrainian) Currently translated at 46.7% (1084 of 2322 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 128 +++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 47 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 09b89c26a6..a6c510d616 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -78,7 +78,7 @@ "Unpin Message": "Відкріпити повідомлення", "Register": "Зареєструватися", "Rooms": "Кімнати", - "Add rooms to this community": "Добавити кімнати в це суспільство", + "Add rooms to this community": "Додати кімнати в цю спільноту", "This email address is already in use": "Ця е-пошта вже використовується", "This phone number is already in use": "Цей телефонний номер вже використовується", "Fetching third party location failed": "Не вдалось отримати стороннє місцеперебування", @@ -240,31 +240,31 @@ "You cannot place a call with yourself.": "Ви не можете подзвонити самим собі.", "Warning!": "Увага!", "Upload Failed": "Помилка відвантаження", - "Sun": "Нд", - "Mon": "Пн", - "Tue": "Вт", - "Wed": "Ср", - "Thu": "Чт", - "Fri": "Пт", - "Sat": "Сб", - "Jan": "Січ", - "Feb": "Лют", - "Mar": "Бер", - "Apr": "Квіт", - "May": "Трав", - "Jun": "Чер", - "Jul": "Лип", - "Aug": "Сер", - "Sep": "Вер", - "Oct": "Жов", - "Nov": "Лис", - "Dec": "Гру", - "PM": "PM", - "AM": "AM", + "Sun": "нд", + "Mon": "пн", + "Tue": "вт", + "Wed": "ср", + "Thu": "чт", + "Fri": "пт", + "Sat": "сб", + "Jan": "січ.", + "Feb": "лют.", + "Mar": "бер.", + "Apr": "квіт.", + "May": "трав.", + "Jun": "черв.", + "Jul": "лип.", + "Aug": "серп.", + "Sep": "вер.", + "Oct": "жовт.", + "Nov": "лист.", + "Dec": "груд.", + "PM": "пп", + "AM": "дп", "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", - "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(time)s", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(fullYear)s", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s %(time)s", "Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?", "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Там, де ця сторінка містить ототожненну інформацію, як-от назва кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.", "Call in Progress": "Іде виклик", @@ -321,8 +321,8 @@ "Define the power level of a user": "Вказати рівень повноважень користувача", "Deops user with given id": "Знімає права оператора з користувача з вказаним ідентифікатором", "Opens the Developer Tools dialog": "Відкриває вікно інструментів розробника", - "Verified key": "Перевірений ключ", - "Displays action": "Показує дію", + "Verified key": "Звірений ключ", + "Displays action": "Відбиває дію", "Reason": "Причина", "%(senderName)s requested a VoIP conference.": "%(senderName)s бажає розпочати дзвінок-конференцію.", "%(senderName)s invited %(targetName)s.": "%(senderName)s запросив/ла %(targetName)s.", @@ -403,7 +403,7 @@ "Password": "Пароль", "New Password": "Новий пароль", "Confirm password": "Підтвердження пароля", - "Last seen": "Востаннє з'являвся", + "Last seen": "Востаннє в мережі", "Failed to set display name": "Не вдалося встановити ім'я для показу", "The maximum permitted number of widgets have already been added to this room.": "Максимально дозволену кількість віджетів уже додано до цієї кімнати.", "Drop File Here": "Киньте файл сюди", @@ -474,8 +474,8 @@ "Adds a custom widget by URL to the room": "Додає власний віджет до кімнати за посиланням", "Please supply a https:// or http:// widget URL": "Вкажіть посилання на віджет — https:// або http://", "You cannot modify widgets in this room.": "Ви не можете змінювати віджети у цій кімнаті.", - "Forces the current outbound group session in an encrypted room to be discarded": "Примусово відкидає поточний вихідний груповий сеанс у шифрованій кімнаті", - "Sends the given message coloured as a rainbow": "Надсилає вказане повідомлення розфарбоване веселкою", + "Forces the current outbound group session in an encrypted room to be discarded": "Примусово відкидає поточний вихідний груповий сеанс у зашифрованій кімнаті", + "Sends the given message coloured as a rainbow": "Надсилає вказане повідомлення, розфарбоване веселкою", "Your %(brand)s is misconfigured": "Ваш %(brand)s налаштовано неправильно", "Join the discussion": "Приєднатися до обговорення", "Upload": "Обрати", @@ -645,22 +645,22 @@ "Command failed": "Не вдалося виконати команду", "Could not find user in room": "Не вдалося знайти користувача в кімнаті", "Please supply a widget URL or embed code": "Вкажіть URL або код вставки віджету", - "Verifies a user, session, and pubkey tuple": "Перевіряє користувача, сесію та публічні ключі", - "Unknown (user, session) pair:": "Невідома (користувач:сесія) пара:", - "Session already verified!": "Сесія вже підтверджена!", - "WARNING: Session already verified, but keys do NOT MATCH!": "УВАГА: Сесія вже підтверджена, проте ключі НЕ ЗБІГАЮТЬСЯ!", - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: НЕ ВДАЛОСЯ ЗДІЙСНИТИ ПЕРЕВІРКУ КЛЮЧА! Ключом для %(userId)s та сесії %(devicerId)s являється \"%(fprint)s\", що не відповідає вказаному ключу \"%(fingerprint)s\". Це може означати, що ваші повідомлення перехоплюються!", - "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Ключ підпису, який ви надали, відповідає ключу підпису, який ви отримали від %(userId)s's сесії %(deviceId)s. Сесія відзначена як підтверджена.", - "Sends the given emote coloured as a rainbow": "Надсилає заданий смайлик, пофарбований у вигляді веселки", - "Displays list of commands with usages and descriptions": "Відображає список команд із описом та користуванням", - "Displays information about a user": "Показати інформацію про користувача", - "Send a bug report with logs": "Відправити звіт про помилку з логами", - "Opens chat with the given user": "Відкрити чат з даним користувачем", - "Sends a message to the given user": "Відправити повідомлення даному користувачу", + "Verifies a user, session, and pubkey tuple": "Звіряє користувача, сеанс та кортеж відкритого ключа", + "Unknown (user, session) pair:": "Невідома пара (користувача, сеансу):", + "Session already verified!": "Сеанс вже підтверджений!", + "WARNING: Session already verified, but keys do NOT MATCH!": "УВАГА: Сеанс вже підтверджений, проте ключі НЕ ЗБІГАЮТЬСЯ!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: НЕ ВДАЛОСЯ ЗДІЙСНИТИ ЗВІРЯННЯ КЛЮЧА! Ключем для %(userId)s та сеансу %(deviceId)s є \"%(fprint)s\", що не відповідає наданому ключу \"%(fingerprint)s\". Це може означати, що ваші повідомлення перехоплюють!", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Наданий вами ключ підпису збігається з ключем підпису, що ви отримали від сеансу %(deviceId)s %(userId)s. Сеанс позначено як звірений.", + "Sends the given emote coloured as a rainbow": "Надсилає вказаний смайлик, розфарбований веселкою", + "Displays list of commands with usages and descriptions": "Відбиває перелік команд із прикладами вжитку та описом", + "Displays information about a user": "Відбиває інформацію про користувача", + "Send a bug report with logs": "Надіслати звіт про ваду разом з журналами", + "Opens chat with the given user": "Відкриває балачку з вказаним користувачем", + "Sends a message to the given user": "Надсилає повідомлення вказаному користувачеві", "%(senderName)s made no change.": "%(senderName)s не вніс змін.", "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s змінив(ла) назву кімнати з %(oldRoomName)s на %(newRoomName)s.", "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s модернізував цю кімнату.", - "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s робить кімнату публічною для всіх, хто знає посилання.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s зробив(-ла) кімнату відкритою для всіх, хто знає посилання.", "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s робить кімнату доступною лише по запрошенню.", "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s змінює правило входу на \"%(rule)s\"", "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s дозволяє гостям приєднуватися до кімнати.", @@ -765,7 +765,7 @@ "Review where you’re logged in": "Перевірте, де ви ввійшли", "Your homeserver has exceeded its user limit.": "Ваш домашній сервер перевищив свій ліміт користувачів.", "Your homeserver has exceeded one of its resource limits.": "Ваш домашній сервер перевищив один із своїх ресурсних лімітів.", - "Contact your server admin.": "Зверніться до адміністратора серверу.", + "Contact your server admin.": "Зверніться до адміністратора серверу.", "Ok": "Гаразд", "Set password": "Встановити пароль", "Set up encryption": "Налаштування шифрування", @@ -882,7 +882,7 @@ "Enable 'Manage Integrations' in Settings to do this.": "Щоб зробити це увімкніть \"Керувати інтеграціями\" у налаштуваннях.", "Confirm by comparing the following with the User Settings in your other session:": "Підтвердьте шляхом порівняння наступного рядка з рядком у користувацьких налаштуваннях вашого іншого сеансу:", "Confirm this user's session by comparing the following with their User Settings:": "Підтвердьте сеанс цього користувача шляхом порівняння наступного рядка з рядком з їхніх користувацьких налаштувань:", - "We recommend you change your password and recovery key in Settings immediately": "Ми радимо невідкладно змінити ваші пароль та ключ відновлення у налаштуваннях", + "We recommend you change your password and recovery key in Settings immediately": "Ми радимо невідкладно змінити ваші пароль та відновлювальний ключ у налаштуваннях", "Share Message": "Поширити повідомлення", "Community Settings": "Налаштування спільноти", "All settings": "Усі налаштування", @@ -968,7 +968,7 @@ "Cross-signing and secret storage are enabled.": "Кросс-підпис та секретне сховище дозволені.", "well formed": "добре сформований", "unexpected type": "несподіваний тип", - "Cross-signing public keys:": "Публічні ключі для кросс-підпису:", + "Cross-signing public keys:": "Перехресно-підписувальні відкриті ключі:", "in memory": "у пам'яті", "not found": "не знайдено", "Cross-signing private keys:": "Приватні ключі для кросс-підпису:", @@ -1059,5 +1059,39 @@ "Feedback": "Зворотній зв'язок", "General failure": "Загальний збій", "Enter your account password to confirm the upgrade:": "Введіть пароль вашого облікового запису щоб підтвердити поліпшення:", - "Security & privacy": "Безпека та конфіденційність" + "Security & privacy": "Безпека та конфіденційність", + "Secret storage public key:": "Таємне сховище відкритого ключа:", + "Key backup": "Резервне копіювання ключів", + "Message search": "Пошук повідомлень", + "Cross-signing": "Перехресне підписування", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Адміністратором вашого сервера було вимкнено початкове наскрізне шифрування у закритих кімнатах та особистих повідомленнях.", + "Something went wrong!": "Щось пішло не так!", + "expand": "розгорнути", + "Wrong Recovery Key": "Неправильний відновлювальний ключ", + "Invalid Recovery Key": "Нечинний відновлювальний ключ", + "Recovery key mismatch": "Незбіг відновлювального ключа", + "Backup could not be decrypted with this recovery key: please verify that you entered the correct recovery key.": "Резервна копія не може бути дешифрована з цим відновлювальним ключем: переконайтесь, будь ласка, що ви ввели правильний відновлювальний ключ.", + "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Якщо ви забули відновлювальну парольну фразу, ви можете скористатись вашим відновлювальним ключем або налаштувати нові параметри відновлювання", + "Enter recovery key": "Введіть відновлювальний ключ", + "This looks like a valid recovery key!": "Це скидається на чинний відновлювальний ключ!", + "Not a valid recovery key": "Нечинний відновлювальний ключ", + "Access your secure message history and set up secure messaging by entering your recovery key.": "Доступіться до вашої захищеної історії повідомлень та налаштуйте захищене листування шляхом вводження вашого відновлювального ключа.", + "If you've forgotten your recovery key you can ": "Якщо ви забули ваш відновлювальний ключ, ви можете ", + "Switch to light mode": "Світла тема", + "Switch to dark mode": "Темна тема", + "Use Recovery Key or Passphrase": "Скористуйтесь відновлювальними ключем або парольною фразою", + "Use Recovery Key": "Скористуйтесь відновлювальним ключем", + "Set up with a recovery key": "Налаштувати з відновлювальним ключем", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your recovery passphrase.": "Ваш відновлювальний ключ — це убезпека. Ви можете використовувати його щоб доступитись до ваших зашифрованих повідомлень у разі втрати вашої відновлювальної парольної фрази.", + "Your recovery key": "Ваш відновлювальний ключ", + "Your recovery key has been copied to your clipboard, paste it to:": "Ваш відновлювальний ключ було скопійовано до буферу обміну, вставте його у:", + "Your recovery key is in your Downloads folder.": "Ваш відновлювальний ключ у вашій теці Завантаження.", + "Make a copy of your recovery key": "Зробити копію вашого відновлювального ключа", + "Don't ask again": "Не запитувати знову", + "New Recovery Method": "Новий відновлювальний засіб", + "A new recovery passphrase and key for Secure Messages have been detected.": "Було виявлено нові відновлювальні парольну фразу та ключ від захищених повідомлень.", + "This session is encrypting history using the new recovery method.": "Цей сеанс зашифровує історію новим відновлювальним засобом.", + "Set up Secure Messages": "Налаштувати захищені повідомлення", + "Recovery Method Removed": "Відновлювальний засіб було видалено", + "This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Цей сеанс виявив, що ваші відновлювальні парольна фраза та ключ від захищених повідомлень були видалені." } From d0e269511401cae0c7908bf07b5842f0fa8beb5e Mon Sep 17 00:00:00 2001 From: Swapnil Raj Date: Thu, 30 Jul 2020 15:58:07 +0530 Subject: [PATCH 100/136] Add access specifier to class methods --- .../views/right_panel/GroupHeaderButtons.tsx | 14 +++++++------- .../views/right_panel/HeaderButton.tsx | 4 ++-- .../views/right_panel/HeaderButtons.tsx | 16 ++++++++-------- .../views/right_panel/RoomHeaderButtons.tsx | 4 ++-- .../views/right_panel/VerificationPanel.tsx | 14 +++++++------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/components/views/right_panel/GroupHeaderButtons.tsx b/src/components/views/right_panel/GroupHeaderButtons.tsx index b15a93e5a0..0bbbea78e3 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.tsx +++ b/src/components/views/right_panel/GroupHeaderButtons.tsx @@ -41,11 +41,11 @@ interface IProps {} export default class GroupHeaderButtons extends HeaderButtons { constructor(props: IProps) { super(props, HeaderKind.Group); - this._onMembersClicked = this._onMembersClicked.bind(this); - this._onRoomsClicked = this._onRoomsClicked.bind(this); + this.onMembersClicked = this.onMembersClicked.bind(this); + this.onRoomsClicked = this.onRoomsClicked.bind(this); } - onAction(payload: ActionPayload) { + public onAction(payload: ActionPayload) { super.onAction(payload); if (payload.action === Action.ViewUser) { @@ -70,7 +70,7 @@ export default class GroupHeaderButtons extends HeaderButtons { } } - _onMembersClicked() { + private onMembersClicked() { if (this.state.phase === RightPanelPhases.GroupMemberInfo) { // send the active phase to trigger a toggle this.setPhase(RightPanelPhases.GroupMemberInfo); @@ -80,7 +80,7 @@ export default class GroupHeaderButtons extends HeaderButtons { } } - _onRoomsClicked() { + private onRoomsClicked() { // This toggles for us, if needed this.setPhase(RightPanelPhases.GroupRoomList); } @@ -90,13 +90,13 @@ export default class GroupHeaderButtons extends HeaderButtons { , , ]; diff --git a/src/components/views/right_panel/HeaderButton.tsx b/src/components/views/right_panel/HeaderButton.tsx index 022a993c65..c7cd064184 100644 --- a/src/components/views/right_panel/HeaderButton.tsx +++ b/src/components/views/right_panel/HeaderButton.tsx @@ -45,12 +45,12 @@ export default class HeaderButton extends React.Component { this.onClick = this.onClick.bind(this); } - onClick(_ev: React.KeyboardEvent) { + private onClick() { Analytics.trackEvent(...this.props.analytics); this.props.onClick(); } - render() { + public render() { const classes = classNames({ mx_RightPanel_headerButton: true, mx_RightPanel_headerButton_highlight: this.props.isHighlighted, diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 499fbbd414..8239a52dea 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -52,21 +52,21 @@ export default class HeaderButtons extends React.Component { }; } - componentDidMount() { + public componentDidMount() { this.storeToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelUpdate.bind(this)); this.dispatcherRef = dis.register(this.onAction.bind(this)); // used by subclasses } - componentWillUnmount() { + public componentWillUnmount() { if (this.storeToken) this.storeToken.remove(); if (this.dispatcherRef) dis.unregister(this.dispatcherRef); } - onAction(payload) { + public onAction(payload) { // Ignore - intended to be overridden by subclasses } - setPhase(phase: RightPanelPhases, extras?: Partial) { + public setPhase(phase: RightPanelPhases, extras?: Partial) { dis.dispatch({ action: Action.SetRightPanelPhase, phase: phase, @@ -74,7 +74,7 @@ export default class HeaderButtons extends React.Component { }); } - isPhase(phases: string | string[]) { + public isPhase(phases: string | string[]) { if (Array.isArray(phases)) { return phases.includes(this.state.phase); } else { @@ -82,7 +82,7 @@ export default class HeaderButtons extends React.Component { } } - onRightPanelUpdate() { + private onRightPanelUpdate() { const rps = RightPanelStore.getSharedInstance(); if (this.state.headerKind === HeaderKind.Room) { this.setState({phase: rps.visibleRoomPanelPhase}); @@ -92,7 +92,7 @@ export default class HeaderButtons extends React.Component { } // XXX: Make renderButtons a prop - renderButtons(): JSX.Element[] { + public renderButtons(): JSX.Element[] { // Ignore - intended to be overridden by subclasses // Return empty fragment to satisfy the type return [ @@ -101,7 +101,7 @@ export default class HeaderButtons extends React.Component { ]; } - render() { + public render() { // inline style as this will be swapped around in future commits return
{this.renderButtons()} diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 1b3117d67e..636e29443a 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -41,7 +41,7 @@ export default class RoomHeaderButtons extends HeaderButtons { this.onNotificationsClicked = this.onNotificationsClicked.bind(this); } - onAction(payload: ActionPayload) { + public onAction(payload: ActionPayload) { super.onAction(payload); if (payload.action === Action.ViewUser) { if (payload.member) { @@ -79,7 +79,7 @@ export default class RoomHeaderButtons extends HeaderButtons { this.setPhase(RightPanelPhases.NotificationPanel); } - renderButtons() { + public renderButtons() { return [ ; } - renderVerifiedPhase() { + private renderVerifiedPhase() { const {member, request} = this.props; let text: string; @@ -281,7 +281,7 @@ export default class VerificationPanel extends React.PureComponent Date: Thu, 30 Jul 2020 16:13:13 +0530 Subject: [PATCH 101/136] Change public to protected for onAction --- src/components/views/right_panel/GroupHeaderButtons.tsx | 2 +- src/components/views/right_panel/HeaderButtons.tsx | 2 +- src/components/views/right_panel/RoomHeaderButtons.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/right_panel/GroupHeaderButtons.tsx b/src/components/views/right_panel/GroupHeaderButtons.tsx index 0bbbea78e3..44237e401f 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.tsx +++ b/src/components/views/right_panel/GroupHeaderButtons.tsx @@ -45,7 +45,7 @@ export default class GroupHeaderButtons extends HeaderButtons { this.onRoomsClicked = this.onRoomsClicked.bind(this); } - public onAction(payload: ActionPayload) { + protected onAction(payload: ActionPayload) { super.onAction(payload); if (payload.action === Action.ViewUser) { diff --git a/src/components/views/right_panel/HeaderButtons.tsx b/src/components/views/right_panel/HeaderButtons.tsx index 8239a52dea..57d3075739 100644 --- a/src/components/views/right_panel/HeaderButtons.tsx +++ b/src/components/views/right_panel/HeaderButtons.tsx @@ -62,7 +62,7 @@ export default class HeaderButtons extends React.Component { if (this.dispatcherRef) dis.unregister(this.dispatcherRef); } - public onAction(payload) { + protected onAction(payload) { // Ignore - intended to be overridden by subclasses } diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 636e29443a..7ac547f499 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -41,7 +41,7 @@ export default class RoomHeaderButtons extends HeaderButtons { this.onNotificationsClicked = this.onNotificationsClicked.bind(this); } - public onAction(payload: ActionPayload) { + protected onAction(payload: ActionPayload) { super.onAction(payload); if (payload.action === Action.ViewUser) { if (payload.member) { From c8807591013c0f3a36e83da8156e2d0a1f0332f1 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Thu, 30 Jul 2020 11:06:15 +0000 Subject: [PATCH 102/136] Translated using Weblate (Albanian) Currently translated at 99.8% (2320 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 08deec1269..3750d4d768 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2387,5 +2387,7 @@ "%(brand)s iOS": "%(brand)s iOS", "%(brand)s X for Android": "%(brand)s X për Android", "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", - "Custom Tag": "Etiketë Vetjake" + "Custom Tag": "Etiketë Vetjake", + "The person who invited you already left the room.": "Personi që ju ftoi ka dalë nga dhoma tashmë.", + "The person who invited you already left the room, or their server is offline.": "Personi që ju ftoi, ka dalë nga dhoma tashmë, ose shërbyesi i tij është jashtë funksionimi." } From 34312d41446109e65543f3a500db4200e2632df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 30 Jul 2020 12:14:46 +0000 Subject: [PATCH 103/136] Translated using Weblate (Estonian) Currently translated at 99.8% (2319 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index cdb60979ad..cf441cd241 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -2383,5 +2383,7 @@ "Did you know: you can use communities to filter your %(brand)s experience!": "Kas sa teadsid, et sa võid %(brand)s'i parema kasutuskogemuse nimel pruukida kogukondi!", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Kui sa pole seadistanud krüptitud sõnumite taastamise meetodeid, siis väljalogimisel või muu sessiooni kasutamisel sa kaotad võimaluse oma krüptitud sõnumeid lugeda.", "Set up Secure Message Recovery": "Võta kasutusele turvaline sõnumivõtmete varundus", - "Secure your backup with a recovery passphrase": "Krüpti oma varukoopia taastamiseks mõeldud paroolifraasiga" + "Secure your backup with a recovery passphrase": "Krüpti oma varukoopia taastamiseks mõeldud paroolifraasiga", + "The person who invited you already left the room.": "See, kes sind jututoa liikmeks kutsus, on juba jututoast lahkunud.", + "The person who invited you already left the room, or their server is offline.": "See, kes sind jututoa liikmeks kutsus, kas juba on jututoast lahkunud või tema koduserver on võrgust väljas." } From 86107602888bcc86f769caa77478c411cda7c97f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:52:17 +0000 Subject: [PATCH 104/136] Bump elliptic from 6.5.2 to 6.5.3 Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7ab5e24145..8e3fe342b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3630,9 +3630,9 @@ electron-to-chromium@^1.3.413: integrity sha512-Oo+0+CN9d2z6FToQW6Hwvi9ez09Y/usKwr0tsDsyg43a871zVJCi1nR0v03djLbRNcaCKjtrnVf2XJhTxEpPCg== elliptic@^6.0.0, elliptic@^6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" - integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" From ad7c94917d5fb7a413257a8c22417348e40a57b1 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:41:51 -0600 Subject: [PATCH 105/136] Fix calculatedAtLevel definition --- src/settings/SettingsStore.ts | 2 +- src/settings/controllers/NotificationControllers.ts | 7 ++++++- src/settings/controllers/SettingController.ts | 9 +++++++-- src/settings/controllers/ThemeController.ts | 7 ++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index 25ed99477f..aeed3321b6 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -402,7 +402,7 @@ export default class SettingsStore { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: any, + calculatedAtLevel: SettingLevel, ): any { let resultingValue = calculatedValue; diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index ff38f41b7e..4a8a70362d 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -46,7 +46,12 @@ function getNotifier(): any { // TODO: [TS] Formal type that doesn't cause a cyc } export class NotificationsEnabledController extends SettingController { - public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { + public getValueOverride( + level: SettingLevel, + roomId: string, + calculatedValue: any, + calculatedAtLevel: SettingLevel, + ): any { if (!getNotifier().isPossible()) return false; if (calculatedValue === null || calculatedAtLevel === "default") { diff --git a/src/settings/controllers/SettingController.ts b/src/settings/controllers/SettingController.ts index db7c10793e..d90eba1e9e 100644 --- a/src/settings/controllers/SettingController.ts +++ b/src/settings/controllers/SettingController.ts @@ -33,11 +33,16 @@ export default abstract class SettingController { * @param {String} roomId The room ID, may be null. * @param {*} calculatedValue The value that the handlers think the setting should be, * may be null. - * @param {string} calculatedAtLevel The level for which the calculated value was + * @param {SettingLevel} calculatedAtLevel The level for which the calculated value was * calculated at. May be null. * @return {*} The value that should be used, or null if no override is applicable. */ - public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { + public getValueOverride( + level: SettingLevel, + roomId: string, + calculatedValue: any, + calculatedAtLevel: SettingLevel, + ): any { return null; // no override } diff --git a/src/settings/controllers/ThemeController.ts b/src/settings/controllers/ThemeController.ts index 01082a43a4..15dd64c901 100644 --- a/src/settings/controllers/ThemeController.ts +++ b/src/settings/controllers/ThemeController.ts @@ -22,7 +22,12 @@ import { SettingLevel } from "../SettingLevel"; export default class ThemeController extends SettingController { public static isLogin = false; - public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: any): any { + public getValueOverride( + level: SettingLevel, + roomId: string, + calculatedValue: any, + calculatedAtLevel: SettingLevel, + ): any { if (!calculatedValue) return null; // Don't override null themes if (ThemeController.isLogin) return 'light'; From 8cdb98300bab2d195ae5f211e9af268577b537f0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:42:01 -0600 Subject: [PATCH 106/136] Ensure methods are public --- src/settings/controllers/SystemFontController.ts | 2 +- src/settings/controllers/UseSystemFontController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/controllers/SystemFontController.ts b/src/settings/controllers/SystemFontController.ts index 4f591efc17..e31077e4c8 100644 --- a/src/settings/controllers/SystemFontController.ts +++ b/src/settings/controllers/SystemFontController.ts @@ -25,7 +25,7 @@ export default class SystemFontController extends SettingController { super(); } - onChange(level, roomId, newValue) { + public onChange(level, roomId, newValue) { // Dispatch font size change so that everything open responds to the change. dis.dispatch({ action: Action.UpdateSystemFont, diff --git a/src/settings/controllers/UseSystemFontController.ts b/src/settings/controllers/UseSystemFontController.ts index d598b25962..07a80c7d38 100644 --- a/src/settings/controllers/UseSystemFontController.ts +++ b/src/settings/controllers/UseSystemFontController.ts @@ -25,7 +25,7 @@ export default class UseSystemFontController extends SettingController { super(); } - onChange(level, roomId, newValue) { + public onChange(level, roomId, newValue) { // Dispatch font size change so that everything open responds to the change. dis.dispatch({ action: Action.UpdateSystemFont, From 1aaedb527db386aeefd6a9ede28279f6818ef693 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:43:33 -0600 Subject: [PATCH 107/136] IHandlerMap --- src/settings/SettingsStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index aeed3321b6..d1092ea23e 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -80,7 +80,7 @@ export type CallbackFn = ( newVal: any, ) => void; -interface HandlerMap { +interface IHandlerMap { // @ts-ignore - TS wants this to be a string key but we know better [level: SettingLevel]: SettingsHandler; } @@ -595,7 +595,7 @@ export default class SettingsStore { return handlers[level]; } - private static getHandlers(settingName: string): HandlerMap { + private static getHandlers(settingName: string): IHandlerMap { if (!SETTINGS[settingName]) return {}; const handlers = {}; From 4cf6b7174b94d03ad21cdc6a99baeac5a5b1ffdc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:44:34 -0600 Subject: [PATCH 108/136] Use a type for labs --- src/settings/SettingsStore.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index d1092ea23e..e64de8af16 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -85,6 +85,8 @@ interface IHandlerMap { [level: SettingLevel]: SettingsHandler; } +export type LabsFeatureState = "labs" | "disable" | "enable" | string; + /** * Controls and manages application settings by providing varying levels at which the * setting value may be specified. The levels are then used to determine what the setting @@ -610,7 +612,7 @@ export default class SettingsStore { return handlers; } - private static getFeatureState(settingName: string): "labs" | "disable" | "enable" | string { + private static getFeatureState(settingName: string): LabsFeatureState { const featuresConfig = SdkConfig.get()['features']; const enableLabs = SdkConfig.get()['enableLabs']; // we'll honour the old flag From 648c3c7796449ab8cea2aaafaa4765e8f5b3c7cf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:49:42 -0600 Subject: [PATCH 109/136] Fix various type definitions --- src/settings/controllers/FontSizeController.ts | 3 ++- src/settings/controllers/NotificationControllers.ts | 2 +- src/settings/controllers/SystemFontController.ts | 3 ++- src/settings/controllers/UseSystemFontController.ts | 3 ++- src/settings/handlers/DefaultSettingsHandler.ts | 2 +- src/settings/handlers/LocalEchoWrapper.ts | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/settings/controllers/FontSizeController.ts b/src/settings/controllers/FontSizeController.ts index 6440fd32fe..b86d596040 100644 --- a/src/settings/controllers/FontSizeController.ts +++ b/src/settings/controllers/FontSizeController.ts @@ -18,13 +18,14 @@ import SettingController from "./SettingController"; import dis from "../../dispatcher/dispatcher"; import { UpdateFontSizePayload } from "../../dispatcher/payloads/UpdateFontSizePayload"; import { Action } from "../../dispatcher/actions"; +import { SettingLevel } from "../SettingLevel"; export default class FontSizeController extends SettingController { constructor() { super(); } - onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // Dispatch font size change so that everything open responds to the change. dis.dispatch({ action: Action.UpdateFontSize, diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index 4a8a70362d..fc50af6096 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -61,7 +61,7 @@ export class NotificationsEnabledController extends SettingController { return calculatedValue; } - public onChange(level: SettingLevel, roomId: string, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { if (getNotifier().supportsDesktopNotifications()) { getNotifier().setEnabled(newValue); } diff --git a/src/settings/controllers/SystemFontController.ts b/src/settings/controllers/SystemFontController.ts index e31077e4c8..f38dbdbbf6 100644 --- a/src/settings/controllers/SystemFontController.ts +++ b/src/settings/controllers/SystemFontController.ts @@ -19,13 +19,14 @@ import SettingsStore from "../SettingsStore"; import dis from "../../dispatcher/dispatcher"; import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload"; import { Action } from "../../dispatcher/actions"; +import { SettingLevel } from "../SettingLevel"; export default class SystemFontController extends SettingController { constructor() { super(); } - public onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // Dispatch font size change so that everything open responds to the change. dis.dispatch({ action: Action.UpdateSystemFont, diff --git a/src/settings/controllers/UseSystemFontController.ts b/src/settings/controllers/UseSystemFontController.ts index 07a80c7d38..0b6acc590a 100644 --- a/src/settings/controllers/UseSystemFontController.ts +++ b/src/settings/controllers/UseSystemFontController.ts @@ -19,13 +19,14 @@ import SettingsStore from "../SettingsStore"; import dis from "../../dispatcher/dispatcher"; import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload"; import { Action } from "../../dispatcher/actions"; +import { SettingLevel } from "../SettingLevel"; export default class UseSystemFontController extends SettingController { constructor() { super(); } - public onChange(level, roomId, newValue) { + public onChange(level: SettingLevel, roomId: string, newValue: any) { // Dispatch font size change so that everything open responds to the change. dis.dispatch({ action: Action.UpdateSystemFont, diff --git a/src/settings/handlers/DefaultSettingsHandler.ts b/src/settings/handlers/DefaultSettingsHandler.ts index fc1c92c159..f54c7eafe1 100644 --- a/src/settings/handlers/DefaultSettingsHandler.ts +++ b/src/settings/handlers/DefaultSettingsHandler.ts @@ -27,7 +27,7 @@ export default class DefaultSettingsHandler extends SettingsHandler { * @param {object} defaults The default setting values, keyed by setting name. * @param {object} invertedDefaults The default inverted setting values, keyed by setting name. */ - constructor(private defaults: any, private invertedDefaults: any) { // TODO: [TS] Appropriate types + constructor(private defaults: Record, private invertedDefaults: Record) { super(); } diff --git a/src/settings/handlers/LocalEchoWrapper.ts b/src/settings/handlers/LocalEchoWrapper.ts index d940413b9b..5cfcd27aed 100644 --- a/src/settings/handlers/LocalEchoWrapper.ts +++ b/src/settings/handlers/LocalEchoWrapper.ts @@ -60,7 +60,7 @@ export default class LocalEchoWrapper extends SettingsHandler { }); } - public canSetValue(settingName, roomId): boolean { + public canSetValue(settingName: string, roomId: string): boolean { return this.handler.canSetValue(settingName, roomId); } From bb48ee669bcf3b3af8244268cb017c1ce64fe3fd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:50:36 -0600 Subject: [PATCH 110/136] Make SettingsHandler truly abstract --- src/settings/handlers/SettingsHandler.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/settings/handlers/SettingsHandler.ts b/src/settings/handlers/SettingsHandler.ts index 4b1c3459da..ca9a068fd3 100644 --- a/src/settings/handlers/SettingsHandler.ts +++ b/src/settings/handlers/SettingsHandler.ts @@ -28,10 +28,7 @@ export default abstract class SettingsHandler { * @param {String} roomId The room ID to read from, may be null. * @returns {*} The setting value, or null if not found. */ - public getValue(settingName: string, roomId: string): any { - console.error("Invalid operation: getValue was not overridden"); - return null; - } + public abstract getValue(settingName: string, roomId: string): any; /** * Sets the value for a particular setting at this level for a particular room. @@ -44,10 +41,7 @@ export default abstract class SettingsHandler { * @param {*} newValue The new value for the setting, may be null. * @returns {Promise} Resolves when the setting has been saved. */ - public setValue(settingName: string, roomId: string, newValue: any): Promise { - console.error("Invalid operation: setValue was not overridden"); - return Promise.reject(new Error("Invalid operation: setValue was not overridden")); - } + public abstract setValue(settingName: string, roomId: string, newValue: any): Promise; /** * Determines if the current user is able to set the value of the given setting @@ -56,15 +50,11 @@ export default abstract class SettingsHandler { * @param {String} roomId The room ID to check in, may be null * @returns {boolean} True if the setting can be set by the user, false otherwise. */ - public canSetValue(settingName: string, roomId: string): boolean { - return false; - } + public abstract canSetValue(settingName: string, roomId: string): boolean; /** * Determines if this level is supported on this device. * @returns {boolean} True if this level is supported on the current device. */ - public isSupported(): boolean { - return false; - } + public abstract isSupported(): boolean; } From 19fa08aafa2b056bd9586c446338b9659a4fe1bf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 08:53:54 -0600 Subject: [PATCH 111/136] Fix RightPanelStore imports --- src/settings/Settings.ts | 6 +++--- src/stores/RightPanelStore.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 90ef45f865..1989bd7a34 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -27,12 +27,12 @@ import CustomStatusController from "./controllers/CustomStatusController"; import ThemeController from './controllers/ThemeController'; import PushToMatrixClientController from './controllers/PushToMatrixClientController'; import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; -import { RIGHT_PANEL_PHASES } from "../stores/RightPanelStorePhases"; import FontSizeController from './controllers/FontSizeController'; import SystemFontController from './controllers/SystemFontController'; import UseSystemFontController from './controllers/UseSystemFontController'; import { SettingLevel } from "./SettingLevel"; import SettingController from "./controllers/SettingController"; +import { RightPanelPhases } from "../stores/RightPanelStorePhases"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -547,11 +547,11 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "lastRightPanelPhaseForRoom": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, - default: RIGHT_PANEL_PHASES.RoomMemberInfo, + default: RightPanelPhases.RoomMemberInfo, }, "lastRightPanelPhaseForGroup": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, - default: RIGHT_PANEL_PHASES.GroupMemberList, + default: RightPanelPhases.GroupMemberList, }, "enableEventIndexing": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, diff --git a/src/stores/RightPanelStore.ts b/src/stores/RightPanelStore.ts index dc5eb99a36..c1799978ad 100644 --- a/src/stores/RightPanelStore.ts +++ b/src/stores/RightPanelStore.ts @@ -17,10 +17,11 @@ limitations under the License. import dis from '../dispatcher/dispatcher'; import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; -import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; +import SettingsStore from "../settings/SettingsStore"; import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases"; import {ActionPayload} from "../dispatcher/payloads"; import {Action} from '../dispatcher/actions'; +import { SettingLevel } from "../settings/SettingLevel"; interface RightPanelStoreState { // Whether or not to show the right panel at all. We split out rooms and groups From fca6def58898c1e8eaf067fc609a5a912bdcd96e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 09:15:19 -0600 Subject: [PATCH 112/136] Rename and document what local echo is --- src/components/views/rooms/RoomTile.tsx | 6 +++--- src/stores/local-echo/EchoChamber.ts | 6 +++--- src/stores/local-echo/EchoStore.ts | 12 ++++++------ .../{CachedEcho.ts => GenericEchoChamber.ts} | 2 +- .../{RoomCachedEcho.ts => RoomEchoChamber.ts} | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) rename src/stores/local-echo/{CachedEcho.ts => GenericEchoChamber.ts} (97%) rename src/stores/local-echo/{RoomCachedEcho.ts => RoomEchoChamber.ts} (92%) diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 4b051cf795..02aa915fa5 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -49,8 +49,8 @@ import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNo import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { EchoChamber } from "../../../stores/local-echo/EchoChamber"; -import { CachedRoomKey, RoomCachedEcho } from "../../../stores/local-echo/RoomCachedEcho"; -import { PROPERTY_UPDATED } from "../../../stores/local-echo/CachedEcho"; +import { CachedRoomKey, RoomEchoChamber } from "../../../stores/local-echo/RoomEchoChamber"; +import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber"; interface IProps { room: Room; @@ -108,7 +108,7 @@ export default class RoomTile extends React.PureComponent { private dispatcherRef: string; private roomTileRef = createRef(); private notificationState: NotificationState; - private roomProps: RoomCachedEcho; + private roomProps: RoomEchoChamber; constructor(props: IProps) { super(props); diff --git a/src/stores/local-echo/EchoChamber.ts b/src/stores/local-echo/EchoChamber.ts index 4c5109da2d..f61e521728 100644 --- a/src/stores/local-echo/EchoChamber.ts +++ b/src/stores/local-echo/EchoChamber.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { RoomCachedEcho } from "./RoomCachedEcho"; +import { RoomEchoChamber } from "./RoomEchoChamber"; import { Room } from "matrix-js-sdk/src/models/room"; import { EchoStore } from "./EchoStore"; @@ -25,7 +25,7 @@ export class EchoChamber { private constructor() { } - public static forRoom(room: Room): RoomCachedEcho { - return EchoStore.instance.getOrCreateEchoForRoom(room); + public static forRoom(room: Room): RoomEchoChamber { + return EchoStore.instance.getOrCreateChamberForRoom(room); } } diff --git a/src/stores/local-echo/EchoStore.ts b/src/stores/local-echo/EchoStore.ts index ef2ee2d275..76e90be45e 100644 --- a/src/stores/local-echo/EchoStore.ts +++ b/src/stores/local-echo/EchoStore.ts @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { CachedEcho } from "./CachedEcho"; +import { GenericEchoChamber } from "./GenericEchoChamber"; import { Room } from "matrix-js-sdk/src/models/room"; -import { RoomCachedEcho } from "./RoomCachedEcho"; +import { RoomEchoChamber } from "./RoomEchoChamber"; import { RoomEchoContext } from "./RoomEchoContext"; import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; import defaultDispatcher from "../../dispatcher/dispatcher"; @@ -36,7 +36,7 @@ const roomContextKey = (room: Room): ContextKey => `room-${room.roomId}`; export class EchoStore extends AsyncStoreWithClient { private static _instance: EchoStore; - private caches = new Map>(); + private caches = new Map>(); constructor() { super(defaultDispatcher); @@ -53,15 +53,15 @@ export class EchoStore extends AsyncStoreWithClient { return Array.from(this.caches.values()).map(e => e.context); } - public getOrCreateEchoForRoom(room: Room): RoomCachedEcho { + public getOrCreateChamberForRoom(room: Room): RoomEchoChamber { if (this.caches.has(roomContextKey(room))) { - return this.caches.get(roomContextKey(room)) as RoomCachedEcho; + return this.caches.get(roomContextKey(room)) as RoomEchoChamber; } const context = new RoomEchoContext(room); context.whenAnything(() => this.checkContexts()); - const echo = new RoomCachedEcho(context); + const echo = new RoomEchoChamber(context); echo.setClient(this.matrixClient); this.caches.set(roomContextKey(room), echo); diff --git a/src/stores/local-echo/CachedEcho.ts b/src/stores/local-echo/GenericEchoChamber.ts similarity index 97% rename from src/stores/local-echo/CachedEcho.ts rename to src/stores/local-echo/GenericEchoChamber.ts index ce89e639c9..7bedc9bde8 100644 --- a/src/stores/local-echo/CachedEcho.ts +++ b/src/stores/local-echo/GenericEchoChamber.ts @@ -25,7 +25,7 @@ export async function implicitlyReverted() { export const PROPERTY_UPDATED = "property_updated"; -export abstract class CachedEcho extends EventEmitter { +export abstract class GenericEchoChamber extends EventEmitter { private cache = new Map(); protected matrixClient: MatrixClient; diff --git a/src/stores/local-echo/RoomCachedEcho.ts b/src/stores/local-echo/RoomEchoChamber.ts similarity index 92% rename from src/stores/local-echo/RoomCachedEcho.ts rename to src/stores/local-echo/RoomEchoChamber.ts index 3ac01d3873..e113f68c32 100644 --- a/src/stores/local-echo/RoomCachedEcho.ts +++ b/src/stores/local-echo/RoomEchoChamber.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { CachedEcho, implicitlyReverted, PROPERTY_UPDATED } from "./CachedEcho"; +import { GenericEchoChamber, implicitlyReverted, PROPERTY_UPDATED } from "./GenericEchoChamber"; import { getRoomNotifsState, setRoomNotifsState } from "../../RoomNotifs"; import { RoomEchoContext } from "./RoomEchoContext"; import { _t } from "../../languageHandler"; @@ -27,7 +27,7 @@ export enum CachedRoomKey { NotificationVolume, } -export class RoomCachedEcho extends CachedEcho { +export class RoomEchoChamber extends GenericEchoChamber { private properties = new Map(); public constructor(context: RoomEchoContext) { From 3b346161a58932409960761ac2bd59a91d168ac5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 09:24:38 -0600 Subject: [PATCH 113/136] Actually include the docs for local echo --- docs/local-echo-dev.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/local-echo-dev.md diff --git a/docs/local-echo-dev.md b/docs/local-echo-dev.md new file mode 100644 index 0000000000..e4725a9b07 --- /dev/null +++ b/docs/local-echo-dev.md @@ -0,0 +1,39 @@ +# Local echo (developer docs) + +The React SDK provides some local echo functionality to allow for components to do something +quickly and fall back when it fails. This is all available in the `local-echo` directory within +`stores`. + +Echo is handled in EchoChambers, with `GenericEchoChamber` being the base implementation for all +chambers. The `EchoChamber` class is provided as semantic access to a `GenericEchoChamber` +implementation, such as the `RoomEchoChamber` (which handles echoable details of a room). + +Anything that can be locally echoed will be provided by the `GenericEchoChamber` implementation. +The echo chamber will also need to deal with external changes, and has full control over whether +or not something has successfully been echoed. + +An `EchoContext` is provided to echo chambers (usually with a matching type: `RoomEchoContext` +gets provided to a `RoomEchoChamber` for example) with details about their intended area of +effect, as well as manage `EchoTransaction`s. An `EchoTransaction` is simply a unit of work that +needs to be locally echoed. + +The `EchoStore` manages echo chamber instances, builds contexts, and is generally less semantically +accessible than the `EchoChamber` class. For separation of concerns, and to try and keep things +tidy, this is an intentional design decision. + +**Note**: The local echo stack uses a "whenable" pattern, which is similar to thenables and +`EventEmitter`. Whenables are ways of actioning a changing condition without having to deal +with listeners being torn down. Once the reference count of the Whenable causes garbage collection, +the Whenable's listeners will also be torn down. This is accelerated by the `IDestroyable` interface +usage. + +## Audit functionality + +The UI supports a "Server isn't responding" dialog which includes a partial audit log-like +structure to it. This is partially the reason for added complexity of `EchoTransaction`s +and `EchoContext`s - this information feeds the UI states which then provide direct retry +mechanisms. + +The `EchoStore` is responsible for ensuring that the appropriate non-urgent toast (lower left) +is set up, where the dialog then drives through the contexts and transactions. + From 7522380763605154ace517420000a3f0a2089333 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 10:17:47 -0600 Subject: [PATCH 114/136] Update copy --- src/components/views/dialogs/ServerOfflineDialog.tsx | 8 +++++--- src/i18n/strings/en_EN.json | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/views/dialogs/ServerOfflineDialog.tsx b/src/components/views/dialogs/ServerOfflineDialog.tsx index 074ea19d4d..6d2aa5ac27 100644 --- a/src/components/views/dialogs/ServerOfflineDialog.tsx +++ b/src/components/views/dialogs/ServerOfflineDialog.tsx @@ -26,6 +26,7 @@ import { TransactionStatus } from "../../../stores/local-echo/EchoTransaction"; import Spinner from "../elements/Spinner"; import AccessibleButton from "../elements/AccessibleButton"; import { UPDATE_EVENT } from "../../../stores/AsyncStore"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IProps { onFinished: (bool) => void; @@ -92,6 +93,7 @@ export default class ServerOfflineDialog extends React.PureComponent { timeline = [
{_t("You're all caught up.")}
]; } + const serverName = MatrixClientPeg.getHomeserverName(); return { >

{_t( - "Your server isn't responding to some of your requests for some reason. " + - "Below are some possible reasons why this happened.", + "Your server isn't responding to some of your requests. " + + "Below are some of the most likely reasons.", )}

    -
  • {_t("The server took too long to respond.")}
  • +
  • {_t("The server (%(serverName)s) took too long to respond.", {serverName})}
  • {_t("Your firewall or anti-virus is blocking the request.")}
  • {_t("A browser extension is preventing the request.")}
  • {_t("The server is offline.")}
  • diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d1e940b3be..c3d4a1c8cc 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1748,8 +1748,8 @@ "Resend": "Resend", "You're all caught up.": "You're all caught up.", "Server isn't responding": "Server isn't responding", - "Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.": "Your server isn't responding to some of your requests for some reason. Below are some possible reasons why this happened.", - "The server took too long to respond.": "The server took too long to respond.", + "Your server isn't responding to some of your requests. Below are some of the most likely reasons.": "Your server isn't responding to some of your requests. Below are some of the most likely reasons.", + "The server (%(serverName)s) took too long to respond.": "The server (%(serverName)s) took too long to respond.", "Your firewall or anti-virus is blocking the request.": "Your firewall or anti-virus is blocking the request.", "A browser extension is preventing the request.": "A browser extension is preventing the request.", "The server is offline.": "The server is offline.", From 9d247321f549962db976281edbb6c06bf5336581 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jul 2020 18:52:47 +0100 Subject: [PATCH 115/136] Fix crash on logging in again after soft logout Fixes https://github.com/vector-im/riot-web/issues/14834 --- src/stores/AsyncStoreWithClient.ts | 5 ----- src/stores/BreadcrumbsStore.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/stores/AsyncStoreWithClient.ts b/src/stores/AsyncStoreWithClient.ts index 5b9f95f991..69aa5367c6 100644 --- a/src/stores/AsyncStoreWithClient.ts +++ b/src/stores/AsyncStoreWithClient.ts @@ -35,11 +35,6 @@ export abstract class AsyncStoreWithClient extends AsyncStore< 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') { diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index 34affbe746..ea29cb9dfc 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -55,7 +55,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } private get meetsRoomRequirement(): boolean { - return this.matrixClient.getVisibleRooms().length >= 20; + return this.matrixClient && this.matrixClient.getVisibleRooms().length >= 20; } protected async onAction(payload: ActionPayload) { From f3320f5041a6fb028475d553b3709f98208a012f Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jul 2020 19:09:47 +0100 Subject: [PATCH 116/136] Fix key backup warning on soft logout page It always showed the warning (if you had at least one session key in your store) because flagAllGroupSessionsForBackup returns the number of keys pending backup after flagging them all for backup, ie. all of them. Seems like the intention was to only show the warning if there were keys that had not yet been backed up. Fixes https://github.com/vector-im/riot-web/issues/14829 Requires https://github.com/matrix-org/matrix-js-sdk/pull/1429 --- src/components/structures/auth/SoftLogout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index 6577386fae..a539c8c9ee 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -72,7 +72,7 @@ export default class SoftLogout extends React.Component { this._initLogin(); - MatrixClientPeg.get().flagAllGroupSessionsForBackup().then(remaining => { + MatrixClientPeg.get().countSessionsNeedingBackup().then(remaining => { this.setState({keyBackupNeeded: remaining > 0}); }); } From 3393f58191ca67cc4695264bbf9d01c33d21bd04 Mon Sep 17 00:00:00 2001 From: Txopi Date: Thu, 30 Jul 2020 18:33:21 +0000 Subject: [PATCH 117/136] Translated using Weblate (Basque) Currently translated at 95.7% (2225 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 9dcfe7ca22..0f49330778 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -1539,7 +1539,7 @@ "View": "Ikusi", "Find a room…": "Bilatu gela bat…", "Find a room… (e.g. %(exampleRoom)s)": "Bilatu gela bat… (adib. %(exampleRoom)s)", - "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Ezin baduzu bila ari zaren gela aurkitu, eskatu gonbidapen bat edo Sortu gela berri bat.", + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Ez baduzu bilatzen duzuna aurkitzen, eskatu gonbidapen bat edo Sortu gela berri bat.", "Explore rooms": "Arakatu gelak", "Community Autocomplete": "Komunitate osatze automatikoa", "Emoji Autocomplete": "Emoji osatze automatikoa", From 76630f6be0944f886eed375c0dc5e592dc0225cc Mon Sep 17 00:00:00 2001 From: iortega Date: Thu, 30 Jul 2020 18:35:05 +0000 Subject: [PATCH 118/136] Translated using Weblate (Basque) Currently translated at 95.7% (2225 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 0f49330778..58ada6c401 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -1897,7 +1897,7 @@ "If your other sessions do not have the key for this message you will not be able to decrypt them.": "Zure beste saioek ez badute mezu honen gakoa ezin izango duzu deszifratu.", "Re-request encryption keys from your other sessions.": "Eskatu berriro zifratze gakoak zure beste saioei.", "Waiting for %(displayName)s to accept…": "%(displayName)s(e)k onartu bitartean zain…", - "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Zuon mezuak babestuta daude eta soilik zuk eta hartzaileak dituzue hauek irekitzeko giltza.", + "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Zuen mezuak babestuta daude eta soilik zuk eta hartzaileak dituzue hauek dekodetzeko gakoak.", "One of the following may be compromised:": "Hauetakoren bat konprometituta egon daiteke:", "Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Egiztatu gailu hau fidagarri gisa markatzeko. Gailu hau fidagarritzat jotzeak lasaitasuna ematen du muturretik-muturrera zifratutako mezuak erabiltzean.", "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Gailu hau egiaztatzean fidagarri gisa markatuko da, eta egiaztatu zaituzten erabiltzaileek fidagarri gisa ikusiko dute.", From aaf0c7d269c2afbb120347dc0425e882f2f1a32b Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jul 2020 19:46:27 +0100 Subject: [PATCH 119/136] Put back code with comment hopefully explaining why it's necessary --- src/stores/AsyncStoreWithClient.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/stores/AsyncStoreWithClient.ts b/src/stores/AsyncStoreWithClient.ts index 69aa5367c6..f305bcb913 100644 --- a/src/stores/AsyncStoreWithClient.ts +++ b/src/stores/AsyncStoreWithClient.ts @@ -35,6 +35,13 @@ export abstract class AsyncStoreWithClient extends AsyncStore< await this.onAction(payload); if (payload.action === 'MatrixActions.sync') { + // Only set the client on the transition into the PREPARED state. + // Everything after this is unnecessary (we only need to know once we have a client) + // and we intentionally don't set the client before this point to avoid stores + // updating for every event emitted during the cached 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') { From f0f691b70e04048845647e1a960e3dd162acbceb Mon Sep 17 00:00:00 2001 From: Txopi Date: Thu, 30 Jul 2020 18:35:25 +0000 Subject: [PATCH 120/136] Translated using Weblate (Basque) Currently translated at 96.5% (2243 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 58ada6c401..01afa51836 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -326,7 +326,7 @@ "Upload an avatar:": "Igo abatarra:", "This server does not support authentication with a phone number.": "Zerbitzari honek ez du telefono zenbakia erabiliz autentifikatzea onartzen.", "An error occurred: %(error_string)s": "Errore bat gertatu da: %(error_string)s", - "There are no visible files in this room": "Ez dago fitxategi ikusgairik gela honetan", + "There are no visible files in this room": "Ez dago fitxategirik ikusgai gela honetan", "Sent messages will be stored until your connection has returned.": "Bidalitako mezuak zure konexioa berreskuratu arte gordeko dira.", "(~%(count)s results)|one": "(~%(count)s emaitza)", "(~%(count)s results)|other": "(~%(count)s emaitza)", @@ -734,7 +734,7 @@ "All Rooms": "Gela guztiak", "Wednesday": "Asteazkena", "You cannot delete this message. (%(code)s)": "Ezin duzu mezu hau ezabatu. (%(code)s)", - "Quote": "Aipua", + "Quote": "Aipatu", "Send logs": "Bidali egunkariak", "All messages": "Mezu guztiak", "Call invitation": "Dei gonbidapena", @@ -1897,7 +1897,7 @@ "If your other sessions do not have the key for this message you will not be able to decrypt them.": "Zure beste saioek ez badute mezu honen gakoa ezin izango duzu deszifratu.", "Re-request encryption keys from your other sessions.": "Eskatu berriro zifratze gakoak zure beste saioei.", "Waiting for %(displayName)s to accept…": "%(displayName)s(e)k onartu bitartean zain…", - "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Zuen mezuak babestuta daude eta soilik zuk eta hartzaileak dituzue hauek dekodetzeko gakoak.", + "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Zuen mezuak babestuta daude eta soilik zuk eta hartzaileak dituzue hauek desblokeatzeko gakoak.", "One of the following may be compromised:": "Hauetakoren bat konprometituta egon daiteke:", "Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.": "Egiztatu gailu hau fidagarri gisa markatzeko. Gailu hau fidagarritzat jotzeak lasaitasuna ematen du muturretik-muturrera zifratutako mezuak erabiltzean.", "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Gailu hau egiaztatzean fidagarri gisa markatuko da, eta egiaztatu zaituzten erabiltzaileek fidagarri gisa ikusiko dute.", @@ -2248,5 +2248,23 @@ "Security & privacy": "Segurtasuna eta pribatutasuna", "All settings": "Ezarpen guztiak", "Feedback": "Iruzkinak", - "Use a different passphrase?": "Erabili pasa-esaldi desberdin bat?" + "Use a different passphrase?": "Erabili pasa-esaldi desberdin bat?", + "We’re excited to announce Riot is now Element": "Pozik jakinarazten dizugu: Riot orain Element deitzen da", + "Riot is now Element!": "Riot orain Element da!", + "Learn More": "Ikasi gehiago", + "Light": "Argia", + "The person who invited you already left the room.": "Gonbidatu zaituen pertsonak dagoeneko gela utzi du.", + "The person who invited you already left the room, or their server is offline.": "Gonbidatu zaituen pertsonak dagoeneko gela utzi du edo bere zerbitzaria lineaz kanpo dago.", + "You joined the call": "Deira batu zara", + "%(senderName)s joined the call": "%(senderName)s deira batu da", + "You left the call": "Deitik atera zara", + "%(senderName)s left the call": "%(senderName)s(e) deitik atera da", + "Call ended": "Deia amaitu da", + "You started a call": "Dei bat hasi duzu", + "%(senderName)s started a call": "%(senderName)s(e)k dei bat hasi du", + "%(senderName)s is calling": "%(senderName)s deitzen ari da", + "%(brand)s Web": "%(brand)s web", + "%(brand)s Desktop": "%(brand)s Desktop", + "%(brand)s iOS": "%(brand)s iOS", + "%(brand)s X for Android": "%(brand)s X for Android" } From 13130c5ed449a30fa8f8788a271d05e82b15cebe Mon Sep 17 00:00:00 2001 From: XoseM Date: Thu, 30 Jul 2020 13:39:26 +0000 Subject: [PATCH 121/136] Translated using Weblate (Galician) Currently translated at 100.0% (2324 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 4ca354c8ec..f06b1e22d9 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -2388,5 +2388,7 @@ "Click to view edits": "Preme para ver as edicións", "Are you sure you want to cancel entering passphrase?": "¿Estás seguro de que non queres escribir a frase de paso?", "Custom Tag": "Etiqueta personal", - "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s" + "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", + "The person who invited you already left the room.": "A persoa que te convidou xa deixou a sala.", + "The person who invited you already left the room, or their server is offline.": "A persoa que te convidou xa deixou a sala, ou o seu servidor non está a funcionar." } From 1e4ab627df75f86ab4d05e6ae035e6d95260654e Mon Sep 17 00:00:00 2001 From: Christopher May-Townsend Date: Thu, 30 Jul 2020 18:33:51 +0000 Subject: [PATCH 122/136] Translated using Weblate (Icelandic) Currently translated at 19.6% (456 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/is/ --- src/i18n/strings/is.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/is.json b/src/i18n/strings/is.json index cbf05b89f0..6f561331f6 100644 --- a/src/i18n/strings/is.json +++ b/src/i18n/strings/is.json @@ -298,7 +298,7 @@ "Low Priority": "Lítill forgangur", "Direct Chat": "Beint spjall", "View Community": "Skoða samfélag", - "I understand the risks and wish to continue": "Ég skil áhættuna og vil halda áfram", + "I understand the risks and wish to continue": "Ég skil áhættuna og óska að halda áfram", "Name": "Nafn", "Failed to upload image": "Gat ekki sent inn mynd", "Add rooms to this community": "Bæta spjallrásum í þetta samfélag", @@ -457,5 +457,6 @@ "Room Notification": "Tilkynning á spjallrás", "Passphrases must match": "Lykilfrasar verða að stemma", "Passphrase must not be empty": "Lykilfrasi má ekki vera auður", - "Create Account": "Stofna Reikning" + "Create Account": "Stofna Reikning", + "Please install Chrome, Firefox, or Safari for the best experience.": "vinsamlegast setja upp Chrome, Firefox, eða Safari fyrir besta reynsluna." } From c861df49bd2321ca2bbe7c7d931d2fa8ed2f8800 Mon Sep 17 00:00:00 2001 From: Marcelo Filho Date: Thu, 30 Jul 2020 14:06:15 +0000 Subject: [PATCH 123/136] Translated using Weblate (Portuguese (Brazil)) Currently translated at 92.3% (2144 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 243 +++++++++++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index d5eff339ac..60beb2b726 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -311,9 +311,9 @@ "Invited": "Convidada(o)", "Results from DuckDuckGo": "Resultados de DuckDuckGo", "Verified key": "Chave verificada", - "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a foto da sala.", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s", - "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s alterou a imagem da sala para ", + "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s alterou a foto da sala para ", "No Microphones detected": "Não foi detectado nenhum microfone", "No Webcams detected": "Não foi detectada nenhuma Webcam", "No media permissions": "Não há permissões de uso de vídeo/áudio no seu navegador", @@ -1127,7 +1127,7 @@ "Room version:": "Versão da sala:", "Developer options": "Opções de desenvolvedor", "Room Addresses": "Endereços da sala", - "Change room avatar": "Alterar avatar da sala", + "Change room avatar": "Alterar a foto da sala", "Change room name": "Alterar nome da sala", "Change main address for the room": "Alterar o endereço principal da sala", "Change history visibility": "Alterar a visibilidade do histórico", @@ -1729,7 +1729,7 @@ "Your avatar URL": "A URL da sua foto de perfil", "Your user ID": "Sua ID de usuário", "%(brand)s URL": "URL de %(brand)s", - "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Se você usar esse widget, os dados poderão ser compartilhados com %(widgetDomain)s & seu Servidor de Integrações.", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Se você usar esse widget, os dados poderão ser compartilhados com %(widgetDomain)s & seu Gerenciador de Integrações.", "Using this widget may share data with %(widgetDomain)s.": "Se você usar esse widget, os dados poderão ser compartilhados com %(widgetDomain)s.", "%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)s não fizeram alterações %(count)s vezes", "%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)s não fizeram alterações", @@ -1916,5 +1916,238 @@ "Navigate up/down in the room list": "Navegue para cima/baixo na lista de salas", "Select room from the room list": "Selecionar sala da lista de salas", "Collapse room list section": "Esconder seção da lista de salas", - "Expand room list section": "Mostrar seção da lista de salas" + "Expand room list section": "Mostrar seção da lista de salas", + "The person who invited you already left the room.": "A pessoa que convidou você já saiu da sala.", + "The person who invited you already left the room, or their server is offline.": "A pessoa que convidou você já saiu da sala, ou o servidor dela está offline.", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Use o Gerenciador de Integrações em (%(serverName)s) para gerenciar bots, widgets e pacotes de adesivos.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use o Gerenciador de Integrações para gerenciar bots, widgets e pacotes de adesivos.", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "O Gerenciador de Integrações recebe dados de configuração e pode modificar widgets, enviar convites para salas e definir níveis de permissão em seu nome.", + "Keyboard Shortcuts": "Atalhos do teclado", + "Customise your experience with experimental labs features. Learn more.": "Personalize sua experiência com os recursos experimentais. Saiba mais.", + "Ignored/Blocked": "Ignorado/Bloqueado", + "Error adding ignored user/server": "Erro ao adicionar usuário/servidor ignorado", + "Something went wrong. Please try again or view your console for hints.": "Algo deu errado. Por favor, tente novamente ou veja seu console para obter dicas.", + "Error subscribing to list": "Erro ao inscrever-se na lista", + "Error removing ignored user/server": "Erro ao remover usuário/servidor ignorado", + "Error unsubscribing from list": "Erro ao cancelar a inscrição da lista", + "Please try again or view your console for hints.": "Por favor, tente novamente ou veja seu console para obter dicas.", + "None": "Nenhum", + "Server rules": "Regras do servidor", + "User rules": "Regras do usuário", + "You have not ignored anyone.": "Você não ignorou ninguém.", + "You are currently ignoring:": "Você está atualmente ignorando:", + "You are not subscribed to any lists": "Você não está inscrito em nenhuma lista", + "Unsubscribe": "Desinscrever-se", + "View rules": "Ver regras", + "You are currently subscribed to:": "No momento, você está inscrito em:", + "⚠ These settings are meant for advanced users.": "⚠ Essas configurações são destinadas a usuários avançados.", + "Add users and servers you want to ignore here. Use asterisks to have %(brand)s match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Adicione aqui os usuários e servidores que você deseja ignorar. Use asteriscos para fazer com que o %(brand)s corresponda a qualquer caractere. Por exemplo, @bot:* ignorará todos os usuários em qualquer servidor que tenham 'bot' no nome.", + "Server or user ID to ignore": "Servidor ou ID de usuário para ignorar", + "Subscribe": "Inscrever-se", + "Session ID:": "ID da sessão:", + "Message search": "Pesquisa de mensagens", + "Where you’re logged in": "Onde você está conectado", + "Set a new custom sound": "Definir um novo som personalizado", + "Browse": "Buscar", + "Upgrade the room": "Atualizar a sala", + "Kick users": "Remover usuários", + "Ban users": "Banir usuários", + "Remove messages": "Remover mensagens", + "Notify everyone": "Notificar todo mundo", + "Your email address hasn't been verified yet": "Seu endereço de e-mail ainda não foi verificado", + "Revoke": "Revogar", + "Share": "Compartilhar", + "Unable to revoke sharing for phone number": "Não foi possível revogar o compartilhamento do número de celular", + "Unable to share phone number": "Não foi possível compartilhar o número de celular", + "Please enter verification code sent via text.": "Digite o código de verificação enviado por mensagem de texto.", + "Remove %(email)s?": "Remover %(email)s?", + "Remove %(phone)s?": "Remover %(phone)s?", + "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "Digite o código de verificação enviado por mensagem de texto para +%(msisdn)s.", + "This user has not verified all of their sessions.": "Este usuário não verificou todas suas próprias sessões.", + "You have not verified this user.": "Você não verificou este usuário.", + "You have verified this user. This user has verified all of their sessions.": "Você confirmou este usuário. Este usuário verificou todas as próprias sessões.", + "Someone is using an unknown session": "Alguém está usando uma sessão desconhecida", + "Everyone in this room is verified": "Todo mundo nesta sala está verificado", + "Edit message": "Editar mensagem", + "Mod": "Moderador", + "Scroll to most recent messages": "Ir para as mensagens mais recentes", + "Close preview": "Fechar a visualização", + "Send a reply…": "Enviar uma resposta…", + "Send a message…": "Enviar uma mensagem…", + "Bold": "Negrito", + "Italics": "Itálico", + "Strikethrough": "Riscado", + "Code block": "Bloco de código", + "Failed to connect to integration manager": "Falha ao conectar-se ao gerenciador de integrações", + "Failed to revoke invite": "Falha ao revogar o convite", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Não foi possível revogar o convite. O servidor pode estar com um problema temporário ou você não tem permissões suficientes para revogar o convite.", + "Revoke invite": "Revogar o convite", + "Invited by %(sender)s": "Convidado por %(sender)s", + "Mark all as read": "Marcar tudo como lido", + "Error updating main address": "Erro ao atualizar o endereço principal", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Ocorreu um erro ao atualizar o endereço principal da sala. Isso pode não ser permitido pelo servidor ou houve um problema temporário.", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Ocorreu um erro ao atualizar o endereço alternativo da sala. Isso pode não ser permitido pelo servidor ou houve um problema temporário.", + "Error creating address": "Erro ao criar o endereço", + "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.": "Ocorreu um erro ao criar esse endereço. Isso pode não ser permitido pelo servidor ou houve um problema temporário.", + "You don't have permission to delete the address.": "Você não tem permissão para excluir este endereço.", + "There was an error removing that address. It may no longer exist or a temporary error occurred.": "Ocorreu um erro ao remover esse endereço. Ele pode não mais existir ou houve um problema temporário.", + "Error removing address": "Erro ao remover o endereço", + "Main address": "Endereço principal", + "Room Name": "Nome da sala", + "Room avatar": "Foto da sala", + "Waiting for you to accept on your other session…": "Aguardando sua confirmação na sua outra sessão…", + "Waiting for %(displayName)s to accept…": "Aguardando %(displayName)s aceitar…", + "Accepting…": "Aceitando…", + "Your messages are secured and only you and the recipient have the unique keys to unlock them.": "Suas mensagens são protegidas e somente você e o destinatário têm as chaves exclusivas para desbloqueá-las.", + "Your messages are not secure": "Suas mensagens não estão seguras", + "Your homeserver": "Seu servidor local", + "Trusted": "Confiável", + "Not trusted": "Não confiável", + "%(count)s verified sessions|other": "%(count)s sessões verificadas", + "%(count)s verified sessions|one": "1 sessão verificada", + "Hide verified sessions": "Esconder sessões verificadas", + "%(count)s sessions|other": "%(count)s sessões", + "%(count)s sessions|one": "%(count)s sessão", + "Hide sessions": "Esconder sessões", + "No recent messages by %(user)s found": "Nenhuma mensagem recente de %(user)s foi encontrada", + "Remove recent messages by %(user)s": "Remover mensagens recentes de %(user)s", + "Remove %(count)s messages|other": "Remover %(count)s mensagens", + "Remove %(count)s messages|one": "Remover 1 mensagem", + "Remove recent messages": "Remover mensagens recentes", + "%(role)s in %(roomName)s": "%(role)s em %(roomName)s", + "Deactivate user?": "Desativar usuário?", + "Deactivate user": "Desativar usuário", + "Failed to deactivate user": "Falha ao desativar o usuário", + "Security": "Segurança", + "You've successfully verified your device!": "Você verificou o seu aparelho com êxito!", + "Verification timed out.": "O tempo de verificação se esgotou.", + "You cancelled verification on your other session.": "Você cancelou a verificação em sua outra sessão.", + "%(displayName)s cancelled verification.": "%(displayName)s cancelou a verificação.", + "You cancelled verification.": "Você cancelou a verificação.", + "Verification cancelled": "Verificação cancelada", + "Compare emoji": "Comparar emojis", + "Show image": "Mostrar imagem", + "You have ignored this user, so their message is hidden. Show anyways.": "Você ignorou este usuário, portanto, a mensagem dele está oculta. Mostrar mesmo assim.", + "You verified %(name)s": "Você verificou %(name)s", + "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Use um servidor de identidade para convidar por e-mail. Use o padrão (%(defaultIdentityServerName)s) ou um servidor personalizado em Configurações.", + "Use an identity server to invite by email. Manage in Settings.": "Use um servidor de identidade para convidar por e-mail. Gerencie o servidor em Configurações.", + "Destroy cross-signing keys?": "Destruir chaves de assinatura cruzada?", + "Waiting for partner to confirm...": "Esperando a outra pessoa confirmar...", + "Enable 'Manage Integrations' in Settings to do this.": "Para fazer isso, ative 'Gerenciar Integrações' nas Configurações.", + "Your %(brand)s doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Seu %(brand)s não permite que você use o Gerenciador de Integrações para fazer isso. Entre em contato com um administrador.", + "Confirm to continue": "Confirme para continuar", + "Click the button below to confirm your identity.": "Clique no botão abaixo para confirmar sua identidade.", + "Failed to invite the following users to chat: %(csvUsers)s": "Falha ao convidar os seguintes usuários para a conversa: %(csvUsers)s", + "Something went wrong trying to invite the users.": "Ocorreu um erro ao tentar convidar os usuários.", + "We couldn't invite those users. Please check the users you want to invite and try again.": "Não foi possível convidar esses usuários. Por favor, tente novamente.", + "Failed to find the following users": "Falha ao encontrar os seguintes usuários", + "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Os seguintes usuários não puderam ser convidados porque não existem ou são inválidos: %(csvNames)s", + "Recent Conversations": "Conversas recentes", + "Suggestions": "Sugestões", + "Invite someone using their name, username (like ), email address or share this room.": "Convide alguém com seu nome, nome de usuário (por exemplo: ), endereço de e-mail ou compartilhe esta sala.", + "Use your account to sign in to the latest version of the app at ": "Use sua conta para fazer login na versão mais recente do aplicativo em ", + "Report bugs & give feedback": "Relatar erros & enviar comentários", + "Room Settings - %(roomName)s": "Configurações da sala - %(roomName)s", + "Automatically invite users": "Convidar usuários automaticamente", + "Upgrade private room": "Atualizar a sala privada", + "Upgrade public room": "Atualizar a sala pública", + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Atualizar uma sala é uma ação avançada e geralmente é recomendada quando uma sala está instável devido a erros, recursos ausentes ou vulnerabilidades de segurança.", + "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Isso geralmente afeta apenas como a sala é processada no servidor. Se você tiver problemas com o %(brand)s, informe um erro.", + "You'll upgrade this room from to .": "Você atualizará esta sala de para .", + "A username can only contain lower case letters, numbers and '=_-./'": "Um nome de usuário só pode ter letras minúsculas, números e '=_-./'", + "Command Help": "Ajuda com Comandos", + "To help us prevent this in future, please send us logs.": "Para nos ajudar a evitar isso no futuro, envie-nos os registros.", + "Your browser likely removed this data when running low on disk space.": "O seu navegador provavelmente removeu esses dados quando o espaço de armazenamento ficou insuficiente.", + "Integration Manager": "Gerenciador de Integrações", + "Find others by phone or email": "Encontre outras pessoas por telefone ou e-mail", + "Use bots, bridges, widgets and sticker packs": "Use bots, pontes, widgets e pacotes de adesivos", + "Terms of Service": "Termos de serviço", + "To continue you need to accept the terms of this service.": "Para continuar, você precisa aceitar os termos deste serviço.", + "Service": "Serviço", + "Summary": "Resumo", + "Document": "Documento", + "Upload files (%(current)s of %(total)s)": "Enviar arquivos (%(current)s de %(total)s)", + "Upload files": "Enviar arquivos", + "Upload all": "Enviar tudo", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Este arquivo é muito grande para ser enviado. O limite do tamanho de arquivos é %(limit)s, enquanto que o tamanho desse arquivo é %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Esses arquivos são muito grandes para serem enviados. O limite do tamanho de arquivos é %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Alguns arquivos são muito grandes para serem enviados. O limite do tamanho de arquivos é %(limit)s.", + "Upload %(count)s other files|other": "Enviar %(count)s outros arquivos", + "Upload %(count)s other files|one": "Enviar %(count)s outros arquivos", + "Cancel All": "Cancelar tudo", + "Upload Error": "Erro no envio", + "Verification Request": "Solicitação de verificação", + "Remember my selection for this widget": "Lembrar minha escolha para este widget", + "Deny": "Rejeitar", + "Wrong file type": "Tipo errado de arquivo", + "Address (optional)": "Endereço (opcional)", + "Report Content": "Reportar conteúdo", + "Update status": "Atualizar status", + "Set status": "Definir status", + "Hide": "Esconder", + "Help": "Ajuda", + "Remove for everyone": "Remover para todo mundo", + "Remove for me": "Remover para mim", + "User Status": "Status do usuário", + "This homeserver would like to make sure you are not a robot.": "Este servidor local gostaria se certificar de que você não é um robô.", + "Confirm your identity by entering your account password below.": "Confirme sua identidade digitando sua senha abaixo.", + "Unable to validate homeserver/identity server": "Não foi possível validar seu servidor local/servidor de identidade", + "Server Name": "Nome do servidor", + "Enter password": "Digite a senha", + "Nice, strong password!": "Muito bem, uma senha forte!", + "Password is allowed, but unsafe": "Esta senha é permitida, mas não é segura", + "Not sure of your password? Set a new one": "Esqueceu sua senha? Defina uma nova", + "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Defina um e-mail para poder recuperar a conta. Este e-mail também pode ser usado para encontrar seus contatos.", + "Enter your custom homeserver URL What does this mean?": "Digite o URL de um servidor local O que isso significa?", + "Homeserver URL": "URL do servidor local", + "Identity Server URL": "URL do servidor de identidade", + "Other servers": "Outros servidores", + "Free": "Gratuito", + "Find other public servers or use a custom server": "Encontre outros servidores públicos ou use um servidor personalizado", + "Sign in to your Matrix account on %(serverName)s": "Faça login com sua conta Matrix em %(serverName)s", + "Sign in to your Matrix account on ": "Faça login com sua conta Matrix em ", + "Sign in with SSO": "Faça login com SSO (Login Único)", + "Please install Chrome, Firefox, or Safari for the best experience.": "Por favor, instale o Chrome, o Firefox ou o Safari para obter a melhor experiência de uso.", + "Couldn't load page": "Não foi possível carregar a página", + "This homeserver does not support communities": "Este servidor local não suporta o recurso de comunidades", + "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s não conseguiu obter a lista de protocolos do servidor local. O servidor local pode ser muito antigo para suportar redes de terceiros.", + "%(brand)s failed to get the public room list.": "%(brand)s não conseguiu obter a lista de salas públicas.", + "The homeserver may be unavailable or overloaded.": "O servidor local pode estar indisponível ou sobrecarregado.", + "Delete the room address %(alias)s and remove %(name)s from the directory?": "Excluir o endereço da sala %(alias)s e remover %(name)s da lista de salas?", + "delete the address.": "exclui o endereço.", + "%(brand)s does not know how to join a room on this network": "%(brand)s não sabe como entrar em uma sala desta rede", + "View": "Ver", + "Find a room…": "Encontrar uma sala…", + "Find a room… (e.g. %(exampleRoom)s)": "Encontrar uma sala… (por exemplo: %(exampleRoom)s)", + "Search rooms": "Buscar salas", + "You have %(count)s unread notifications in a prior version of this room.|other": "Você tem %(count)s notificações não lidas em uma versão anterior desta sala.", + "You have %(count)s unread notifications in a prior version of this room.|one": "Você tem %(count)s notificações não lidas em uma versão anterior desta sala.", + "Feedback": "Feedback", + "User menu": "Menu do usuário", + "Could not load user profile": "Não foi possível carregar o perfil do usuário", + "Session verified": "Sessão verificada", + "Your Matrix account on %(serverName)s": "Sua conta Matrix em %(serverName)s", + "Your Matrix account on ": "Sua conta Matrix em ", + "No identity server is configured: add one in server settings to reset your password.": "Nenhum servidor de identidade está configurado: adicione um nas configurações do servidor para redefinir sua senha.", + "A verification email will be sent to your inbox to confirm setting your new password.": "Um e-mail de verificação será enviado para sua caixa de entrada para confirmar sua nova senha.", + "Your password has been reset.": "Sua senha foi alterada.", + "You have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Você foi desconectado de todas as sessões e não receberá mais notificações. Para reativar as notificações, faça login novamente em cada aparelho.", + "Set a new password": "Digite uma nova senha", + "Invalid base_url for m.homeserver": "base_url inválido para m.homeserver", + "Invalid base_url for m.identity_server": "base_url inválido para m.identity_server", + "This account has been deactivated.": "Esta conta foi desativada.", + "Syncing...": "Sincronizando...", + "%(brand)s Web": "%(brand)s Web", + "Your new session is now verified. Other users will see it as trusted.": "Sua nova sessão agora está confirmada. Para outros usuários ela será vista como confiável.", + "Forgotten your password?": "Esqueceu sua senha?", + "Restore": "Restaurar", + "Copy": "Copiar", + "For maximum security, this should be different from your account password.": "Para segurança máxima, essa deve ser diferente da senha da sua conta.", + "Success!": "Pronto!", + "Disable": "Desativar", + "Space used:": "Espaço usado:", + "Navigation": "Navegação", + "Calls": "Chamadas", + "Esc": "Esc", + "Enter": "Enter" } From f233778095d55d455fefaa3ed900e0019781c016 Mon Sep 17 00:00:00 2001 From: HelaBasa Date: Thu, 30 Jul 2020 19:56:26 +0000 Subject: [PATCH 124/136] Added translation using Weblate (Sinhala) --- src/i18n/strings/si.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/i18n/strings/si.json diff --git a/src/i18n/strings/si.json b/src/i18n/strings/si.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/src/i18n/strings/si.json @@ -0,0 +1 @@ +{} From e953bfbf88ae81256e0a4b153cbc58455c119448 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 14:08:18 -0600 Subject: [PATCH 125/136] Replace flawed objectHasValueChange usage with objectHasDiff Fixes https://github.com/vector-im/riot-web/issues/14782 We need to check if the keys changed, not just the values. --- src/components/views/rooms/RoomSublist.tsx | 6 +++--- src/utils/objects.ts | 14 -------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 54766c58df..c6730e5a65 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -47,7 +47,7 @@ import { polyfillTouchEvent } from "../../../@types/polyfill"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore"; import { arrayHasOrderChange } from "../../../utils/arrays"; -import { objectExcluding, objectHasValueChange } from "../../../utils/objects"; +import { objectExcluding, objectHasDiff } from "../../../utils/objects"; import TemporaryTile from "./TemporaryTile"; import { ListNotificationState } from "../../../stores/notifications/ListNotificationState"; @@ -181,7 +181,7 @@ export default class RoomSublist extends React.Component { } public shouldComponentUpdate(nextProps: Readonly, nextState: Readonly): boolean { - if (objectHasValueChange(this.props, nextProps)) { + if (objectHasDiff(this.props, nextProps)) { // Something we don't care to optimize has updated, so update. return true; } @@ -189,7 +189,7 @@ export default class RoomSublist extends React.Component { // Do the same check used on props for state, without the rooms we're going to no-op const prevStateNoRooms = objectExcluding(this.state, ['rooms']); const nextStateNoRooms = objectExcluding(nextState, ['rooms']); - if (objectHasValueChange(prevStateNoRooms, nextStateNoRooms)) { + if (objectHasDiff(prevStateNoRooms, nextStateNoRooms)) { return true; } diff --git a/src/utils/objects.ts b/src/utils/objects.ts index 9dcc41ecd2..77e5f59418 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -58,20 +58,6 @@ export function objectShallowClone(a: any, propertyCloner?: (k: string, v: any) return newObj; } -/** - * Determines if the two objects, which are assumed to be of the same - * key shape, have a difference in their values. If a difference is - * determined, true is returned. - * @param a The first object. Must be defined. - * @param b The second object. Must be defined. - * @returns True if there's a perceptual difference in the object's values. - */ -export function objectHasValueChange(a: any, b: any): boolean { - const aValues = Object.values(a); - const bValues = Object.values(b); - return arrayHasDiff(aValues, bValues); -} - /** * Determines if any keys were added, removed, or changed between two objects. * For changes, simple triple equal comparisons are done, not in-depth From 5b15d128652aa66a85d599f4df61f1a67ccb7b80 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 14:18:54 -0600 Subject: [PATCH 126/136] Ensure list visibility changes get counted as list changes Fixes https://github.com/vector-im/riot-web/issues/14799 We were checking to see if the tags were visible at render time, but we needed to ensure that they were(n't) included when checking for diffs. This introduces a new kind of object cloning for semantic reasons. This also fixes the selection indicator being a bit off on custom tags. --- res/css/structures/_CustomRoomTagPanel.scss | 2 +- src/components/views/rooms/RoomList.tsx | 15 ++++++++++----- src/utils/objects.ts | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/res/css/structures/_CustomRoomTagPanel.scss b/res/css/structures/_CustomRoomTagPanel.scss index 135a51c7cd..3feb2565be 100644 --- a/res/css/structures/_CustomRoomTagPanel.scss +++ b/res/css/structures/_CustomRoomTagPanel.scss @@ -54,5 +54,5 @@ limitations under the License. position: absolute; left: -9px; border-radius: 0 3px 3px 0; - top: 12px; // just feels right (see comment above about designs needing to be updated) + top: 5px; // just feels right (see comment above about designs needing to be updated) } diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index f4b9de93b1..09fdbf0864 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -42,7 +42,7 @@ import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNo import SettingsStore from "../../../settings/SettingsStore"; import CustomRoomTagStore from "../../../stores/CustomRoomTagStore"; import { arrayFastClone, arrayHasDiff } from "../../../utils/arrays"; -import { objectShallowClone } from "../../../utils/objects"; +import { objectShallowClone, objectWithOnly } from "../../../utils/objects"; interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; @@ -220,7 +220,12 @@ export default class RoomList extends React.PureComponent { } const previousListIds = Object.keys(this.state.sublists); - const newListIds = Object.keys(newLists); + const newListIds = Object.keys(newLists).filter(t => { + if (!isCustomTag(t)) return true; // always include non-custom tags + + // if the tag is custom though, only include it if it is enabled + return CustomRoomTagStore.getTags()[t]; + }); let doUpdate = arrayHasDiff(previousListIds, newListIds); if (!doUpdate) { @@ -240,7 +245,8 @@ export default class RoomList extends React.PureComponent { if (doUpdate) { // We have to break our reference to the room list store if we want to be able to // diff the object for changes, so do that. - const sublists = objectShallowClone(newLists, (k, v) => arrayFastClone(v)); + const newSublists = objectWithOnly(newLists, newListIds); + const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v)); this.setState({sublists}, () => { this.props.onResize(); @@ -288,8 +294,7 @@ export default class RoomList extends React.PureComponent { const tagOrder = TAG_ORDER.reduce((p, c) => { if (c === CUSTOM_TAGS_BEFORE_TAG) { const customTags = Object.keys(this.state.sublists) - .filter(t => isCustomTag(t)) - .filter(t => CustomRoomTagStore.getTags()[t]); // isSelected + .filter(t => isCustomTag(t)); p.push(...customTags); } p.push(c); diff --git a/src/utils/objects.ts b/src/utils/objects.ts index 77e5f59418..ddd9830832 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -36,6 +36,23 @@ export function objectExcluding(a: any, props: string[]): any { }, {}); } +/** + * Gets a new object which represents the provided object, with only some properties + * included. + * @param a The object to clone properties of. Must be defined. + * @param props The property names to keep. + * @returns The new object with only the provided properties. + */ +export function objectWithOnly(a: any, props: string[]): any { + const existingProps = Object.keys(a); + const diff = arrayDiff(existingProps, props); + if (diff.removed.length === 0) { + return objectShallowClone(a); + } else { + return objectExcluding(a, diff.removed); + } +} + /** * Clones an object to a caller-controlled depth. When a propertyCloner is supplied, the * object's properties will be passed through it with the return value used as the new From da05cac1b6d189fe4809cbe3ea100a25cb795851 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 14:33:38 -0600 Subject: [PATCH 127/136] Listen for our own membership changes on notification states Fixes https://github.com/vector-im/riot-web/issues/14798 (part 1) When we transition from invite to not-invite we need to ensure we clear the invite notification state. --- src/stores/notifications/RoomNotificationState.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stores/notifications/RoomNotificationState.ts b/src/stores/notifications/RoomNotificationState.ts index dc38f8bf0f..3fadbe7d7a 100644 --- a/src/stores/notifications/RoomNotificationState.ts +++ b/src/stores/notifications/RoomNotificationState.ts @@ -31,6 +31,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy this.room.on("Room.receipt", this.handleReadReceipt); this.room.on("Room.timeline", this.handleRoomEventUpdate); this.room.on("Room.redaction", this.handleRoomEventUpdate); + this.room.on("Room.myMembership", this.handleMembershipUpdate); MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate); MatrixClientPeg.get().on("accountData", this.handleAccountDataUpdate); this.updateNotificationState(); @@ -45,6 +46,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy this.room.removeListener("Room.receipt", this.handleReadReceipt); this.room.removeListener("Room.timeline", this.handleRoomEventUpdate); this.room.removeListener("Room.redaction", this.handleRoomEventUpdate); + this.room.removeListener("Room.myMembership", this.handleMembershipUpdate); if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate); MatrixClientPeg.get().removeListener("accountData", this.handleAccountDataUpdate); @@ -57,6 +59,10 @@ export class RoomNotificationState extends NotificationState implements IDestroy this.updateNotificationState(); }; + private handleMembershipUpdate = () => { + this.updateNotificationState(); + }; + private handleRoomEventUpdate = (event: MatrixEvent) => { const roomId = event.getRoomId(); From 49abfc1fb20235e7c3ea3ac55cd0321f9635c0eb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jul 2020 15:06:04 -0600 Subject: [PATCH 128/136] Ensure sublists are updated when rooms are removed from them Fixes https://github.com/vector-im/riot-web/issues/14798 (part 2) This is in two parts itself: The `RoomSublist` needs to break its references to the `RoomListStore`, so it now clones the room arrays. The `Algorithm` is the other part, which is slightly more complicated. It turns out that we weren't handling splicing as a change in the `ImportanceAlgorithm`, therefore the `Algorithm` wasn't really feeling like it needed to change anything. Further, the `Algorithm` was using the wrong reference to where it should be dumping rooms (`this.cachedRooms` is a getter which returns a different object depending on conditions), so having fixed that we need to ensure that the filtered and sticky maps are also updated when we remove a room. Because we send the new tag through a Timeline update, we'll end up updating the tag later on and don't need to update the filter and sticky collections. --- src/components/views/rooms/RoomSublist.tsx | 6 +++--- src/stores/room-list/algorithms/Algorithm.ts | 8 +++++--- .../algorithms/list-ordering/ImportanceAlgorithm.ts | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index c6730e5a65..f6d0d1c22e 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -46,7 +46,7 @@ import { Direction } from "re-resizable/lib/resizer"; import { polyfillTouchEvent } from "../../../@types/polyfill"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore"; -import { arrayHasOrderChange } from "../../../utils/arrays"; +import { arrayFastClone, arrayHasOrderChange } from "../../../utils/arrays"; import { objectExcluding, objectHasDiff } from "../../../utils/objects"; import TemporaryTile from "./TemporaryTile"; import { ListNotificationState } from "../../../stores/notifications/ListNotificationState"; @@ -115,7 +115,7 @@ export default class RoomSublist extends React.Component { isResizing: false, isExpanded: this.isBeingFiltered ? this.isBeingFiltered : !this.layout.isCollapsed, height: 0, // to be fixed in a moment, we need `rooms` to calculate this. - rooms: RoomListStore.instance.orderedLists[this.props.tagId] || [], + rooms: arrayFastClone(RoomListStore.instance.orderedLists[this.props.tagId] || []), }; // Why Object.assign() and not this.state.height? Because TypeScript says no. this.state = Object.assign(this.state, {height: this.calculateInitialHeight()}); @@ -255,7 +255,7 @@ export default class RoomSublist extends React.Component { } const currentRooms = this.state.rooms; - const newRooms = RoomListStore.instance.orderedLists[this.props.tagId] || []; + const newRooms = arrayFastClone(RoomListStore.instance.orderedLists[this.props.tagId] || []); if (arrayHasOrderChange(currentRooms, newRooms)) { stateUpdates.rooms = newRooms; } diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 9b2779d900..2654a8b460 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -715,7 +715,9 @@ export class Algorithm extends EventEmitter { const algorithm: OrderingAlgorithm = this.algorithms[rmTag]; if (!algorithm) throw new Error(`No algorithm for ${rmTag}`); await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); - this.cachedRooms[rmTag] = algorithm.orderedRooms; + this._cachedRooms[rmTag] = algorithm.orderedRooms; + this.recalculateFilteredRoomsForTag(rmTag); // update filter to re-sort the list + this.recalculateStickyRoom(rmTag); // update sticky room to make sure it moves if needed } for (const addTag of diff.added) { if (SettingsStore.getValue("advancedRoomListLogging")) { @@ -725,7 +727,7 @@ export class Algorithm extends EventEmitter { const algorithm: OrderingAlgorithm = this.algorithms[addTag]; if (!algorithm) throw new Error(`No algorithm for ${addTag}`); await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom); - this.cachedRooms[addTag] = algorithm.orderedRooms; + this._cachedRooms[addTag] = algorithm.orderedRooms; } // Update the tag map so we don't regen it in a moment @@ -821,7 +823,7 @@ export class Algorithm extends EventEmitter { if (!algorithm) throw new Error(`No algorithm for ${tag}`); await algorithm.handleRoomUpdate(room, cause); - this.cachedRooms[tag] = algorithm.orderedRooms; + this._cachedRooms[tag] = algorithm.orderedRooms; // Flag that we've done something this.recalculateFilteredRoomsForTag(tag); // update filter to re-sort the list diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index 9cb7679e89..e4aa5ff06f 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -136,6 +136,9 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { } else { throw new Error(`Unhandled splice: ${cause}`); } + + // changes have been made if we made it here, so say so + return true; } public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { From 15cc1475c51f201a2b6aeb1a3388f53bad784cb1 Mon Sep 17 00:00:00 2001 From: HelaBasa Date: Thu, 30 Jul 2020 19:57:30 +0000 Subject: [PATCH 129/136] Translated using Weblate (Sinhala) Currently translated at 0.3% (6 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/si/ --- src/i18n/strings/si.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/si.json b/src/i18n/strings/si.json index 0967ef424b..11da7b7e4c 100644 --- a/src/i18n/strings/si.json +++ b/src/i18n/strings/si.json @@ -1 +1,8 @@ -{} +{ + "This email address is already in use": "මෙම විද්‍යුත් තැපැල් ලිපිනය දැනටමත් භාවිතයේ පවතී", + "This phone number is already in use": "මෙම දුරකථන අංකය දැනටමත් භාවිතයේ පවතී", + "Use Single Sign On to continue": "ඉදිරියට යාමට තනි පුරනය වීම භාවිතා කරන්න", + "Confirm adding this email address by using Single Sign On to prove your identity.": "ඔබගේ අනන්‍යතාවය සනාථ කිරීම සඳහා තනි පුරනය භාවිතා කිරීමෙන් මෙම විද්‍යුත් තැපැල් ලිපිනය එක් කිරීම තහවුරු කරන්න.", + "Confirm": "තහවුරු කරන්න", + "Add Email Address": "විද්‍යුත් තැපැල් ලිපිනය එක් කරන්න" +} From d72394bacc597fe752d040b5bc9fc06d523c3f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Fri, 31 Jul 2020 09:08:26 +0000 Subject: [PATCH 130/136] Translated using Weblate (Estonian) Currently translated at 99.8% (2319 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index cf441cd241..271893b862 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1714,10 +1714,10 @@ "%(role)s in %(roomName)s": "%(role)s jututoas %(roomName)s", "Failed to change power level": "Õiguste muutmine ei õnnestunud", "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Sa ei saa seda muudatust hiljem tagasi pöörata, sest annad teisele kasutajale samad õigused, mis sinul on.", - "Deactivate user?": "Kas blokeerime kasutaja?", - "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?": "Kasutaja blokeerimisel logitakse ta automaatselt välja ning ei lubata enam sisse logida. Lisaks lahkub ta kõikidest jututubadest, mille liige ta parasjagu on. Seda tegevust ei saa tagasi pöörata. Kas sa oled ikka kindel, et soovid selle kasutaja blokeerida?", - "Deactivate user": "Blokeeri kasutaja", - "Failed to deactivate user": "Kasutaja blokeerimine ei õnnestunud", + "Deactivate user?": "Kas deaktiveerime kasutajakonto?", + "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?": "Kasutaja deaktiveerimisel logitakse ta automaatselt välja ning ei lubata enam sisse logida. Lisaks lahkub ta kõikidest jututubadest, mille liige ta parasjagu on. Seda tegevust ei saa tagasi pöörata. Kas sa oled ikka kindel, et soovid selle kasutaja kõijkalt eemaldada?", + "Deactivate user": "Deaktiveeri kasutaja", + "Failed to deactivate user": "Kasutaja deaktiveerimine ei õnnestunud", "This client does not support end-to-end encryption.": "See klient ei toeta läbivat krüptimist.", "Security": "Turvalisus", "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Selle vidina kasutamisel võidakse jagada andmeid saitidega %(widgetDomain)s ning sinu vidinahalduriga.", From b8f93df22d5fd99dab5fe58f8b01bc93417a0293 Mon Sep 17 00:00:00 2001 From: call_xz Date: Fri, 31 Jul 2020 08:48:22 +0000 Subject: [PATCH 131/136] Translated using Weblate (Japanese) Currently translated at 58.2% (1353 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 9611ea9a3e..ee3871fc0f 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -1365,5 +1365,10 @@ "Composer": "入力欄", "Sort by": "並び替え", "List options": "一覧の設定", - "Use Single Sign On to continue": "シングルサインオンを使用して続行" + "Use Single Sign On to continue": "シングルサインオンを使用して続行", + "Accept to continue:": " に同意して続行:", + "Always show the window menu bar": "常にウィンドウメニューバーを表示する", + "Create room": "部屋を作成", + "Show %(count)s more|other": "さらに %(count)s 件を表示", + "Show %(count)s more|one": "さらに %(count)s 件を表示" } From 235295249f22658ed41b0336e88e7e50ca9e190d Mon Sep 17 00:00:00 2001 From: strix aluco Date: Thu, 30 Jul 2020 22:59:09 +0000 Subject: [PATCH 132/136] Translated using Weblate (Ukrainian) Currently translated at 46.9% (1091 of 2324 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 55 ++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index a6c510d616..2272ed49eb 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -339,9 +339,9 @@ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s розблокував/ла %(targetName)s.", "%(senderName)s kicked %(targetName)s.": "%(senderName)s викинув/ла %(targetName)s.", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s відкликав/ла запрошення %(targetName)s.", - "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s надіслав/ла зображення.", - "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s призначив/ла основну адресу цієї кімнати: %(address)s.", - "%(senderName)s removed the main address for this room.": "%(senderName)s вилучив/ла основу адресу цієї кімнати.", + "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s надіслав(-ла) зображення.", + "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s призначив(-ла) основну адресу цієї кімнати: %(address)s.", + "%(senderName)s removed the main address for this room.": "%(senderName)s прибрав(-ла) основу адресу цієї кімнати.", "Someone": "Хтось", "(not supported by this browser)": "(не підтримується цією веб-переглядачкою)", "(could not connect media)": "(не можливо під'єднати медіа)", @@ -469,7 +469,7 @@ "This room has no topic.": "Ця кімната не має теми.", "Sets the room name": "Встановлює назву кімнати", "Use an identity server": "Використовувати сервер ідентифікації", - "Use an identity server to invite by email. Manage in Settings.": "Використовувати сервер ідентифікації для запрошень через е-пошту. Керується у налаштуваннях.", + "Use an identity server to invite by email. Manage in Settings.": "Використовувати сервер ідентифікації для запрошень через е-пошту. Керуйте у налаштуваннях.", "Unbans user with given ID": "Розблоковує користувача з вказаним ідентифікатором", "Adds a custom widget by URL to the room": "Додає власний віджет до кімнати за посиланням", "Please supply a https:// or http:// widget URL": "Вкажіть посилання на віджет — https:// або http://", @@ -639,9 +639,9 @@ "Room Settings - %(roomName)s": "Налаштування кімнати - %(roomName)s", "A verification email will be sent to your inbox to confirm setting your new password.": "Ми відправимо перевіряльний електронний лист до вас для підтвердження зміни пароля.", "To return to your account in future you need to set a password": "Щоб повернутися до своєї обліківки в майбутньому, вам потрібно встановити пароль", - "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Використайте сервер ідентифікації, щоб запросити е-поштою. Нажміть продовжити, щоб використовувати звичайний сервер ідентифікації(%(defaultIdentityServerName)s) або змініть у Налаштуваннях.", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Використовувати сервер ідентифікації, щоб запрошувати через е-пошту. Натисніть \"Продовжити\", щоб використовувати типовий сервер ідентифікації (%(defaultIdentityServerName)s) або змініть його у налаштуваннях.", "Joins room with given address": "Приєднатися до кімнати зі вказаною адресою", - "Unrecognised room address:": "Не вдалося знайти адресу кімнати:", + "Unrecognised room address:": "Невпізнана адреса кімнати:", "Command failed": "Не вдалося виконати команду", "Could not find user in room": "Не вдалося знайти користувача в кімнаті", "Please supply a widget URL or embed code": "Вкажіть URL або код вставки віджету", @@ -657,29 +657,29 @@ "Send a bug report with logs": "Надіслати звіт про ваду разом з журналами", "Opens chat with the given user": "Відкриває балачку з вказаним користувачем", "Sends a message to the given user": "Надсилає повідомлення вказаному користувачеві", - "%(senderName)s made no change.": "%(senderName)s не вніс змін.", + "%(senderName)s made no change.": "%(senderName)s не запровадив(-ла) жодних змін.", "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s змінив(ла) назву кімнати з %(oldRoomName)s на %(newRoomName)s.", - "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s модернізував цю кімнату.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s поліпшив(-ла) цю кімнату.", "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s зробив(-ла) кімнату відкритою для всіх, хто знає посилання.", - "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s робить кімнату доступною лише по запрошенню.", - "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s змінює правило входу на \"%(rule)s\"", - "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s дозволяє гостям приєднуватися до кімнати.", - "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s забороняє гостям приєднуватися до кімнати.", - "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s змінює гостьовий доступ на \"%(rule)s\"", - "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s увімкнуто для %(groups)s у цій кімнаті.", - "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s вимкнуто для %(groups)s в цій кімнаті.", - "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s додав(ла) альтернативні адреси %(addresses)s для цієї кімнати.", - "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s добавив(ла)альтернативні адреси %(addresses)s для цієї кімнати.", - "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s видалив(ла) альтернативні адреси %(addresses)s для цієї кімнати.", - "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s видалив(ла) альтернативні адреси %(addresses)s для цієї кімнати.", - "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s змінив(ла) альтернативні адреси для цієї кімнати.", - "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s змінив(ла) головні та альтернативні адреси для цієї кімнати.", - "%(senderName)s changed the addresses for this room.": "%(senderName)s змінив(ла) адреси для цієї кімнати.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s зробив(-ла) кімнату доступною лише за запрошеннями.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s змінив(-ла) правило приєднування на \"%(rule)s\"", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s дозволив(-ла) гостям приєднуватися до кімнати.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s заборонив(-ла) гостям приєднуватися до кімнати.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s змінив(-ла) гостьовий доступ на \"%(rule)s\"", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s увімкнув(-ла) значок для %(groups)s у цій кімнаті.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s вимкнув(-ла) значок для %(groups)s в цій кімнаті.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s додав(-ла) альтернативні адреси %(addresses)s для цієї кімнати.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s додав(-ла) альтернативні адреси %(addresses)s для цієї кімнати.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s прибрав(-ла) альтернативні адреси %(addresses)s для цієї кімнати.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s прибрав(-ла) альтернативні адреси %(addresses)s для цієї кімнати.", + "%(senderName)s changed the alternative addresses for this room.": "%(senderName)s змінив(-ла) альтернативні адреси для цієї кімнати.", + "%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s змінив(-ла) головні та альтернативні адреси для цієї кімнати.", + "%(senderName)s changed the addresses for this room.": "%(senderName)s змінив(-ла) адреси для цієї кімнати.", "%(senderName)s placed a voice call.": "%(senderName)s розпочав(-ла) голосовий виклик.", "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s розпочав(-ла) голосовий виклик. (не підтримується цим переглядачем)", "%(senderName)s placed a video call.": "%(senderName)s розпочав(-ла) відеовиклик.", "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s розпочав(-ла) відеовиклик. (не підтримується цим переглядачем)", - "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s відкликав(ла) запрошення %(targetDisplayName)s приєднання до кімнати.", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s відкликав(-ла) запрошення %(targetDisplayName)s приєднання до кімнати.", "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s видалив(ла) правило блокування користувачів по шаблону %(glob)s", "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s видалив(ла) правило блокування кімнат по шаблону %(glob)s", "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s видалив(ла) правило блокування серверів по шаблону %(glob)s", @@ -1093,5 +1093,12 @@ "This session is encrypting history using the new recovery method.": "Цей сеанс зашифровує історію новим відновлювальним засобом.", "Set up Secure Messages": "Налаштувати захищені повідомлення", "Recovery Method Removed": "Відновлювальний засіб було видалено", - "This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Цей сеанс виявив, що ваші відновлювальні парольна фраза та ключ від захищених повідомлень були видалені." + "This session has detected that your recovery passphrase and key for Secure Messages have been removed.": "Цей сеанс виявив, що ваші відновлювальні парольна фраза та ключ від захищених повідомлень були видалені.", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s увімкнув(-ла) значок для %(newGroups)s та вимкнув(-ла) значок для %(oldGroups)s у цій кімнаті.", + "New version available. Update now.": "Доступна нова версія. Оновити зараз", + "Upgrade public room": "Поліпшити відкриту кімнату", + "Restore your key backup to upgrade your encryption": "Відновіть резервну копію вашого ключа щоб поліпшити шифрування", + "You'll need to authenticate with the server to confirm the upgrade.": "Ви матимете пройти розпізнання на сервері щоб підтвердити поліпшування.", + "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Поліпште цей сеанс щоб уможливити звіряння інших сеансів, надаючи їм доступ до зашифрованих повідомлень та позначуючи їх довіреними для інших користувачів.", + "Upgrade your encryption": "Поліпшити ваше шифрування" } From b017983efd5dfd4fb9a47357011e487544280c25 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 31 Jul 2020 11:24:53 +0000 Subject: [PATCH 133/136] Translated using Weblate (Hungarian) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/element-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index d72787a932..209c237c0c 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2388,5 +2388,21 @@ "You’re already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at element.io/get-started.": "Már be vagy jelentkezve és ez rendben van, de minden platformon az alkalmazás legfrissebb verziójának beszerzéséhez látogass el ide: element.io/get-started.", "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use %(brand)s with an existing Matrix account on a different homeserver.": "Használhatod a más szerver opciót, hogy egy másik matrix szerverre jelentkezz be amihez megadod a szerver url címét. Ezzel használhatod %(brand)s klienst egy már létező Matrix fiókkal egy másik matrix szerveren.", "Enter the location of your Element Matrix Services homeserver. It may use your own domain name or be a subdomain of element.io.": "Add meg az Element Matrix Services matrix szerveredet. Használhatod a saját domain-edet vagy az element.io al-domain-jét.", - "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s" + "* %(senderName)s %(emote)s": "* %(senderName)s %(emote)s", + "The person who invited you already left the room.": "Aki meghívott a szobába már távozott.", + "The person who invited you already left the room, or their server is offline.": "Aki meghívott a szobába már távozott, vagy a szervere elérhetetlen.", + "Change notification settings": "Értesítési beállítások megváltoztatása", + "Your server isn't responding to some requests.": "A szervered nem válaszol néhány kérésre.", + "You're all caught up.": "Mindent elolvastál.", + "Server isn't responding": "A szerver nem válaszol", + "Your server isn't responding to some of your requests. Below are some of the most likely reasons.": "A szervered néhány kérésre nem válaszol. Alább felsorolunk pár lehetséges okot.", + "The server (%(serverName)s) took too long to respond.": "A szervernek (%(serverName)s) túl hosszú időbe telt válaszolni.", + "Your firewall or anti-virus is blocking the request.": "A tűzfalad vagy víruskeresőd blokkolja a kérést.", + "A browser extension is preventing the request.": "Egy böngésző kiterjesztés megakadályozza a kérést.", + "The server is offline.": "A szerver nem működik.", + "The server has denied your request.": "A szerver elutasította a kérést.", + "Your area is experiencing difficulties connecting to the internet.": "Probléma az Internet elérésben.", + "A connection error occurred while trying to contact the server.": "Kapcsolati hiba lépett fel miközben a szervert próbáltad elérni.", + "The server is not configured to indicate what the problem is (CORS).": "A szerver nincs beállítva, hogy megmutassa mi okozhatta a hibát (CORS).", + "Recent changes that have not yet been received": "Legutóbbi változások amik még nem érkeztek meg" } From 561b8752b95fbd8b157b014068c0eec6ee4db313 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 31 Jul 2020 14:23:24 +0200 Subject: [PATCH 134/136] actually center the icon --- res/css/views/rooms/_JumpToBottomButton.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_JumpToBottomButton.scss b/res/css/views/rooms/_JumpToBottomButton.scss index cb3803fe5b..c4408b4dd1 100644 --- a/res/css/views/rooms/_JumpToBottomButton.scss +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -69,6 +69,6 @@ limitations under the License. right: 0; mask: url('$(res)/img/icon-jump-to-bottom.svg'); mask-repeat: no-repeat; - mask-position: 9px 14px; + mask-position: center; background: $muted-fg-color; } From 29e17af2f88920591bac87ad835cdc73c0b18af4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 31 Jul 2020 14:30:38 +0200 Subject: [PATCH 135/136] scale the icon a bit, as centering changed it's size --- res/css/views/rooms/_JumpToBottomButton.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_JumpToBottomButton.scss b/res/css/views/rooms/_JumpToBottomButton.scss index c4408b4dd1..4659442d9b 100644 --- a/res/css/views/rooms/_JumpToBottomButton.scss +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -70,5 +70,6 @@ limitations under the License. mask: url('$(res)/img/icon-jump-to-bottom.svg'); mask-repeat: no-repeat; mask-position: center; + mask-size: 50%; background: $muted-fg-color; } From 6b667a93bd0adfc3bda87bb8c65d1091ab884472 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 31 Jul 2020 14:39:34 +0200 Subject: [PATCH 136/136] also center chevron in top unread button --- res/css/views/rooms/_TopUnreadMessagesBar.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/res/css/views/rooms/_TopUnreadMessagesBar.scss b/res/css/views/rooms/_TopUnreadMessagesBar.scss index c0545660c2..d9970ef037 100644 --- a/res/css/views/rooms/_TopUnreadMessagesBar.scss +++ b/res/css/views/rooms/_TopUnreadMessagesBar.scss @@ -28,7 +28,7 @@ limitations under the License. content: ""; position: absolute; top: -8px; - left: 11px; + left: 10.5px; width: 4px; height: 4px; border-radius: 16px; @@ -49,11 +49,12 @@ limitations under the License. .mx_TopUnreadMessagesBar_scrollUp::before { content: ""; position: absolute; - width: 38px; - height: 38px; + width: 36px; + height: 36px; mask-image: url('$(res)/img/icon-jump-to-first-unread.svg'); mask-repeat: no-repeat; - mask-position: 9px 13px; + mask-position: center; + mask-size: 50%; background: $muted-fg-color; }