From 5f0501a18aa9c6d1d22cf0ff4c4cadda931d8bec Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 6 Sep 2023 17:14:18 +0100 Subject: [PATCH 1/8] Fix double-tooltips --- src/components/views/rooms/RoomHeader.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index 884314c39f..d0581178de 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -178,7 +178,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { { evt.stopPropagation(); placeCall(room, CallType.Voice, voiceCallType); @@ -191,7 +191,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { { evt.stopPropagation(); placeCall(room, CallType.Video, videoCallType); @@ -207,7 +207,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { evt.stopPropagation(); showOrHidePanel(RightPanelPhases.ThreadPanel); }} - title={_t("common|threads")} + aria-label={_t("common|threads")} > @@ -219,7 +219,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { evt.stopPropagation(); showOrHidePanel(RightPanelPhases.NotificationPanel); }} - title={_t("Notifications")} + aria-label={_t("Notifications")} > From 5a2595a093cb81530d3e139b3badad3cfd23d0d0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 6 Sep 2023 17:41:52 +0100 Subject: [PATCH 2/8] Rebuild hook around room header call management And tweak behaviour around ongoing yet unpinned calls to add a shortcut to quickly pin --- src/components/views/rooms/RoomHeader.tsx | 16 +- src/hooks/room/useRoomCall.ts | 204 ++++++++++++++++++++++ src/hooks/room/useRoomCallStatus.ts | 154 ---------------- src/hooks/useRoomState.ts | 18 +- src/utils/room/placeCall.ts | 2 +- 5 files changed, 222 insertions(+), 172 deletions(-) create mode 100644 src/hooks/room/useRoomCall.ts delete mode 100644 src/hooks/room/useRoomCallStatus.ts diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index d0581178de..49591ef414 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -23,7 +23,6 @@ import { Icon as NotificationsIcon } from "@vector-im/compound-design-tokens/ico import { Icon as VerifiedIcon } from "@vector-im/compound-design-tokens/icons/verified.svg"; import { Icon as ErrorIcon } from "@vector-im/compound-design-tokens/icons/error.svg"; import { Icon as PublicIcon } from "@vector-im/compound-design-tokens/icons/public.svg"; -import { CallType } from "matrix-js-sdk/src/webrtc/call"; import { EventType, JoinRule, type Room } from "matrix-js-sdk/src/matrix"; import { useRoomName } from "../../../hooks/useRoomName"; @@ -36,13 +35,12 @@ import { useRoomMemberCount, useRoomMembers } from "../../../hooks/useRoomMember import { _t } from "../../../languageHandler"; import { Flex } from "../../utils/Flex"; import { Box } from "../../utils/Box"; -import { useRoomCallStatus } from "../../../hooks/room/useRoomCallStatus"; +import { useRoomCall } from "../../../hooks/room/useRoomCall"; import { useRoomThreadNotifications } from "../../../hooks/room/useRoomThreadNotifications"; import { NotificationColor } from "../../../stores/notifications/NotificationColor"; import { useGlobalNotificationState } from "../../../hooks/useGlobalNotificationState"; import SdkConfig from "../../../SdkConfig"; import { useFeatureEnabled } from "../../../hooks/useSettings"; -import { placeCall } from "../../../utils/room/placeCall"; import { useEncryptionStatus } from "../../../hooks/useEncryptionStatus"; import { E2EStatus } from "../../../utils/ShieldUtils"; import FacePile from "../elements/FacePile"; @@ -84,7 +82,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { const members = useRoomMembers(room, 2500); const memberCount = useRoomMemberCount(room, { throttleWait: 2500 }); - const { voiceCallDisabledReason, voiceCallType, videoCallDisabledReason, videoCallType } = useRoomCallStatus(room); + const { voiceCallDisabledReason, voiceCallClick, videoCallDisabledReason, videoCallClick } = useRoomCall(room); const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); /** @@ -179,10 +177,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { { - evt.stopPropagation(); - placeCall(room, CallType.Voice, voiceCallType); - }} + onClick={voiceCallClick} > @@ -192,10 +187,7 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element { { - evt.stopPropagation(); - placeCall(room, CallType.Video, videoCallType); - }} + onClick={videoCallClick} > diff --git a/src/hooks/room/useRoomCall.ts b/src/hooks/room/useRoomCall.ts new file mode 100644 index 0000000000..2d0e36d2a3 --- /dev/null +++ b/src/hooks/room/useRoomCall.ts @@ -0,0 +1,204 @@ +/* +Copyright 2023 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 { Room } from "matrix-js-sdk/src/matrix"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { CallType } from "matrix-js-sdk/src/webrtc/call"; + +import { useFeatureEnabled } from "../useSettings"; +import SdkConfig from "../../SdkConfig"; +import { useEventEmitter, useEventEmitterState } from "../useEventEmitter"; +import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../LegacyCallHandler"; +import { useWidgets } from "../../components/views/right_panel/RoomSummaryCard"; +import { WidgetType } from "../../widgets/WidgetType"; +import { useCall } from "../useCall"; +import { useRoomMemberCount } from "../useRoomMembers"; +import { ElementCall } from "../../models/Call"; +import { placeCall } from "../../utils/room/placeCall"; +import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore"; +import { useRoomState } from "../useRoomState"; +import { _t } from "../../languageHandler"; + +export type PlatformCallType = "element_call" | "jitsi_or_element_call" | "legacy_or_jitsi"; + +const enum State { + NoCall, + NoOneHere, + NoPermission, + Unpinned, + Ongoing, +} + +/** + * Utility hook for resolving state and click handlers for Voice & Video call buttons in the room header + * @param room the room to track + * @returns the call button attributes for the given room + */ +export const useRoomCall = ( + room: Room, +): { + voiceCallDisabledReason: string | null; + voiceCallClick(evt: React.MouseEvent): void; + videoCallDisabledReason: string | null; + videoCallClick(evt: React.MouseEvent): void; +} => { + const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); + const useElementCallExclusively = useMemo(() => { + return SdkConfig.get("element_call").use_exclusively; + }, []); + + const hasLegacyCall = useEventEmitterState( + LegacyCallHandler.instance, + LegacyCallHandlerEvent.CallsChanged, + () => LegacyCallHandler.instance.getCallForRoom(room.roomId) !== null, + ); + + const widgets = useWidgets(room); + const jitsiWidget = useMemo(() => widgets.find((widget) => WidgetType.JITSI.matches(widget.type)), [widgets]); + const hasJitsiWidget = !!jitsiWidget; + + const groupCall = useCall(room.roomId); + const hasGroupCall = groupCall !== null; + + const memberCount = useRoomMemberCount(room); + + const [mayEditWidgets, mayCreateElementCalls] = useRoomState(room, () => [ + room.currentState.mayClientSendStateEvent("im.vector.modular.widgets", room.client), + room.currentState.mayClientSendStateEvent(ElementCall.CALL_EVENT_TYPE.name, room.client), + ]); + + const callType = useMemo((): PlatformCallType => { + if (groupCallsEnabled) { + if (hasGroupCall) { + return "jitsi_or_element_call"; + } + if (mayCreateElementCalls && hasJitsiWidget) { + return "jitsi_or_element_call"; + } + if (useElementCallExclusively || mayCreateElementCalls) { + // Looks like for Audio this was previously legacy_or_jitsi + return "element_call"; + } + if (mayEditWidgets) { + return "jitsi_or_element_call"; + } + } + return "legacy_or_jitsi"; + }, [ + groupCallsEnabled, + hasGroupCall, + mayCreateElementCalls, + hasJitsiWidget, + useElementCallExclusively, + mayEditWidgets, + ]); + const widget = callType === "element_call" ? groupCall?.widget : jitsiWidget; + + const [canPinWidget, setCanPinWidget] = useState(false); + const [widgetPinned, setWidgetPinned] = useState(false); + const promptPinWidget = canPinWidget && !widgetPinned; + + const updateWidgetState = useCallback((): void => { + setCanPinWidget(WidgetLayoutStore.instance.canAddToContainer(room, Container.Top)); + setWidgetPinned(!!widget && WidgetLayoutStore.instance.isInContainer(room, widget, Container.Top)); + }, [room, widget]); + + useEventEmitter(WidgetLayoutStore.instance, WidgetLayoutStore.emissionForRoom(room), updateWidgetState); + useEffect(() => { + updateWidgetState(); + }, [room, jitsiWidget, groupCall, updateWidgetState]); + + const state = useMemo((): State => { + if (hasGroupCall || hasJitsiWidget) { + return promptPinWidget ? State.Unpinned : State.Ongoing; + } + if (hasLegacyCall) { + return State.Ongoing; + } + + if (memberCount <= 1) { + return State.NoOneHere; + } + + if (!mayCreateElementCalls && !mayEditWidgets) { + return State.NoPermission; + } + + return State.NoCall; + }, [ + hasGroupCall, + hasJitsiWidget, + hasLegacyCall, + mayCreateElementCalls, + mayEditWidgets, + memberCount, + promptPinWidget, + ]); + + const voiceCallClick = useCallback( + (evt: React.MouseEvent): void => { + evt.stopPropagation(); + if (widget && promptPinWidget) { + WidgetLayoutStore.instance.moveToContainer(room, widget, Container.Top); + } else { + placeCall(room, CallType.Voice, callType); + } + }, + [promptPinWidget, room, widget, callType], + ); + const videoCallClick = useCallback( + (evt: React.MouseEvent): void => { + evt.stopPropagation(); + if (widget && promptPinWidget) { + WidgetLayoutStore.instance.moveToContainer(room, widget, Container.Top); + } else { + placeCall(room, CallType.Video, callType); + } + }, + [widget, promptPinWidget, room, callType], + ); + + let voiceCallDisabledReason: string | null; + let videoCallDisabledReason: string | null; + switch (state) { + case State.NoPermission: + voiceCallDisabledReason = _t("You do not have permission to start voice calls"); + videoCallDisabledReason = _t("You do not have permission to start voice calls"); + break; + case State.Ongoing: + voiceCallDisabledReason = _t("Ongoing call"); + videoCallDisabledReason = _t("Ongoing call"); + break; + case State.NoOneHere: + voiceCallDisabledReason = _t("There's no one here to call"); + videoCallDisabledReason = _t("There's no one here to call"); + break; + case State.Unpinned: + case State.NoCall: + voiceCallDisabledReason = null; + videoCallDisabledReason = null; + } + + /** + * We've gone through all the steps + */ + return { + voiceCallDisabledReason, + voiceCallClick, + videoCallDisabledReason, + videoCallClick, + }; +}; diff --git a/src/hooks/room/useRoomCallStatus.ts b/src/hooks/room/useRoomCallStatus.ts deleted file mode 100644 index 7afd1f9ce4..0000000000 --- a/src/hooks/room/useRoomCallStatus.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2023 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 { Room, RoomStateEvent } from "matrix-js-sdk/src/matrix"; -import { useCallback, useEffect, useMemo, useState } from "react"; - -import { useFeatureEnabled } from "../useSettings"; -import SdkConfig from "../../SdkConfig"; -import { useEventEmitterState, useTypedEventEmitterState } from "../useEventEmitter"; -import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../LegacyCallHandler"; -import { useWidgets } from "../../components/views/right_panel/RoomSummaryCard"; -import { WidgetType } from "../../widgets/WidgetType"; -import { useCall } from "../useCall"; -import { _t } from "../../languageHandler"; -import { useRoomMemberCount } from "../useRoomMembers"; -import { ElementCall } from "../../models/Call"; - -export type PlatformCallType = "element_call" | "jitsi_or_element_call" | "legacy_or_jitsi"; - -const DEFAULT_DISABLED_REASON = null; -const DEFAULT_CALL_TYPE = "jitsi_or_element_call"; - -/** - * Reports the call capabilities for the current room - * @param room the room to track - * @returns the call status for a room - */ -export const useRoomCallStatus = ( - room: Room, -): { - voiceCallDisabledReason: string | null; - voiceCallType: PlatformCallType; - videoCallDisabledReason: string | null; - videoCallType: PlatformCallType; -} => { - const [voiceCallDisabledReason, setVoiceCallDisabledReason] = useState(DEFAULT_DISABLED_REASON); - const [videoCallDisabledReason, setVideoCallDisabledReason] = useState(DEFAULT_DISABLED_REASON); - const [voiceCallType, setVoiceCallType] = useState(DEFAULT_CALL_TYPE); - const [videoCallType, setVideoCallType] = useState(DEFAULT_CALL_TYPE); - - const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); - const useElementCallExclusively = useMemo(() => { - return SdkConfig.get("element_call").use_exclusively; - }, []); - - const hasLegacyCall = useEventEmitterState( - LegacyCallHandler.instance, - LegacyCallHandlerEvent.CallsChanged, - () => LegacyCallHandler.instance.getCallForRoom(room.roomId) !== null, - ); - - const widgets = useWidgets(room); - const hasJitsiWidget = useMemo(() => widgets.some((widget) => WidgetType.JITSI.matches(widget.type)), [widgets]); - - const hasGroupCall = useCall(room.roomId) !== null; - - const memberCount = useRoomMemberCount(room); - - const [mayEditWidgets, mayCreateElementCalls] = useTypedEventEmitterState( - room, - RoomStateEvent.Update, - useCallback( - () => [ - room.currentState.mayClientSendStateEvent("im.vector.modular.widgets", room.client), - room.currentState.mayClientSendStateEvent(ElementCall.CALL_EVENT_TYPE.name, room.client), - ], - [room], - ), - ); - - useEffect(() => { - // First reset all state to their default value - setVoiceCallDisabledReason(DEFAULT_DISABLED_REASON); - setVideoCallDisabledReason(DEFAULT_DISABLED_REASON); - setVoiceCallType(DEFAULT_CALL_TYPE); - setVideoCallType(DEFAULT_CALL_TYPE); - - // And then run the logic to figure out their correct state - if (groupCallsEnabled) { - if (useElementCallExclusively) { - if (hasGroupCall) { - setVideoCallDisabledReason(_t("Ongoing call")); - } else if (mayCreateElementCalls) { - setVideoCallType("element_call"); - } else { - setVideoCallDisabledReason(_t("You do not have permission to start video calls")); - } - } else if (hasLegacyCall || hasJitsiWidget || hasGroupCall) { - setVoiceCallDisabledReason(_t("Ongoing call")); - setVideoCallDisabledReason(_t("Ongoing call")); - } else if (memberCount <= 1) { - setVoiceCallDisabledReason(_t("There's no one here to call")); - setVideoCallDisabledReason(_t("There's no one here to call")); - } else if (memberCount === 2) { - setVoiceCallType("legacy_or_jitsi"); - setVideoCallType("legacy_or_jitsi"); - } else if (mayEditWidgets) { - setVoiceCallType("legacy_or_jitsi"); - setVideoCallType(mayCreateElementCalls ? "jitsi_or_element_call" : "legacy_or_jitsi"); - } else { - setVoiceCallDisabledReason(_t("You do not have permission to start voice calls")); - if (mayCreateElementCalls) { - setVideoCallType("element_call"); - } else { - setVideoCallDisabledReason(_t("You do not have permission to start video calls")); - } - } - } else if (hasLegacyCall || hasJitsiWidget) { - setVoiceCallDisabledReason(_t("Ongoing call")); - setVideoCallDisabledReason(_t("Ongoing call")); - } else if (memberCount <= 1) { - setVoiceCallDisabledReason(_t("There's no one here to call")); - setVideoCallDisabledReason(_t("There's no one here to call")); - } else if (memberCount === 2 || mayEditWidgets) { - setVoiceCallType("legacy_or_jitsi"); - setVideoCallType("legacy_or_jitsi"); - } else { - setVoiceCallDisabledReason(_t("You do not have permission to start voice calls")); - setVideoCallDisabledReason(_t("You do not have permission to start video calls")); - } - }, [ - memberCount, - groupCallsEnabled, - hasGroupCall, - hasJitsiWidget, - hasLegacyCall, - mayCreateElementCalls, - mayEditWidgets, - useElementCallExclusively, - ]); - - /** - * We've gone through all the steps - */ - return { - voiceCallDisabledReason, - voiceCallType, - videoCallDisabledReason, - videoCallType, - }; -}; diff --git a/src/hooks/useRoomState.ts b/src/hooks/useRoomState.ts index 1c8c27c285..dc3e4c2df1 100644 --- a/src/hooks/useRoomState.ts +++ b/src/hooks/useRoomState.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { Room, RoomState, RoomStateEvent } from "matrix-js-sdk/src/matrix"; import { useTypedEventEmitter } from "./useEventEmitter"; @@ -28,19 +28,27 @@ export const useRoomState = ( room?: Room, mapper: Mapper = defaultMapper as Mapper, ): T => { + // Create a ref that stores mapper + const savedMapper = useRef(mapper); + + // Update ref.current value if mapper changes. + useEffect(() => { + savedMapper.current = mapper; + }, [mapper]); + const [value, setValue] = useState(room ? mapper(room.currentState) : (undefined as T)); const update = useCallback(() => { if (!room) return; - setValue(mapper(room.currentState)); - }, [room, mapper]); + setValue(savedMapper.current(room.currentState)); + }, [room]); useTypedEventEmitter(room?.currentState, RoomStateEvent.Update, update); useEffect(() => { update(); return () => { - setValue(room ? mapper(room.currentState) : (undefined as T)); + setValue(room ? savedMapper.current(room.currentState) : (undefined as T)); }; - }, [room, mapper, update]); + }, [room, update]); return value; }; diff --git a/src/utils/room/placeCall.ts b/src/utils/room/placeCall.ts index b684b494ea..a50a7f2725 100644 --- a/src/utils/room/placeCall.ts +++ b/src/utils/room/placeCall.ts @@ -18,7 +18,7 @@ import { CallType } from "matrix-js-sdk/src/webrtc/call"; import { Room } from "matrix-js-sdk/src/matrix"; import LegacyCallHandler from "../../LegacyCallHandler"; -import { PlatformCallType } from "../../hooks/room/useRoomCallStatus"; +import { PlatformCallType } from "../../hooks/room/useRoomCall"; import defaultDispatcher from "../../dispatcher/dispatcher"; import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; import { Action } from "../../dispatcher/actions"; From 569832bc248aaa2c9fe1073e4c47af19eba20366 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 Sep 2023 11:39:30 +0100 Subject: [PATCH 3/8] Handle ManagedHybrid widgets in `useRoomCall` and mark them in the widget state event Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/hooks/room/useRoomCall.ts | 8 ++++++-- src/stores/WidgetStore.ts | 8 +++++--- src/utils/WidgetUtils.ts | 2 +- src/widgets/ManagedHybrid.ts | 11 +++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/hooks/room/useRoomCall.ts b/src/hooks/room/useRoomCall.ts index 2d0e36d2a3..58ef67b002 100644 --- a/src/hooks/room/useRoomCall.ts +++ b/src/hooks/room/useRoomCall.ts @@ -31,6 +31,7 @@ import { placeCall } from "../../utils/room/placeCall"; import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore"; import { useRoomState } from "../useRoomState"; import { _t } from "../../languageHandler"; +import { isManagedHybridWidget } from "../../widgets/ManagedHybrid"; export type PlatformCallType = "element_call" | "jitsi_or_element_call" | "legacy_or_jitsi"; @@ -69,6 +70,8 @@ export const useRoomCall = ( const widgets = useWidgets(room); const jitsiWidget = useMemo(() => widgets.find((widget) => WidgetType.JITSI.matches(widget.type)), [widgets]); const hasJitsiWidget = !!jitsiWidget; + const managedHybridWidget = useMemo(() => widgets.find(isManagedHybridWidget), [widgets]); + const hasManagedHybridWidget = !!managedHybridWidget; const groupCall = useCall(room.roomId); const hasGroupCall = groupCall !== null; @@ -105,7 +108,7 @@ export const useRoomCall = ( useElementCallExclusively, mayEditWidgets, ]); - const widget = callType === "element_call" ? groupCall?.widget : jitsiWidget; + const widget = callType === "element_call" ? groupCall?.widget : jitsiWidget ?? managedHybridWidget; const [canPinWidget, setCanPinWidget] = useState(false); const [widgetPinned, setWidgetPinned] = useState(false); @@ -122,7 +125,7 @@ export const useRoomCall = ( }, [room, jitsiWidget, groupCall, updateWidgetState]); const state = useMemo((): State => { - if (hasGroupCall || hasJitsiWidget) { + if (hasGroupCall || hasJitsiWidget || hasManagedHybridWidget) { return promptPinWidget ? State.Unpinned : State.Ongoing; } if (hasLegacyCall) { @@ -142,6 +145,7 @@ export const useRoomCall = ( hasGroupCall, hasJitsiWidget, hasLegacyCall, + hasManagedHybridWidget, mayCreateElementCalls, mayEditWidgets, memberCount, diff --git a/src/stores/WidgetStore.ts b/src/stores/WidgetStore.ts index 4280e71947..fd092e7fbb 100644 --- a/src/stores/WidgetStore.ts +++ b/src/stores/WidgetStore.ts @@ -29,10 +29,12 @@ import { UPDATE_EVENT } from "./AsyncStore"; interface IState {} export interface IApp extends IWidget { - roomId: string; - eventId?: string; // not present on virtual widgets + "roomId": string; + "eventId"?: string; // not present on virtual widgets // eslint-disable-next-line camelcase - avatar_url?: string; // MSC2765 https://github.com/matrix-org/matrix-doc/pull/2765 + "avatar_url"?: string; // MSC2765 https://github.com/matrix-org/matrix-doc/pull/2765 + // Whether the widget was created from `widget_build_url` and thus is a call widget of some kind + "io.element.managed_hybrid"?: boolean; } export function isAppWidget(widget: IWidget | IApp): widget is IApp { diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index 42a87507a4..2d5396fd1f 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -332,7 +332,7 @@ export default class WidgetUtils { client: MatrixClient, roomId: string, widgetId: string, - content: IWidget, + content: IWidget & Record, ): Promise { const addingWidget = !!content.url; diff --git a/src/widgets/ManagedHybrid.ts b/src/widgets/ManagedHybrid.ts index e171a31af7..ff06c295e6 100644 --- a/src/widgets/ManagedHybrid.ts +++ b/src/widgets/ManagedHybrid.ts @@ -22,7 +22,7 @@ import { getCallBehaviourWellKnown } from "../utils/WellKnownUtils"; import WidgetUtils from "../utils/WidgetUtils"; import { IStoredLayout, WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore"; import WidgetEchoStore from "../stores/WidgetEchoStore"; -import WidgetStore from "../stores/WidgetStore"; +import WidgetStore, { IApp } from "../stores/WidgetStore"; import SdkConfig from "../SdkConfig"; import DMRoomMap from "../utils/DMRoomMap"; @@ -97,7 +97,10 @@ export async function addManagedHybridWidget(roomId: string): Promise { // Add the widget try { - await WidgetUtils.setRoomWidgetContent(cli, roomId, widgetId, widgetContent); + await WidgetUtils.setRoomWidgetContent(cli, roomId, widgetId, { + ...widgetContent, + "io.element.managed_hybrid": true, + }); } catch (e) { logger.error(`Unable to add managed hybrid widget in room ${roomId}`, e); return; @@ -116,3 +119,7 @@ export async function addManagedHybridWidget(roomId: string): Promise { WidgetLayoutStore.instance.setContainerHeight(room, layout.container, layout.height); WidgetLayoutStore.instance.copyLayoutToRoom(room); } + +export function isManagedHybridWidget(widget: IApp): boolean { + return !!widget["io.element.managed_hybrid"]; +} From 623212ea0cc42483017dd2992d5051ef80724f10 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 Sep 2023 17:49:01 +0100 Subject: [PATCH 4/8] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 238503ca78..29de8a581f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1129,10 +1129,9 @@ "you_did_it": "You did it!", "complete_these": "Complete these to get the most out of %(brand)s" }, - "Ongoing call": "Ongoing call", - "You do not have permission to start video calls": "You do not have permission to start video calls", - "There's no one here to call": "There's no one here to call", "You do not have permission to start voice calls": "You do not have permission to start voice calls", + "Ongoing call": "Ongoing call", + "There's no one here to call": "There's no one here to call", "Sends the given message with confetti": "Sends the given message with confetti", "sends confetti": "sends confetti", "Sends the given message with fireworks": "Sends the given message with fireworks", @@ -1822,6 +1821,7 @@ "Scroll to most recent messages": "Scroll to most recent messages", "Video call (Jitsi)": "Video call (Jitsi)", "Video call (%(brand)s)": "Video call (%(brand)s)", + "You do not have permission to start video calls": "You do not have permission to start video calls", "Freedom": "Freedom", "Spotlight": "Spotlight", "Change layout": "Change layout", From e54dd603815e184f8b4933f4a69e2414e9e34fe2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 11 Sep 2023 17:55:32 +0100 Subject: [PATCH 5/8] Update tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/rooms/RoomHeader-test.tsx | 44 +++++++++---------- .../__snapshots__/RoomHeader-test.tsx.snap | 8 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/components/views/rooms/RoomHeader-test.tsx b/test/components/views/rooms/RoomHeader-test.tsx index 64f805bca2..5d0a17aba0 100644 --- a/test/components/views/rooms/RoomHeader-test.tsx +++ b/test/components/views/rooms/RoomHeader-test.tsx @@ -18,7 +18,7 @@ import React from "react"; import userEvent from "@testing-library/user-event"; import { CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { EventType, JoinRule, MatrixClient, MatrixEvent, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix"; -import { getAllByTitle, getByLabelText, getByText, getByTitle, render, screen, waitFor } from "@testing-library/react"; +import { getAllByLabelText, getByLabelText, getByText, render, screen, waitFor } from "@testing-library/react"; import { mkEvent, stubClient, withClientContextRenderOptions } from "../../../test-utils"; import RoomHeader from "../../../../src/components/views/rooms/RoomHeader"; @@ -195,7 +195,7 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - await userEvent.click(getByTitle(container, "Threads")); + await userEvent.click(getByLabelText(container, "Threads")); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel }); }); @@ -205,7 +205,7 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - await userEvent.click(getByTitle(container, "Notifications")); + await userEvent.click(getByLabelText(container, "Notifications")); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.NotificationPanel }); }); @@ -216,7 +216,7 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - for (const button of getAllByTitle(container, "There's no one here to call")) { + for (const button of getAllByLabelText(container, "There's no one here to call")) { expect(button).toBeDisabled(); } }); @@ -227,8 +227,8 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - const voiceButton = getByTitle(container, "Voice call"); - const videoButton = getByTitle(container, "Video call"); + const voiceButton = getByLabelText(container, "Voice call"); + const videoButton = getByLabelText(container, "Video call"); expect(voiceButton).not.toBeDisabled(); expect(videoButton).not.toBeDisabled(); @@ -251,7 +251,7 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - for (const button of getAllByTitle(container, "Ongoing call")) { + for (const button of getAllByLabelText(container, "Ongoing call")) { expect(button).toBeDisabled(); } }); @@ -264,8 +264,8 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - expect(getByTitle(container, "Voice call")).not.toBeDisabled(); - expect(getByTitle(container, "Video call")).not.toBeDisabled(); + expect(getByLabelText(container, "Voice call")).not.toBeDisabled(); + expect(getByLabelText(container, "Video call")).not.toBeDisabled(); }); it("disable calls in large rooms by default", () => { @@ -275,8 +275,8 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - expect(getByTitle(container, "You do not have permission to start voice calls")).toBeDisabled(); - expect(getByTitle(container, "You do not have permission to start video calls")).toBeDisabled(); + expect(getByLabelText(container, "You do not have permission to start voice calls")).toBeDisabled(); + expect(getByLabelText(container, "You do not have permission to start video calls")).toBeDisabled(); }); }); @@ -297,12 +297,12 @@ describe("RoomHeader", () => { expect(screen.queryByTitle("Voice call")).toBeNull(); - const videoCallButton = getByTitle(container, "Video call"); + const videoCallButton = getByLabelText(container, "Video call"); expect(videoCallButton).not.toBeDisabled(); const dispatcherSpy = jest.spyOn(dispatcher, "dispatch"); - await userEvent.click(getByTitle(container, "Video call")); + await userEvent.click(getByLabelText(container, "Video call")); expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({ view_call: true })); }); @@ -318,7 +318,7 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - expect(getByTitle(container, "Ongoing call")).toBeDisabled(); + expect(getByLabelText(container, "Ongoing call")).toBeDisabled(); }); it("disables calling if there's a jitsi call", () => { @@ -331,7 +331,7 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - for (const button of getAllByTitle(container, "Ongoing call")) { + for (const button of getAllByLabelText(container, "Ongoing call")) { expect(button).toBeDisabled(); } }); @@ -342,7 +342,7 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - for (const button of getAllByTitle(container, "There's no one here to call")) { + for (const button of getAllByLabelText(container, "There's no one here to call")) { expect(button).toBeDisabled(); } }); @@ -354,8 +354,8 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - const voiceButton = getByTitle(container, "Voice call"); - const videoButton = getByTitle(container, "Video call"); + const voiceButton = getByLabelText(container, "Voice call"); + const videoButton = getByLabelText(container, "Video call"); expect(voiceButton).not.toBeDisabled(); expect(videoButton).not.toBeDisabled(); @@ -380,8 +380,8 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - const voiceButton = getByTitle(container, "Voice call"); - const videoButton = getByTitle(container, "Video call"); + const voiceButton = getByLabelText(container, "Voice call"); + const videoButton = getByLabelText(container, "Video call"); expect(voiceButton).not.toBeDisabled(); expect(videoButton).not.toBeDisabled(); @@ -407,8 +407,8 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - const voiceButton = getByTitle(container, "Voice call"); - const videoButton = getByTitle(container, "Video call"); + const voiceButton = getByLabelText(container, "Voice call"); + const videoButton = getByLabelText(container, "Video call"); expect(voiceButton).not.toBeDisabled(); expect(videoButton).not.toBeDisabled(); diff --git a/test/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap b/test/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap index 345a05ff67..258671d7c6 100644 --- a/test/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap +++ b/test/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap @@ -35,36 +35,36 @@ exports[`RoomHeader does not show the face pile for DMs 1`] = ` style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);" > From 68e8fa7c8c0f99a158364e5dfd53a13bce316f06 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 12 Sep 2023 08:35:07 +0100 Subject: [PATCH 6/8] Tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/hooks/room/useRoomCall.ts | 25 +- src/i18n/strings/en_EN.json | 274 +++++++++--------- .../views/rooms/RoomHeader-test.tsx | 77 +++-- 3 files changed, 209 insertions(+), 167 deletions(-) diff --git a/src/hooks/room/useRoomCall.ts b/src/hooks/room/useRoomCall.ts index 58ef67b002..c369f5ab15 100644 --- a/src/hooks/room/useRoomCall.ts +++ b/src/hooks/room/useRoomCall.ts @@ -32,6 +32,7 @@ import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutS import { useRoomState } from "../useRoomState"; import { _t } from "../../languageHandler"; import { isManagedHybridWidget } from "../../widgets/ManagedHybrid"; +import { IApp } from "../../stores/WidgetStore"; export type PlatformCallType = "element_call" | "jitsi_or_element_call" | "legacy_or_jitsi"; @@ -91,12 +92,14 @@ export const useRoomCall = ( if (mayCreateElementCalls && hasJitsiWidget) { return "jitsi_or_element_call"; } - if (useElementCallExclusively || mayCreateElementCalls) { - // Looks like for Audio this was previously legacy_or_jitsi + if (useElementCallExclusively) { return "element_call"; } - if (mayEditWidgets) { - return "jitsi_or_element_call"; + if (memberCount <= 2) { + return "legacy_or_jitsi"; + } + if (mayCreateElementCalls) { + return "element_call"; } } return "legacy_or_jitsi"; @@ -106,9 +109,17 @@ export const useRoomCall = ( mayCreateElementCalls, hasJitsiWidget, useElementCallExclusively, - mayEditWidgets, + memberCount, ]); - const widget = callType === "element_call" ? groupCall?.widget : jitsiWidget ?? managedHybridWidget; + + let widget: IApp | undefined; + if (callType === "legacy_or_jitsi") { + widget = jitsiWidget ?? managedHybridWidget; + } else if (callType === "element_call") { + widget = groupCall?.widget; + } else { + widget = groupCall?.widget ?? jitsiWidget; + } const [canPinWidget, setCanPinWidget] = useState(false); const [widgetPinned, setWidgetPinned] = useState(false); @@ -180,7 +191,7 @@ export const useRoomCall = ( switch (state) { case State.NoPermission: voiceCallDisabledReason = _t("You do not have permission to start voice calls"); - videoCallDisabledReason = _t("You do not have permission to start voice calls"); + videoCallDisabledReason = _t("You do not have permission to start video calls"); break; case State.Ongoing: voiceCallDisabledReason = _t("Ongoing call"); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index aff4891ff7..ada853d890 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -143,8 +143,10 @@ "public": "Public", "private": "Private", "options": "Options", + "integration_manager": "Integration manager", "message_layout": "Message layout", "modern": "Modern", + "identity_server": "Identity server", "success": "Success", "legal": "Legal", "credits": "Credits", @@ -203,9 +205,7 @@ "support": "Support", "room_name": "Room name", "thread": "Thread", - "accessibility": "Accessibility", - "integration_manager": "Integration manager", - "identity_server": "Identity server" + "accessibility": "Accessibility" }, "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "The file '%(fileName)s' failed to upload.": "The file '%(fileName)s' failed to upload.", @@ -515,17 +515,17 @@ "m.room.canonical_alias": { "set": "%(senderName)s set the main address for this room to %(address)s.", "removed": "%(senderName)s removed the main address for this room.", - "changed_alternative": "%(senderName)s changed the alternative addresses for this room.", - "changed_main_and_alternative": "%(senderName)s changed the main and alternative addresses for this room.", - "changed": "%(senderName)s changed the addresses for this room.", + "alt_added": { + "other": "%(senderName)s added the alternative addresses %(addresses)s for this room.", + "one": "%(senderName)s added alternative address %(addresses)s for this room." + }, "alt_removed": { "other": "%(senderName)s removed the alternative addresses %(addresses)s for this room.", "one": "%(senderName)s removed alternative address %(addresses)s for this room." }, - "alt_added": { - "other": "%(senderName)s added the alternative addresses %(addresses)s for this room.", - "one": "%(senderName)s added alternative address %(addresses)s for this room." - } + "changed_alternative": "%(senderName)s changed the alternative addresses for this room.", + "changed_main_and_alternative": "%(senderName)s changed the main and alternative addresses for this room.", + "changed": "%(senderName)s changed the addresses for this room." }, "m.room.third_party_invite": { "revoked": "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.", @@ -538,6 +538,10 @@ "world_readable": "%(senderName)s made future room history visible to anyone.", "unknown": "%(senderName)s made future room history visible to unknown (%(visibility)s)." }, + "m.room.power_levels": { + "changed": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", + "user_from_to": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s" + }, "m.room.pinned_events": { "pinned_link": "%(senderName)s pinned a message to this room. See all pinned messages.", "pinned": "%(senderName)s pinned a message to this room. See all pinned messages.", @@ -552,6 +556,25 @@ "removed": "%(widgetName)s widget removed by %(senderName)s" }, "io.element.widgets.layout": "%(senderName)s has updated the room layout", + "mjolnir": { + "removed_rule_users": "%(senderName)s removed the rule banning users matching %(glob)s", + "removed_rule_rooms": "%(senderName)s removed the rule banning rooms matching %(glob)s", + "removed_rule_servers": "%(senderName)s removed the rule banning servers matching %(glob)s", + "removed_rule": "%(senderName)s removed a ban rule matching %(glob)s", + "updated_invalid_rule": "%(senderName)s updated an invalid ban rule", + "updated_rule_users": "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s", + "updated_rule_rooms": "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s", + "updated_rule_servers": "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s", + "updated_rule": "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s", + "created_rule_users": "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s", + "created_rule_rooms": "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s", + "created_rule_servers": "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s", + "created_rule": "%(senderName)s created a ban rule matching %(glob)s for %(reason)s", + "changed_rule_users": "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", + "changed_rule_rooms": "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", + "changed_rule_servers": "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", + "changed_rule_glob": "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s" + }, "m.location": "%(senderName)s has shared their location", "self_redaction": "Message deleted", "redaction": "Message deleted by %(name)s", @@ -706,29 +729,6 @@ "other": "%(oneUser)ssent %(count)s hidden messages", "one": "%(oneUser)ssent a hidden message" } - }, - "mjolnir": { - "removed_rule_users": "%(senderName)s removed the rule banning users matching %(glob)s", - "removed_rule_rooms": "%(senderName)s removed the rule banning rooms matching %(glob)s", - "removed_rule_servers": "%(senderName)s removed the rule banning servers matching %(glob)s", - "removed_rule": "%(senderName)s removed a ban rule matching %(glob)s", - "updated_invalid_rule": "%(senderName)s updated an invalid ban rule", - "updated_rule_users": "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s", - "updated_rule_rooms": "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s", - "updated_rule_servers": "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s", - "updated_rule": "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s", - "created_rule_users": "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s", - "created_rule_rooms": "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s", - "created_rule_servers": "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s", - "created_rule": "%(senderName)s created a ban rule matching %(glob)s for %(reason)s", - "changed_rule_users": "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", - "changed_rule_rooms": "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", - "changed_rule_servers": "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", - "changed_rule_glob": "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s" - }, - "m.room.power_levels": { - "changed": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", - "user_from_to": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s" } }, "Light high contrast": "Light high contrast", @@ -980,6 +980,16 @@ "creating_output": "Creating output…" }, "That's fine": "That's fine", + "analytics": { + "consent_migration": "You previously consented to share anonymous usage data with us. We're updating how that works.", + "learn_more": "Share anonymous data to help us identify issues. Nothing personal. No third parties. Learn More", + "enable_prompt": "Help improve %(analyticsOwner)s", + "privacy_policy": "You can read all our terms here", + "pseudonymous_usage_data": "Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. To understand how people use multiple devices, we'll generate a random identifier, shared by your devices.", + "bullet_1": "We don't record or profile any account data", + "bullet_2": "We don't share information with third parties", + "disable_prompt": "You can turn this off anytime in settings" + }, "You have unverified sessions": "You have unverified sessions", "Review to ensure your account is safe": "Review to ensure your account is safe", "Later": "Later", @@ -1061,13 +1071,18 @@ "Could not find user in room": "Could not find user in room", "Define the power level of a user": "Define the power level of a user", "Deops user with given id": "Deops user with given id", - "Profile": "Profile", - "Spaces": "Spaces", - "Widgets": "Widgets", - "Rooms": "Rooms", - "Voice & Video": "Voice & Video", - "Encryption": "Encryption", "labs": { + "group_messaging": "Messaging", + "group_profile": "Profile", + "group_spaces": "Spaces", + "group_widgets": "Widgets", + "group_rooms": "Rooms", + "group_voip": "Voice & Video", + "group_moderation": "Moderation", + "group_themes": "Themes", + "group_encryption": "Encryption", + "group_experimental": "Experimental", + "group_developer": "Developer", "video_rooms": "Video rooms", "video_rooms_a_new_way_to_chat": "A new way to chat over voice and video in %(brand)s.", "video_rooms_always_on_voip_channels": "Video rooms are always-on VoIP channels embedded within a room in %(brand)s.", @@ -1105,18 +1120,7 @@ "hidebold": "Hide notification dot (only display counters badges)", "intentional_mentions": "Enable intentional mentions", "ask_to_join": "Enable ask to join", - "new_room_decoration_ui": "New room header & details interface", - "group_widgets": "Widgets", - "group_voip": "Voice & Video", - "group_themes": "Themes", - "group_spaces": "Spaces", - "group_rooms": "Rooms", - "group_profile": "Profile", - "group_moderation": "Moderation", - "group_messaging": "Messaging", - "group_experimental": "Experimental", - "group_encryption": "Encryption", - "group_developer": "Developer" + "new_room_decoration_ui": "New room header & details interface" }, "Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.", "Notification Settings": "Notification Settings", @@ -1125,6 +1129,24 @@ "settings": { "disable_historical_profile": "Show current profile picture and name for users in message history", "send_read_receipts": "Send read receipts", + "appearance": { + "font_size": "Font size", + "match_system_theme": "Match system theme", + "custom_font": "Use a system font", + "custom_font_name": "System font name", + "timeline_image_size": "Image size in the timeline", + "layout_irc": "IRC (Experimental)", + "layout_bubbles": "Message bubbles", + "custom_theme_invalid": "Invalid theme schema.", + "custom_theme_error_downloading": "Error downloading theme information.", + "custom_theme_success": "Theme added!", + "use_high_contrast": "Use high contrast", + "custom_theme_url": "Custom theme URL", + "custom_theme_add_button": "Add theme", + "custom_font_description": "Set the name of a font installed on your system & %(brand)s will attempt to use it.", + "heading": "Customise your appearance", + "subheading": "Appearance Settings only affect this %(brand)s session." + }, "emoji_autocomplete": "Enable Emoji suggestions while typing", "show_stickers_button": "Show stickers button", "insert_trailing_colon_mentions": "Insert a trailing colon after user mentions at the start of a message", @@ -1173,34 +1195,16 @@ "rule_call": "Call invitation", "rule_suppress_notices": "Messages sent by bot", "rule_tombstone": "When rooms are upgraded", - "show_message_desktop_notification": "Show message in desktop notification", "messages_containing_keywords": "Messages containing keywords", - "error_saving_detail": "An error occurred whilst saving your notification preferences.", "error_saving": "Error saving notification preferences", - "enable_notifications_device": "Enable notifications for this device", - "enable_notifications_account_detail": "Turn off to disable notifications on all your devices and sessions", + "error_saving_detail": "An error occurred whilst saving your notification preferences.", "enable_notifications_account": "Enable notifications for this account", + "enable_notifications_account_detail": "Turn off to disable notifications on all your devices and sessions", "enable_email_notifications": "Enable email notifications for %(email)s", + "enable_notifications_device": "Enable notifications for this device", "enable_desktop_notifications_session": "Enable desktop notifications for this session", + "show_message_desktop_notification": "Show message in desktop notification", "enable_audible_notifications_session": "Enable audible notifications for this session" - }, - "appearance": { - "font_size": "Font size", - "match_system_theme": "Match system theme", - "custom_font": "Use a system font", - "custom_font_name": "System font name", - "timeline_image_size": "Image size in the timeline", - "layout_irc": "IRC (Experimental)", - "layout_bubbles": "Message bubbles", - "custom_theme_invalid": "Invalid theme schema.", - "custom_theme_error_downloading": "Error downloading theme information.", - "custom_theme_success": "Theme added!", - "use_high_contrast": "Use high contrast", - "custom_theme_url": "Custom theme URL", - "custom_theme_add_button": "Add theme", - "custom_font_description": "Set the name of a font installed on your system & %(brand)s will attempt to use it.", - "heading": "Customise your appearance", - "subheading": "Appearance Settings only affect this %(brand)s session." } }, "Your server doesn't support disabling sending read receipts.": "Your server doesn't support disabling sending read receipts.", @@ -1255,12 +1259,12 @@ "description": "Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.", "submit_debug_logs": "Submit debug logs", "matrix_security_issue": "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.", + "create_new_issue": "Please create a new issue on GitHub so that we can investigate this bug.", "before_submitting": "Before submitting logs, you must create a GitHub issue to describe your problem.", "download_logs": "Download logs", "github_issue": "GitHub issue", "additional_context": "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.", - "send_logs": "Send logs", - "create_new_issue": "Please create a new issue on GitHub so that we can investigate this bug." + "send_logs": "Send logs" }, "Waiting for response from server": "Waiting for response from server", "My Ban List": "My Ban List", @@ -1303,10 +1307,24 @@ "you_did_it": "You did it!", "complete_these": "Complete these to get the most out of %(brand)s" }, - "Ongoing call": "Ongoing call", - "You do not have permission to start video calls": "You do not have permission to start video calls", - "There's no one here to call": "There's no one here to call", "You do not have permission to start voice calls": "You do not have permission to start voice calls", + "You do not have permission to start video calls": "You do not have permission to start video calls", + "Ongoing call": "Ongoing call", + "There's no one here to call": "There's no one here to call", + "chat_effects": { + "confetti_description": "Sends the given message with confetti", + "confetti_message": "sends confetti", + "fireworks_description": "Sends the given message with fireworks", + "fireworks_message": "sends fireworks", + "rainfall_description": "Sends the given message with rainfall", + "rainfall_message": "sends rainfall", + "snowfall_description": "Sends the given message with snowfall", + "snowfall_message": "sends snowfall", + "spaceinvaders_description": "Sends the given message with a space themed effect", + "spaceinvaders_message": "sends space invaders", + "hearts_description": "Sends the given message with hearts", + "hearts_message": "sends hearts" + }, "Server error": "Server error", "Command error": "Command error", "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", @@ -1324,6 +1342,22 @@ "Hide sidebar": "Hide sidebar", "Show sidebar": "Show sidebar", "More": "More", + "encryption": { + "verification": { + "other_party_cancelled": "The other party cancelled the verification.", + "complete_title": "Verified!", + "complete_description": "You've successfully verified this user.", + "sas_no_match": "They don't match", + "sas_match": "They match", + "in_person": "To be secure, do this in person or use a trusted way to communicate.", + "no_support_qr_emoji": "The device you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", + "qr_prompt": "Scan this unique code", + "sas_prompt": "Compare unique emoji", + "sas_description": "Compare a unique set of emoji if you don't have a camera on either device", + "qr_or_sas": "%(qrCode)s or %(emojiCompare)s", + "qr_or_sas_header": "Verify this device by completing one of the following:" + } + }, "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", "Got It": "Got It", "Confirm the emoji below are displayed on both devices, in the same order:": "Confirm the emoji below are displayed on both devices, in the same order:", @@ -1364,6 +1398,7 @@ "You can change these anytime.": "You can change these anytime.", "Creating…": "Creating…", "Show all rooms": "Show all rooms", + "Spaces": "Spaces", "Click to copy": "Click to copy", "Copied!": "Copied!", "Failed to copy": "Failed to copy", @@ -1436,6 +1471,7 @@ "Cryptography": "Cryptography", "Session ID:": "Session ID:", "Session key:": "Session key:", + "Encryption": "Encryption", "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.": { "other": "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.", @@ -1484,6 +1520,8 @@ "Space members": "Space members", "Ask to join": "Ask to join", "People cannot join unless access is granted.": "People cannot join unless access is granted.", + "Make this space visible in the public room directory.": "Make this space visible in the public room directory.", + "Make this room visible in the public room directory.": "Make this room visible in the public room directory.", "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.": "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.", "This upgrade will allow members of selected spaces access to this room without an invite.": "This upgrade will allow members of selected spaces access to this room without an invite.", "Mark all as read": "Mark all as read", @@ -1500,6 +1538,7 @@ "There was an error loading your notification settings.": "There was an error loading your notification settings.", "Failed to save your profile": "Failed to save your profile", "The operation could not be completed": "The operation could not be completed", + "Profile": "Profile", "Display Name": "Display Name", "Profile picture": "Profile picture", "Delete Backup": "Delete Backup", @@ -1675,6 +1714,7 @@ "No Audio Outputs detected": "No Audio Outputs detected", "No Microphones detected": "No Microphones detected", "No Webcams detected": "No Webcams detected", + "Voice & Video": "Voice & Video", "Voice settings": "Voice settings", "Automatically adjust the microphone volume": "Automatically adjust the microphone volume", "Video settings": "Video settings", @@ -2092,6 +2132,7 @@ "Explore public rooms": "Explore public rooms", "Add room": "Add room", "Saved Items": "Saved Items", + "Rooms": "Rooms", "Low priority": "Low priority", "Historical": "Historical", "Suggested Rooms": "Suggested Rooms", @@ -2123,6 +2164,8 @@ "Forget this space": "Forget this space", "Forget this room": "Forget this room", "Re-join": "Re-join", + "You have been denied access": "You have been denied access", + "As you have been denied access, you cannot rejoin unless you are invited by the admin or moderator of the group.": "As you have been denied access, you cannot rejoin unless you are invited by the admin or moderator of the group.", "You were banned from %(roomName)s by %(memberName)s": "You were banned from %(roomName)s by %(memberName)s", "You were banned by %(memberName)s": "You were banned by %(memberName)s", "Something went wrong with your invite to %(roomName)s": "Something went wrong with your invite to %(roomName)s", @@ -2291,6 +2334,7 @@ "Unpin this widget to view it in this panel": "Unpin this widget to view it in this panel", "Close this widget to view it in this panel": "Close this widget to view it in this panel", "Set my room layout for everyone": "Set my room layout for everyone", + "Widgets": "Widgets", "Edit widgets, bridges & bots": "Edit widgets, bridges & bots", "Add widgets, bridges & bots": "Add widgets, bridges & bots", "Not encrypted": "Not encrypted", @@ -2528,6 +2572,19 @@ "My live location": "My live location", "Drop a Pin": "Drop a Pin", "What location type do you want to share?": "What location type do you want to share?", + "emoji": { + "category_frequently_used": "Frequently Used", + "category_smileys_people": "Smileys & People", + "category_animals_nature": "Animals & Nature", + "category_food_drink": "Food & Drink", + "category_activities": "Activities", + "category_travel_places": "Travel & Places", + "category_objects": "Objects", + "category_symbols": "Symbols", + "category_flags": "Flags", + "categories": "Categories", + "quick_reactions": "Quick Reactions" + }, "Cancel search": "Cancel search", "Any of the following data may be shared:": "Any of the following data may be shared:", "Your display name": "Your display name", @@ -2725,7 +2782,6 @@ "Anyone will be able to find and join this room.": "Anyone will be able to find and join this room.", "Only people invited will be able to find and join this room.": "Only people invited will be able to find and join this room.", "Anyone can request to join, but admins or moderators need to grant access. You can change this later.": "Anyone can request to join, but admins or moderators need to grant access. You can change this later.", - "Make this room visible in the public room directory.": "Make this room visible in the public room directory.", "You can't disable this later. The room will be encrypted but the embedded call will not.": "You can't disable this later. The room will be encrypted but the embedded call will not.", "You can't disable this later. Bridges & most bots won't work yet.": "You can't disable this later. Bridges & most bots won't work yet.", "Your server requires encryption to be enabled in private rooms.": "Your server requires encryption to be enabled in private rooms.", @@ -3695,61 +3751,5 @@ "Activate selected button": "Activate selected button", "New line": "New line", "Force complete": "Force complete", - "Search (must be enabled)": "Search (must be enabled)", - "encryption": { - "verification": { - "other_party_cancelled": "The other party cancelled the verification.", - "complete_title": "Verified!", - "complete_description": "You've successfully verified this user.", - "sas_no_match": "They don't match", - "sas_match": "They match", - "in_person": "To be secure, do this in person or use a trusted way to communicate.", - "no_support_qr_emoji": "The device you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", - "qr_prompt": "Scan this unique code", - "sas_prompt": "Compare unique emoji", - "sas_description": "Compare a unique set of emoji if you don't have a camera on either device", - "qr_or_sas": "%(qrCode)s or %(emojiCompare)s", - "qr_or_sas_header": "Verify this device by completing one of the following:" - } - }, - "emoji": { - "category_frequently_used": "Frequently Used", - "category_smileys_people": "Smileys & People", - "category_animals_nature": "Animals & Nature", - "category_food_drink": "Food & Drink", - "category_activities": "Activities", - "category_travel_places": "Travel & Places", - "category_objects": "Objects", - "category_symbols": "Symbols", - "category_flags": "Flags", - "categories": "Categories", - "quick_reactions": "Quick Reactions" - }, - "chat_effects": { - "confetti_description": "Sends the given message with confetti", - "confetti_message": "sends confetti", - "fireworks_description": "Sends the given message with fireworks", - "fireworks_message": "sends fireworks", - "rainfall_description": "Sends the given message with rainfall", - "rainfall_message": "sends rainfall", - "snowfall_description": "Sends the given message with snowfall", - "snowfall_message": "sends snowfall", - "spaceinvaders_description": "Sends the given message with a space themed effect", - "spaceinvaders_message": "sends space invaders", - "hearts_description": "Sends the given message with hearts", - "hearts_message": "sends hearts" - }, - "analytics": { - "consent_migration": "You previously consented to share anonymous usage data with us. We're updating how that works.", - "learn_more": "Share anonymous data to help us identify issues. Nothing personal. No third parties. Learn More", - "enable_prompt": "Help improve %(analyticsOwner)s", - "privacy_policy": "You can read all our terms here", - "pseudonymous_usage_data": "Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. To understand how people use multiple devices, we'll generate a random identifier, shared by your devices.", - "bullet_1": "We don't record or profile any account data", - "bullet_2": "We don't share information with third parties", - "disable_prompt": "You can turn this off anytime in settings" - }, - "You have been denied access": "You have been denied access", - "Make this space visible in the public room directory.": "Make this space visible in the public room directory.", - "As you have been denied access, you cannot rejoin unless you are invited by the admin or moderator of the group.": "As you have been denied access, you cannot rejoin unless you are invited by the admin or moderator of the group." -} \ No newline at end of file + "Search (must be enabled)": "Search (must be enabled)" +} diff --git a/test/components/views/rooms/RoomHeader-test.tsx b/test/components/views/rooms/RoomHeader-test.tsx index 5d0a17aba0..1b08dd227f 100644 --- a/test/components/views/rooms/RoomHeader-test.tsx +++ b/test/components/views/rooms/RoomHeader-test.tsx @@ -15,12 +15,19 @@ limitations under the License. */ import React from "react"; -import userEvent from "@testing-library/user-event"; import { CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { EventType, JoinRule, MatrixClient, MatrixEvent, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix"; -import { getAllByLabelText, getByLabelText, getByText, render, screen, waitFor } from "@testing-library/react"; +import { + fireEvent, + getAllByLabelText, + getByLabelText, + getByText, + render, + screen, + waitFor, +} from "@testing-library/react"; -import { mkEvent, stubClient, withClientContextRenderOptions } from "../../../test-utils"; +import { filterConsole, mkEvent, stubClient, withClientContextRenderOptions } from "../../../test-utils"; import RoomHeader from "../../../../src/components/views/rooms/RoomHeader"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; @@ -33,10 +40,13 @@ import dispatcher from "../../../../src/dispatcher/dispatcher"; import { CallStore } from "../../../../src/stores/CallStore"; import { Call, ElementCall } from "../../../../src/models/Call"; import * as ShieldUtils from "../../../../src/utils/ShieldUtils"; +import { Container, WidgetLayoutStore } from "../../../../src/stores/widgets/WidgetLayoutStore"; jest.mock("../../../../src/utils/ShieldUtils"); describe("RoomHeader", () => { + filterConsole("[getType] Room !1:example.org does not have an m.room.create event"); + let room: Room; const ROOM_ID = "!1:example.org"; @@ -94,7 +104,7 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - await userEvent.click(getByText(container, ROOM_ID)); + fireEvent.click(getByText(container, ROOM_ID)); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.RoomSummary }); }); @@ -184,7 +194,7 @@ describe("RoomHeader", () => { const facePile = getByLabelText(container, "4 members"); expect(facePile).toHaveTextContent("4"); - await userEvent.click(facePile); + fireEvent.click(facePile); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.RoomMemberList }); }); @@ -195,7 +205,7 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - await userEvent.click(getByLabelText(container, "Threads")); + fireEvent.click(getByLabelText(container, "Threads")); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel }); }); @@ -205,7 +215,7 @@ describe("RoomHeader", () => { withClientContextRenderOptions(MatrixClientPeg.get()!), ); - await userEvent.click(getByLabelText(container, "Notifications")); + fireEvent.click(getByLabelText(container, "Notifications")); expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.NotificationPanel }); }); @@ -234,10 +244,10 @@ describe("RoomHeader", () => { const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall"); - await userEvent.click(voiceButton); + fireEvent.click(voiceButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Voice); - await userEvent.click(videoButton); + fireEvent.click(videoButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Video); }); @@ -275,8 +285,12 @@ describe("RoomHeader", () => { , withClientContextRenderOptions(MatrixClientPeg.get()!), ); - expect(getByLabelText(container, "You do not have permission to start voice calls")).toBeDisabled(); - expect(getByLabelText(container, "You do not have permission to start video calls")).toBeDisabled(); + expect( + getByLabelText(container, "You do not have permission to start voice calls", { selector: "button" }), + ).toBeDisabled(); + expect( + getByLabelText(container, "You do not have permission to start video calls", { selector: "button" }), + ).toBeDisabled(); }); }); @@ -286,6 +300,7 @@ describe("RoomHeader", () => { }); it("renders only the video call element", async () => { + mockRoomMembers(room, 3); jest.spyOn(SdkConfig, "get").mockReturnValue({ use_exclusively: true }); // allow element calls jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); @@ -302,17 +317,18 @@ describe("RoomHeader", () => { const dispatcherSpy = jest.spyOn(dispatcher, "dispatch"); - await userEvent.click(getByLabelText(container, "Video call")); + fireEvent.click(getByLabelText(container, "Video call")); expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({ view_call: true })); }); - it("can call if there's an ongoing call", () => { + it("can't call if there's an ongoing (pinned) call", () => { jest.spyOn(SdkConfig, "get").mockReturnValue({ use_exclusively: true }); // allow element calls jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); + jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(true); - jest.spyOn(CallStore.instance, "getCall").mockReturnValue({} as Call); + jest.spyOn(CallStore.instance, "getCall").mockReturnValue({ widget: {} } as Call); const { container } = render( , @@ -321,6 +337,25 @@ describe("RoomHeader", () => { expect(getByLabelText(container, "Ongoing call")).toBeDisabled(); }); + it("clicking on ongoing (unpinned) call re-pins it", () => { + jest.spyOn(SdkConfig, "get").mockReturnValue({ use_exclusively: true }); + // allow element calls + jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); + jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(false); + const spy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer"); + + const widget = {}; + jest.spyOn(CallStore.instance, "getCall").mockReturnValue({ widget } as Call); + + const { container } = render( + , + withClientContextRenderOptions(MatrixClientPeg.get()!), + ); + expect(getByLabelText(container, "Video call")).not.toBeDisabled(); + fireEvent.click(getByLabelText(container, "Video call")); + expect(spy).toHaveBeenCalledWith(room, widget, Container.Top); + }); + it("disables calling if there's a jitsi call", () => { mockRoomMembers(room, 2); jest.spyOn(LegacyCallHandler.instance, "getCallForRoom").mockReturnValue( @@ -360,10 +395,10 @@ describe("RoomHeader", () => { expect(videoButton).not.toBeDisabled(); const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall"); - await userEvent.click(voiceButton); + fireEvent.click(voiceButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Voice); - await userEvent.click(videoButton); + fireEvent.click(videoButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Video); }); @@ -386,10 +421,10 @@ describe("RoomHeader", () => { expect(videoButton).not.toBeDisabled(); const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall"); - await userEvent.click(voiceButton); + fireEvent.click(voiceButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Voice); - await userEvent.click(videoButton); + fireEvent.click(videoButton); expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Video); }); @@ -412,12 +447,8 @@ describe("RoomHeader", () => { expect(voiceButton).not.toBeDisabled(); expect(videoButton).not.toBeDisabled(); - const placeCallSpy = jest.spyOn(LegacyCallHandler.instance, "placeCall"); - await userEvent.click(voiceButton); - expect(placeCallSpy).toHaveBeenLastCalledWith(room.roomId, CallType.Voice); - const dispatcherSpy = jest.spyOn(dispatcher, "dispatch"); - await userEvent.click(videoButton); + fireEvent.click(videoButton); expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({ view_call: true })); }); }); From 1d0c7046c0489520f6333cddc6b4ab81c23d859d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 Sep 2023 11:39:17 +0100 Subject: [PATCH 7/8] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 368 ++++++++++++++++++------------------ 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8632609e7d..5644293990 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -5,6 +5,22 @@ "No identity access token found": "No identity access token found", "Use Single Sign On to continue": "Use Single Sign On to continue", "Confirm adding this email address by using Single Sign On to prove your identity.": "Confirm adding this email address by using Single Sign On to prove your identity.", + "auth": { + "sso": "Single Sign On", + "continue_with_idp": "Continue with %(provider)s", + "sign_in_with_sso": "Sign in with single sign-on", + "reset_password_action": "Reset password", + "reset_password_title": "Reset your password", + "continue_with_sso": "Continue with %(ssoButtons)s", + "sso_or_username_password": "%(ssoButtons)s Or %(usernamePassword)s", + "sign_in_instead": "Already have an account? Sign in here", + "account_clash": "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).", + "account_clash_previous_account": "Continue with previous account", + "log_in_new_account": "Log in to your new account.", + "registration_successful": "Registration Successful", + "server_picker_title": "Host account on", + "server_picker_dialog_title": "Decide where your account is hosted" + }, "Confirm adding email": "Confirm adding email", "Click the button below to confirm adding this email address.": "Click the button below to confirm adding this email address.", "action": { @@ -183,6 +199,7 @@ "encryption_enabled": "Encryption enabled", "image": "Image", "reactions": "Reactions", + "qr_code": "QR Code", "homeserver": "Homeserver", "help": "Help", "matrix": "Matrix", @@ -204,8 +221,7 @@ "support": "Support", "room_name": "Room name", "thread": "Thread", - "accessibility": "Accessibility", - "qr_code": "QR Code" + "accessibility": "Accessibility" }, "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "The file '%(fileName)s' failed to upload.": "The file '%(fileName)s' failed to upload.", @@ -402,36 +418,36 @@ "unignore": "Stops ignoring a user, showing their messages going forward", "devtools": "Opens the Developer Tools dialog", "addwidget": "Adds a custom widget by URL to the room", + "addwidget_missing_url": "Please supply a widget URL or embed code", + "addwidget_iframe_missing_src": "iframe has no src attribute", + "addwidget_invalid_protocol": "Please supply a https:// or http:// widget URL", + "addwidget_no_permissions": "You cannot modify widgets in this room.", + "discardsession": "Forces the current outbound group session in an encrypted room to be discarded", + "remakeolm": "Developer command: Discards the current outbound group session and sets up new Olm sessions", "rainbow": "Sends the given message coloured as a rainbow", "rainbowme": "Sends the given emote coloured as a rainbow", "help": "Displays list of commands with usages and descriptions", "whois": "Displays information about a user", "rageshake": "Send a bug report with logs", + "tovirtual": "Switches to this room's virtual room, if it has one", + "tovirtual_not_found": "No virtual room for this room", + "query": "Opens chat with the given user", + "query_not_found_phone_number": "Unable to find Matrix ID for phone number", "msg": "Sends a message to the given user", + "holdcall": "Places the call in the current room on hold", + "no_active_call": "No active call in this room", + "unholdcall": "Takes the call in the current room off hold", + "converttodm": "Converts the room to a DM", + "could_not_find_room": "Could not find room", + "converttoroom": "Converts the DM to a room", + "me": "Displays action", "usage": "Usage", "category_messages": "Messages", "category_actions": "Actions", "category_admin": "Admin", "category_advanced": "Advanced", "category_effects": "Effects", - "category_other": "Other", - "unholdcall": "Takes the call in the current room off hold", - "tovirtual_not_found": "No virtual room for this room", - "tovirtual": "Switches to this room's virtual room, if it has one", - "remakeolm": "Developer command: Discards the current outbound group session and sets up new Olm sessions", - "query_not_found_phone_number": "Unable to find Matrix ID for phone number", - "query": "Opens chat with the given user", - "no_active_call": "No active call in this room", - "me": "Displays action", - "holdcall": "Places the call in the current room on hold", - "discardsession": "Forces the current outbound group session in an encrypted room to be discarded", - "could_not_find_room": "Could not find room", - "converttoroom": "Converts the DM to a room", - "converttodm": "Converts the room to a DM", - "addwidget_no_permissions": "You cannot modify widgets in this room.", - "addwidget_missing_url": "Please supply a widget URL or embed code", - "addwidget_invalid_protocol": "Please supply a https:// or http:// widget URL", - "addwidget_iframe_missing_src": "iframe has no src attribute" + "category_other": "Other" }, "Use an identity server": "Use an identity server", "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.", @@ -978,23 +994,23 @@ "file_attached": "File Attached", "fetching_events": "Fetching events…", "creating_output": "Creating output…", - "title": "Export Chat", - "successful_detail": "Your export was successful. Find it in your Downloads folder.", - "successful": "Export Successful", - "size_limit_min_max": "Size can only be a number between %(min)s MB and %(max)s MB", - "size_limit": "Size Limit", - "select_option": "Select from the options below to export chats from your timeline", "processing": "Processing…", + "enter_number_between_min_max": "Enter a number between %(min)s and %(max)s", + "size_limit_min_max": "Size can only be a number between %(min)s MB and %(max)s MB", "num_messages_min_max": "Number of messages can only be a number between %(min)s and %(max)s", "num_messages": "Number of messages", - "messages": "Messages", - "include_attachments": "Include Attachments", - "format": "Format", - "exporting_your_data": "Exporting your data", - "enter_number_between_min_max": "Enter a number between %(min)s and %(max)s", - "confirm_stop": "Are you sure you want to stop exporting your data? If you do, you'll need to start over.", + "cancelled": "Export Cancelled", "cancelled_detail": "The export was cancelled successfully", - "cancelled": "Export Cancelled" + "successful": "Export Successful", + "successful_detail": "Your export was successful. Find it in your Downloads folder.", + "confirm_stop": "Are you sure you want to stop exporting your data? If you do, you'll need to start over.", + "exporting_your_data": "Exporting your data", + "title": "Export Chat", + "select_option": "Select from the options below to export chats from your timeline", + "format": "Format", + "messages": "Messages", + "size_limit": "Size Limit", + "include_attachments": "Include Attachments" }, "That's fine": "That's fine", "analytics": { @@ -1138,12 +1154,12 @@ "intentional_mentions": "Enable intentional mentions", "ask_to_join": "Enable ask to join", "new_room_decoration_ui": "New room header & details interface", - "leave_beta_reload": "Leaving the beta will reload %(brand)s.", - "leave_beta": "Leave the beta", - "join_beta_reload": "Joining the beta will reload %(brand)s.", - "join_beta": "Join the beta", + "beta_feature": "This is a beta feature", "click_for_info": "Click for more info", - "beta_feature": "This is a beta feature" + "leave_beta_reload": "Leaving the beta will reload %(brand)s.", + "join_beta_reload": "Joining the beta will reload %(brand)s.", + "leave_beta": "Leave the beta", + "join_beta": "Join the beta" }, "Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.", "Notification Settings": "Notification Settings", @@ -1329,28 +1345,28 @@ }, "you_did_it": "You did it!", "complete_these": "Complete these to get the most out of %(brand)s", - "welcome_user": "Welcome %(name)s", - "welcome_detail": "Now, let's help you get started", - "send_dm": "Send a Direct Message", + "download_brand": "Download %(brand)s", + "download_brand_desktop": "Download %(brand)s Desktop", "qr_or_app_links": "%(qrCode)s or %(appLinks)s", - "no_avatar_label": "Add a photo so people know it's you.", - "intro_welcome": "Welcome to %(appName)s", - "intro_byline": "Own your conversations.", - "has_avatar_label": "Great, that'll help people know it's you", - "google_trademarks": "Google Play and the Google Play logo are trademarks of Google LLC.", - "explore_rooms": "Explore Public Rooms", + "download_app_store": "Download on the App Store", "download_google_play": "Get it on Google Play", "download_f_droid": "Get it on F-Droid", - "download_brand_desktop": "Download %(brand)s Desktop", - "download_brand": "Download %(brand)s", - "download_app_store": "Download on the App Store", - "create_room": "Create a Group Chat", - "apple_trademarks": "App Store® and the Apple logo® are trademarks of Apple Inc." + "apple_trademarks": "App Store® and the Apple logo® are trademarks of Apple Inc.", + "google_trademarks": "Google Play and the Google Play logo are trademarks of Google LLC.", + "has_avatar_label": "Great, that'll help people know it's you", + "no_avatar_label": "Add a photo so people know it's you.", + "welcome_user": "Welcome %(name)s", + "welcome_detail": "Now, let's help you get started", + "intro_welcome": "Welcome to %(appName)s", + "intro_byline": "Own your conversations.", + "send_dm": "Send a Direct Message", + "explore_rooms": "Explore Public Rooms", + "create_room": "Create a Group Chat" }, - "Ongoing call": "Ongoing call", - "You do not have permission to start video calls": "You do not have permission to start video calls", - "There's no one here to call": "There's no one here to call", "You do not have permission to start voice calls": "You do not have permission to start voice calls", + "You do not have permission to start video calls": "You do not have permission to start video calls", + "Ongoing call": "Ongoing call", + "There's no one here to call": "There's no one here to call", "chat_effects": { "confetti_description": "Sends the given message with confetti", "confetti_message": "sends confetti", @@ -1663,6 +1679,21 @@ "Deactivating your account is a permanent action — be careful!": "Deactivating your account is a permanent action — be careful!", "Deactivate Account": "Deactivate Account", "Discovery": "Discovery", + "setting": { + "help_about": { + "brand_version": "%(brand)s version:", + "olm_version": "Olm version:", + "help_link": "For help with using %(brand)s, click here.", + "help_link_chat_bot": "For help with using %(brand)s, click here or start a chat with our bot using the button below.", + "chat_bot": "Chat with %(brand)s Bot", + "title": "Help & About", + "versions": "Versions", + "homeserver": "Homeserver is %(homeserverUrl)s", + "identity_server": "Identity server is %(identityServerUrl)s", + "access_token_detail": "Your access token gives full access to your account. Do not share it with anyone.", + "clear_cache_reload": "Clear cache and reload" + } + }, "credits": { "default_cover_photo": "The default cover photo is © Jesús Roncero used under the terms of CC-BY-SA 4.0.", "twemoji_colr": "The twemoji-colr font is © Mozilla Foundation used under the terms of Apache 2.0.", @@ -2150,6 +2181,12 @@ }, "Start new chat": "Start new chat", "Invite to space": "Invite to space", + "spaces": { + "error_no_permission_invite": "You do not have permissions to invite people to this space", + "error_no_permission_create_room": "You do not have permissions to create new rooms in this space", + "error_no_permission_add_room": "You do not have permissions to add rooms to this space", + "error_no_permission_add_space": "You do not have permissions to add spaces to this space" + }, "Add people": "Add people", "Explore rooms": "Explore rooms", "New room": "New room", @@ -2237,6 +2274,32 @@ "To view, please enable video rooms in Labs first": "To view, please enable video rooms in Labs first", "To join, please enable video rooms in Labs first": "To join, please enable video rooms in Labs first", "Show Labs settings": "Show Labs settings", + "room_list": { + "sort_unread_first": "Show rooms with unread messages first", + "show_previews": "Show previews of messages", + "sort_by": "Sort by", + "sort_by_activity": "Activity", + "sort_by_alphabet": "A-Z", + "sublist_options": "List options", + "show_n_more": { + "other": "Show %(count)s more", + "one": "Show %(count)s more" + }, + "show_less": "Show less", + "notification_options": "Notification options" + }, + "a11y": { + "n_unread_messages_mentions": { + "other": "%(count)s unread messages including mentions.", + "one": "1 unread mention." + }, + "n_unread_messages": { + "other": "%(count)s unread messages.", + "one": "1 unread message." + }, + "unread_messages": "Unread messages.", + "user_menu": "User menu" + }, "Joined": "Joined", "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.", "This room has already been upgraded.": "This room has already been upgraded.", @@ -2625,62 +2688,62 @@ "control": "Ctrl", "shift": "Shift", "number": "[number]", - "upload_file": "Upload a file", - "toggle_webcam_mute": "Toggle webcam on/off", - "toggle_top_left_menu": "Toggle the top left menu", - "toggle_space_panel": "Toggle space panel", - "toggle_right_panel": "Toggle right panel", - "toggle_microphone_mute": "Toggle microphone mute", - "toggle_hidden_events": "Toggle hidden event visibility", - "switch_to_space": "Switch to space by number", - "send_sticker": "Send a sticker", - "search": "Search (must be enabled)", - "scroll_up_timeline": "Scroll up in the timeline", - "scroll_down_timeline": "Scroll down in the timeline", - "room_list_select_room": "Select room from the room list", - "room_list_navigate_up": "Navigate up in the room list", - "room_list_navigate_down": "Navigate down in the room list", - "room_list_expand_section": "Expand room list section", - "room_list_collapse_section": "Collapse room list section", - "prev_unread_room": "Previous unread room or DM", - "prev_room": "Previous room or DM", - "open_user_settings": "Open user settings", - "next_unread_room": "Next unread room or DM", - "next_room": "Next room or DM", - "navigate_prev_message_edit": "Navigate to previous message to edit", - "navigate_prev_history": "Previous recently visited room or space", - "navigate_next_message_edit": "Navigate to next message to edit", - "navigate_next_history": "Next recently visited room or space", - "keyboard_shortcuts_tab": "Open this settings tab", - "jump_to_read_marker": "Jump to oldest unread message", - "jump_room_search": "Jump to room search", - "jump_last_message": "Jump to last message", - "jump_first_message": "Jump to first message", - "go_home_view": "Go to Home View", - "dismiss_read_marker_and_jump_bottom": "Dismiss read marker and jump to bottom", - "composer_undo": "Undo edit", - "composer_toggle_quote": "Toggle Quote", - "composer_toggle_link": "Toggle Link", - "composer_toggle_italics": "Toggle Italics", - "composer_toggle_code_block": "Toggle Code Block", - "composer_toggle_bold": "Toggle Bold", - "composer_redo": "Redo edit", - "composer_new_line": "New line", - "composer_navigate_prev_history": "Navigate to previous message in composer history", - "composer_navigate_next_history": "Navigate to next message in composer history", - "composer_jump_start": "Jump to start of the composer", - "composer_jump_end": "Jump to end of the composer", - "close_dialog_menu": "Close dialog or context menu", + "category_calls": "Calls", "category_room_list": "Room List", "category_navigation": "Navigation", - "category_calls": "Calls", "category_autocomplete": "Autocomplete", + "composer_toggle_bold": "Toggle Bold", + "composer_toggle_italics": "Toggle Italics", + "composer_toggle_quote": "Toggle Quote", + "composer_toggle_code_block": "Toggle Code Block", + "composer_toggle_link": "Toggle Link", "cancel_reply": "Cancel replying to a message", - "autocomplete_navigate_prev": "Previous autocomplete suggestion", - "autocomplete_navigate_next": "Next autocomplete suggestion", - "autocomplete_force": "Force complete", + "navigate_next_message_edit": "Navigate to next message to edit", + "navigate_prev_message_edit": "Navigate to previous message to edit", + "composer_jump_start": "Jump to start of the composer", + "composer_jump_end": "Jump to end of the composer", + "composer_navigate_next_history": "Navigate to next message in composer history", + "composer_navigate_prev_history": "Navigate to previous message in composer history", + "send_sticker": "Send a sticker", + "toggle_microphone_mute": "Toggle microphone mute", + "toggle_webcam_mute": "Toggle webcam on/off", + "dismiss_read_marker_and_jump_bottom": "Dismiss read marker and jump to bottom", + "jump_to_read_marker": "Jump to oldest unread message", + "upload_file": "Upload a file", + "scroll_up_timeline": "Scroll up in the timeline", + "scroll_down_timeline": "Scroll down in the timeline", + "jump_room_search": "Jump to room search", + "room_list_select_room": "Select room from the room list", + "room_list_collapse_section": "Collapse room list section", + "room_list_expand_section": "Expand room list section", + "room_list_navigate_down": "Navigate down in the room list", + "room_list_navigate_up": "Navigate up in the room list", + "toggle_top_left_menu": "Toggle the top left menu", + "toggle_right_panel": "Toggle right panel", + "keyboard_shortcuts_tab": "Open this settings tab", + "go_home_view": "Go to Home View", + "next_unread_room": "Next unread room or DM", + "prev_unread_room": "Previous unread room or DM", + "next_room": "Next room or DM", + "prev_room": "Previous room or DM", "autocomplete_cancel": "Cancel autocomplete", - "activate_button": "Activate selected button" + "autocomplete_navigate_next": "Next autocomplete suggestion", + "autocomplete_navigate_prev": "Previous autocomplete suggestion", + "toggle_space_panel": "Toggle space panel", + "toggle_hidden_events": "Toggle hidden event visibility", + "jump_first_message": "Jump to first message", + "jump_last_message": "Jump to last message", + "composer_undo": "Undo edit", + "composer_redo": "Redo edit", + "navigate_prev_history": "Previous recently visited room or space", + "navigate_next_history": "Next recently visited room or space", + "switch_to_space": "Switch to space by number", + "open_user_settings": "Open user settings", + "close_dialog_menu": "Close dialog or context menu", + "activate_button": "Activate selected button", + "composer_new_line": "New line", + "autocomplete_force": "Force complete", + "search": "Search (must be enabled)" }, "Something went wrong!": "Something went wrong!", "Image view": "Image view", @@ -3095,6 +3158,19 @@ "Continuing without email": "Continuing without email", "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.": "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.", "Email (optional)": "Email (optional)", + "report_content": { + "missing_reason": "Please fill why you're reporting.", + "ignore_user": "Ignore user", + "hide_messages_from_user": "Check if you want to hide all current and future messages from this user.", + "nature_disagreement": "What this user is writing is wrong.\nThis will be reported to the room moderators.", + "nature": "Please pick a nature and describe what makes this message abusive.", + "disagree": "Disagree", + "toxic_behaviour": "Toxic Behaviour", + "illegal_content": "Illegal Content", + "spam_or_propaganda": "Spam or propaganda", + "report_entire_room": "Report the entire room", + "report_content_to_homeserver": "Report Content to Your Homeserver Administrator" + }, "Unable to create room with moderation bot": "Unable to create room with moderation bot", "This user is displaying toxic behaviour, for instance by insulting other users or sharing adult-only content in a family-friendly room or otherwise violating the rules of this room.\nThis will be reported to the room moderators.": "This user is displaying toxic behaviour, for instance by insulting other users or sharing adult-only content in a family-friendly room or otherwise violating the rules of this room.\nThis will be reported to the room moderators.", "This user is displaying illegal behaviour, for instance by doxing people or threatening violence.\nThis will be reported to the room moderators who may escalate this to legal authorities.": "This user is displaying illegal behaviour, for instance by doxing people or threatening violence.\nThis will be reported to the room moderators who may escalate this to legal authorities.", @@ -3531,18 +3607,6 @@ "Switch to light mode": "Switch to light mode", "Switch to dark mode": "Switch to dark mode", "Switch theme": "Switch theme", - "a11y": { - "user_menu": "User menu", - "unread_messages": "Unread messages.", - "n_unread_messages_mentions": { - "other": "%(count)s unread messages including mentions.", - "one": "1 unread mention." - }, - "n_unread_messages": { - "other": "%(count)s unread messages.", - "one": "1 unread message." - } - }, "Could not load user profile": "Could not load user profile", "Decrypted event source": "Decrypted event source", "Decrypted source unavailable": "Decrypted source unavailable", @@ -3699,69 +3763,5 @@ "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Failed to set direct message tag": "Failed to set direct message tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", - "spaces": { - "error_no_permission_invite": "You do not have permissions to invite people to this space", - "error_no_permission_create_room": "You do not have permissions to create new rooms in this space", - "error_no_permission_add_room": "You do not have permissions to add rooms to this space", - "error_no_permission_add_space": "You do not have permissions to add spaces to this space" - }, - "setting": { - "help_about": { - "brand_version": "%(brand)s version:", - "olm_version": "Olm version:", - "help_link": "For help with using %(brand)s, click here.", - "help_link_chat_bot": "For help with using %(brand)s, click here or start a chat with our bot using the button below.", - "chat_bot": "Chat with %(brand)s Bot", - "title": "Help & About", - "versions": "Versions", - "homeserver": "Homeserver is %(homeserverUrl)s", - "identity_server": "Identity server is %(identityServerUrl)s", - "access_token_detail": "Your access token gives full access to your account. Do not share it with anyone.", - "clear_cache_reload": "Clear cache and reload" - } - }, - "room_list": { - "sort_unread_first": "Show rooms with unread messages first", - "show_previews": "Show previews of messages", - "sort_by": "Sort by", - "sort_by_activity": "Activity", - "sort_by_alphabet": "A-Z", - "sublist_options": "List options", - "show_n_more": { - "other": "Show %(count)s more", - "one": "Show %(count)s more" - }, - "show_less": "Show less", - "notification_options": "Notification options" - }, - "report_content": { - "missing_reason": "Please fill why you're reporting.", - "ignore_user": "Ignore user", - "hide_messages_from_user": "Check if you want to hide all current and future messages from this user.", - "nature_disagreement": "What this user is writing is wrong.\nThis will be reported to the room moderators.", - "nature": "Please pick a nature and describe what makes this message abusive.", - "disagree": "Disagree", - "toxic_behaviour": "Toxic Behaviour", - "illegal_content": "Illegal Content", - "spam_or_propaganda": "Spam or propaganda", - "report_entire_room": "Report the entire room", - "report_content_to_homeserver": "Report Content to Your Homeserver Administrator" - }, - "auth": { - "sso": "Single Sign On", - "continue_with_idp": "Continue with %(provider)s", - "sign_in_with_sso": "Sign in with single sign-on", - "reset_password_action": "Reset password", - "reset_password_title": "Reset your password", - "continue_with_sso": "Continue with %(ssoButtons)s", - "sso_or_username_password": "%(ssoButtons)s Or %(usernamePassword)s", - "sign_in_instead": "Already have an account? Sign in here", - "account_clash": "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).", - "account_clash_previous_account": "Continue with previous account", - "log_in_new_account": "Log in to your new account.", - "registration_successful": "Registration Successful", - "server_picker_title": "Host account on", - "server_picker_dialog_title": "Decide where your account is hosted" - } -} \ No newline at end of file + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" +} From ea3067b14583911fd202df2f3d86057822e6132a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 19 Sep 2023 10:42:46 +0100 Subject: [PATCH 8/8] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 326 ++++++++++++++++++------------------ 1 file changed, 163 insertions(+), 163 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ffbf444d1f..fd8a4c513c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -7,10 +7,37 @@ "Confirm adding this email address by using Single Sign On to prove your identity.": "Confirm adding this email address by using Single Sign On to prove your identity.", "auth": { "sso": "Single Sign On", + "sign_in_or_register": "Sign In or Create Account", + "sign_in_or_register_description": "Use your account or create a new one to continue.", + "sign_in_description": "Use your account to continue.", + "register_action": "Create Account", "continue_with_idp": "Continue with %(provider)s", "sign_in_with_sso": "Sign in with single sign-on", + "server_picker_failed_validate_homeserver": "Unable to validate homeserver", + "server_picker_invalid_url": "Invalid URL", + "server_picker_required": "Specify a homeserver", + "server_picker_matrix.org": "Matrix.org is the biggest public homeserver in the world, so it's a good place for many.", + "server_picker_title": "Sign into your homeserver", + "server_picker_intro": "We call the places where you can host your account 'homeservers'.", + "server_picker_custom": "Other homeserver", + "server_picker_explainer": "Use your preferred Matrix homeserver if you have one, or host your own.", + "server_picker_learn_more": "About homeservers", + "footer_powered_by_matrix": "powered by Matrix", + "sign_in_prompt": "Got an account? Sign in", + "create_account_prompt": "New here? Create an account", "reset_password_action": "Reset password", "reset_password_title": "Reset your password", + "unsupported_auth_email": "This homeserver does not support login using email address.", + "failed_homeserver_discovery": "Failed to perform homeserver discovery", + "unsupported_auth": "This homeserver doesn't offer any login flows that are supported by this client.", + "syncing": "Syncing…", + "signing_in": "Signing In…", + "sync_footer_subtitle": "If you've joined lots of rooms, this might take a while", + "registration_disabled": "Registration has been disabled on this homeserver.", + "failed_query_registration_methods": "Unable to query for supported registration methods.", + "unsupported_auth_msisdn": "This server does not support authentication with a phone number.", + "username_in_use": "Someone already has that username, please try another.", + "3pid_in_use": "That e-mail address or phone number is already in use.", "continue_with_sso": "Continue with %(ssoButtons)s", "sso_or_username_password": "%(ssoButtons)s Or %(usernamePassword)s", "sign_in_instead": "Sign in instead", @@ -18,52 +45,25 @@ "account_clash_previous_account": "Continue with previous account", "log_in_new_account": "Log in to your new account.", "registration_successful": "Registration Successful", - "server_picker_title": "Sign into your homeserver", "server_picker_dialog_title": "Decide where your account is hosted", - "verify_email_heading": "Verify your email to continue", - "verify_email_explainer": "We need to know it’s you before resetting your password. Click the link in the email we just sent to %(email)s", - "username_in_use": "Someone already has that username, please try another.", - "unsupported_auth_msisdn": "This server does not support authentication with a phone number.", - "unsupported_auth_email": "This homeserver does not support login using email address.", - "unsupported_auth": "This homeserver doesn't offer any login flows that are supported by this client.", - "syncing": "Syncing…", - "sync_footer_subtitle": "If you've joined lots of rooms, this might take a while", - "soft_logout_intro_unsupported_auth": "You cannot sign in to your account. Please contact your homeserver admin for more information.", - "soft_logout_intro_sso": "Sign in and regain access to your account.", - "soft_logout_intro_password": "Enter your password to sign in and regain access to your account.", - "soft_logout_heading": "You're signed out", - "signing_in": "Signing In…", - "sign_in_prompt": "Got an account? Sign in", - "sign_in_or_register_description": "Use your account or create a new one to continue.", - "sign_in_or_register": "Sign In or Create Account", - "sign_in_description": "Use your account to continue.", - "server_picker_required": "Specify a homeserver", - "server_picker_matrix.org": "Matrix.org is the biggest public homeserver in the world, so it's a good place for many.", - "server_picker_learn_more": "About homeservers", - "server_picker_invalid_url": "Invalid URL", - "server_picker_intro": "We call the places where you can host your account 'homeservers'.", - "server_picker_failed_validate_homeserver": "Unable to validate homeserver", - "server_picker_explainer": "Use your preferred Matrix homeserver if you have one, or host your own.", - "server_picker_custom": "Other homeserver", - "registration_disabled": "Registration has been disabled on this homeserver.", - "register_action": "Create Account", "incorrect_password": "Incorrect password", - "forgot_password_prompt": "Forgotten your password?", - "forgot_password_email_required": "The email address linked to your account must be entered.", - "forgot_password_email_invalid": "The email address doesn't appear to be valid.", - "footer_powered_by_matrix": "powered by Matrix", "failed_soft_logout_auth": "Failed to re-authenticate", - "failed_query_registration_methods": "Unable to query for supported registration methods.", - "failed_homeserver_discovery": "Failed to perform homeserver discovery", - "enter_email_heading": "Enter your email to reset password", - "enter_email_explainer": "%(homeserver)s will send you a verification link to let you reset your password.", - "create_account_prompt": "New here? Create an account", + "forgot_password_prompt": "Forgotten your password?", + "soft_logout_intro_password": "Enter your password to sign in and regain access to your account.", + "soft_logout_intro_sso": "Sign in and regain access to your account.", + "soft_logout_intro_unsupported_auth": "You cannot sign in to your account. Please contact your homeserver admin for more information.", + "soft_logout_heading": "You're signed out", + "check_email_explainer": "Follow the instructions sent to %(email)s", "check_email_wrong_email_prompt": "Wrong email address?", "check_email_wrong_email_button": "Re-enter email address", - "check_email_resend_tooltip": "Verification link email resent!", "check_email_resend_prompt": "Did not receive it?", - "check_email_explainer": "Follow the instructions sent to %(email)s", - "3pid_in_use": "That e-mail address or phone number is already in use." + "check_email_resend_tooltip": "Verification link email resent!", + "enter_email_heading": "Enter your email to reset password", + "enter_email_explainer": "%(homeserver)s will send you a verification link to let you reset your password.", + "forgot_password_email_required": "The email address linked to your account must be entered.", + "forgot_password_email_invalid": "The email address doesn't appear to be valid.", + "verify_email_heading": "Verify your email to continue", + "verify_email_explainer": "We need to know it’s you before resetting your password. Click the link in the email we just sent to %(email)s" }, "Confirm adding email": "Confirm adding email", "Click the button below to confirm adding this email address.": "Click the button below to confirm adding this email address.", @@ -250,6 +250,7 @@ "ios": "iOS", "android": "Android", "unnamed_space": "Unnamed Space", + "feedback": "Feedback", "report_a_bug": "Report a bug", "forward_message": "Forward message", "suggestions": "Suggestions", @@ -265,8 +266,7 @@ "support": "Support", "room_name": "Room name", "thread": "Thread", - "accessibility": "Accessibility", - "feedback": "Feedback" + "accessibility": "Accessibility" }, "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "The file '%(fileName)s' failed to upload.": "The file '%(fileName)s' failed to upload.", @@ -489,9 +489,9 @@ "category_advanced": "Advanced", "category_effects": "Effects", "category_other": "Other", + "join": "Joins room with given address", "view": "Views room with given address", "op": "Define the power level of a user", - "join": "Joins room with given address", "deop": "Deops user with given id" }, "Use an identity server": "Use an identity server", @@ -652,6 +652,10 @@ "m.call.hangup": { "dm": "Call ended" }, + "no_permission_messages_before_invite": "You don't have permission to view messages from before you were invited.", + "no_permission_messages_before_join": "You don't have permission to view messages from before you joined.", + "encrypted_historical_messages_unavailable": "Encrypted messages before this point are unavailable.", + "historical_messages_unavailable": "You can't see earlier messages", "summary": { "format": "%(nameList)s %(transitionList)s", "joined_multiple": { @@ -790,13 +794,76 @@ "other": "%(oneUser)ssent %(count)s hidden messages", "one": "%(oneUser)ssent a hidden message" } - }, - "no_permission_messages_before_join": "You don't have permission to view messages from before you joined.", - "no_permission_messages_before_invite": "You don't have permission to view messages from before you were invited.", - "historical_messages_unavailable": "You can't see earlier messages", - "encrypted_historical_messages_unavailable": "Encrypted messages before this point are unavailable." + } }, "Light high contrast": "Light high contrast", + "widget": { + "capability": { + "always_on_screen_viewing_another_room": "Remain on your screen when viewing another room, when running", + "always_on_screen_generic": "Remain on your screen while running", + "send_stickers_this_room": "Send stickers into this room", + "send_stickers_active_room": "Send stickers into your active room", + "switch_room": "Change which room you're viewing", + "switch_room_message_user": "Change which room, message, or user you're viewing", + "change_topic_this_room": "Change the topic of this room", + "see_topic_change_this_room": "See when the topic changes in this room", + "change_topic_active_room": "Change the topic of your active room", + "see_topic_change_active_room": "See when the topic changes in your active room", + "change_name_this_room": "Change the name of this room", + "see_name_change_this_room": "See when the name changes in this room", + "change_name_active_room": "Change the name of your active room", + "see_name_change_active_room": "See when the name changes in your active room", + "change_avatar_this_room": "Change the avatar of this room", + "see_avatar_change_this_room": "See when the avatar changes in this room", + "change_avatar_active_room": "Change the avatar of your active room", + "see_avatar_change_active_room": "See when the avatar changes in your active room", + "remove_ban_invite_leave_this_room": "Remove, ban, or invite people to this room, and make you leave", + "receive_membership_this_room": "See when people join, leave, or are invited to this room", + "remove_ban_invite_leave_active_room": "Remove, ban, or invite people to your active room, and make you leave", + "receive_membership_active_room": "See when people join, leave, or are invited to your active room", + "send_stickers_this_room_as_you": "Send stickers to this room as you", + "see_sticker_posted_this_room": "See when a sticker is posted in this room", + "send_stickers_active_room_as_you": "Send stickers to your active room as you", + "see_sticker_posted_active_room": "See when anyone posts a sticker to your active room", + "byline_empty_state_key": "with an empty state key", + "byline_state_key": "with state key %(stateKey)s", + "any_room": "The above, but in any room you are joined or invited to as well", + "specific_room": "The above, but in as well", + "send_event_type_this_room": "Send %(eventType)s events as you in this room", + "see_event_type_sent_this_room": "See %(eventType)s events posted to this room", + "send_event_type_active_room": "Send %(eventType)s events as you in your active room", + "see_event_type_sent_active_room": "See %(eventType)s events posted to your active room", + "capability": "The %(capability)s capability", + "send_messages_this_room": "Send messages as you in this room", + "send_messages_active_room": "Send messages as you in your active room", + "see_messages_sent_this_room": "See messages posted to this room", + "see_messages_sent_active_room": "See messages posted to your active room", + "send_text_messages_this_room": "Send text messages as you in this room", + "send_text_messages_active_room": "Send text messages as you in your active room", + "see_text_messages_sent_this_room": "See text messages posted to this room", + "see_text_messages_sent_active_room": "See text messages posted to your active room", + "send_emotes_this_room": "Send emotes as you in this room", + "send_emotes_active_room": "Send emotes as you in your active room", + "see_sent_emotes_this_room": "See emotes posted to this room", + "see_sent_emotes_active_room": "See emotes posted to your active room", + "send_images_this_room": "Send images as you in this room", + "send_images_active_room": "Send images as you in your active room", + "see_images_sent_this_room": "See images posted to this room", + "see_images_sent_active_room": "See images posted to your active room", + "send_videos_this_room": "Send videos as you in this room", + "send_videos_active_room": "Send videos as you in your active room", + "see_videos_sent_this_room": "See videos posted to this room", + "see_videos_sent_active_room": "See videos posted to your active room", + "send_files_this_room": "Send general files as you in this room", + "send_files_active_room": "Send general files as you in your active room", + "see_sent_files_this_room": "See general files posted to this room", + "see_sent_files_active_room": "See general files posted to your active room", + "send_msgtype_this_room": "Send %(msgtype)s messages as you in this room", + "send_msgtype_active_room": "Send %(msgtype)s messages as you in your active room", + "see_msgtype_sent_this_room": "See %(msgtype)s messages posted to this room", + "see_msgtype_sent_active_room": "See %(msgtype)s messages posted to your active room" + } + }, "Can't start a new voice broadcast": "Can't start a new voice broadcast", "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.": "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.", "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.": "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.", @@ -1106,6 +1173,7 @@ "video_rooms_faq2_question": "Can I use text chat alongside the video call?", "video_rooms_faq2_answer": "Yes, the chat timeline is displayed alongside the video.", "notification_settings": "New Notification Settings", + "notification_settings_beta_title": "Notification Settings", "msc3531_hide_messages_pending_moderation": "Let moderators hide messages pending moderation.", "report_to_moderators": "Report to moderators", "latex_maths": "Render LaTeX maths in messages", @@ -1130,25 +1198,24 @@ "location_share_live": "Live Location Sharing", "location_share_live_description": "Temporary implementation. Locations persist in room history.", "dynamic_room_predecessors": "Dynamic room predecessors", + "dynamic_room_predecessors_description": "Enable MSC3946 (to support late-arriving room archives)", "voice_broadcast": "Voice broadcast", + "voice_broadcast_force_small_chunks": "Force 15s voice broadcast chunk length", + "oidc_native_flow": "Enable new native OIDC flows (Under active development)", "rust_crypto": "Rust cryptography implementation", + "render_reaction_images": "Render custom images in reactions", + "render_reaction_images_description": "Sometimes referred to as \"custom emojis\".", "hidebold": "Hide notification dot (only display counters badges)", "ask_to_join": "Enable ask to join", "new_room_decoration_ui": "Under active development, new room header & details interface", + "notifications": "Enable the notifications panel in the room header", + "unrealiable_e2e": "Unreliable in encrypted rooms", "beta_feature": "This is a beta feature", "click_for_info": "Click for more info", "leave_beta_reload": "Leaving the beta will reload %(brand)s.", "join_beta_reload": "Joining the beta will reload %(brand)s.", "leave_beta": "Leave the beta", - "join_beta": "Join the beta", - "voice_broadcast_force_small_chunks": "Force 15s voice broadcast chunk length", - "unrealiable_e2e": "Unreliable in encrypted rooms", - "render_reaction_images_description": "Sometimes referred to as \"custom emojis\".", - "render_reaction_images": "Render custom images in reactions", - "oidc_native_flow": "Enable new native OIDC flows (Under active development)", - "notifications": "Enable the notifications panel in the room header", - "notification_settings_beta_title": "Notification Settings", - "dynamic_room_predecessors_description": "Enable MSC3946 (to support late-arriving room archives)" + "join_beta": "Join the beta" }, "Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.", "Introducing a simpler way to change your notification settings. Customize your %(brand)s, just the way you like.": "Introducing a simpler way to change your notification settings. Customize your %(brand)s, just the way you like.", @@ -1202,6 +1269,8 @@ "enable_markdown_description": "Start messages with /plain to send without markdown.", "show_nsfw_content": "Show NSFW content", "inline_url_previews_default": "Enable inline URL previews by default", + "inline_url_previews_room_account": "Enable URL previews for this room (only affects you)", + "inline_url_previews_room": "Enable URL previews by default for participants in this room", "prompt_invite": "Prompt before sending invites to potentially invalid matrix IDs", "show_breadcrumbs": "Show shortcuts to recently viewed rooms above the room list", "image_thumbnails": "Show previews/thumbnails for images", @@ -1232,9 +1301,7 @@ "enable_desktop_notifications_session": "Enable desktop notifications for this session", "show_message_desktop_notification": "Show message in desktop notification", "enable_audible_notifications_session": "Enable audible notifications for this session" - }, - "inline_url_previews_room_account": "Enable URL previews for this room (only affects you)", - "inline_url_previews_room": "Enable URL previews by default for participants in this room" + } }, "Your server doesn't support disabling sending read receipts.": "Your server doesn't support disabling sending read receipts.", "Use custom size": "Use custom size", @@ -1346,10 +1413,10 @@ "explore_rooms": "Explore Public Rooms", "create_room": "Create a Group Chat" }, - "Ongoing call": "Ongoing call", - "You do not have permission to start video calls": "You do not have permission to start video calls", - "There's no one here to call": "There's no one here to call", "You do not have permission to start voice calls": "You do not have permission to start voice calls", + "You do not have permission to start video calls": "You do not have permission to start video calls", + "Ongoing call": "Ongoing call", + "There's no one here to call": "There's no one here to call", "chat_effects": { "confetti_description": "Sends the given message with confetti", "confetti_message": "sends confetti", @@ -2019,11 +2086,14 @@ "Everyone in this room is verified": "Everyone in this room is verified", "Edit message": "Edit message", "From a thread": "From a thread", + "Encrypted by an unverified user.": "Encrypted by an unverified user.", + "Encrypted by a device not verified by its owner.": "Encrypted by a device not verified by its owner.", + "Encrypted by an unknown or deleted device.": "Encrypted by an unknown or deleted device.", + "The authenticity of this encrypted message can't be guaranteed on this device.": "The authenticity of this encrypted message can't be guaranteed on this device.", + "Encrypted by an unverified session": "Encrypted by an unverified session", "This event could not be displayed": "This event could not be displayed", " in %(room)s": " in %(room)s", - "Encrypted by an unverified session": "Encrypted by an unverified session", "Unencrypted": "Unencrypted", - "The authenticity of this encrypted message can't be guaranteed on this device.": "The authenticity of this encrypted message can't be guaranteed on this device.", "This message could not be decrypted": "This message could not be decrypted", "Sending your message…": "Sending your message…", "Encrypting your message…": "Encrypting your message…", @@ -2866,29 +2936,29 @@ "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.", "Clear all data": "Clear all data", "create_room": { + "name_validation_required": "Please enter a name for the room", + "join_rule_restricted_label": "Everyone in will be able to find and join this room.", + "join_rule_change_notice": "You can change this at any time from room settings.", + "join_rule_public_parent_space_label": "Anyone will be able to find and join this room, not just members of .", + "join_rule_public_label": "Anyone will be able to find and join this room.", + "join_rule_invite_label": "Only people invited will be able to find and join this room.", + "join_rule_knock_label": "Anyone can request to join, but admins or moderators need to grant access. You can change this later.", + "encrypted_video_room_warning": "You can't disable this later. The room will be encrypted but the embedded call will not.", + "encrypted_warning": "You can't disable this later. Bridges & most bots won't work yet.", + "encryption_forced": "Your server requires encryption to be enabled in private rooms.", + "encryption_label": "Enable end-to-end encryption", + "unfederated_label_default_off": "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.", + "unfederated_label_default_on": "You might disable this if the room will be used for collaborating with external teams who have their own homeserver. This cannot be changed later.", "title_video_room": "Create a video room", "title_public_room": "Create a public room", "title_private_room": "Create a private room", - "action_create_video_room": "Create video room", - "action_create_room": "Create room", - "unfederated_label_default_on": "You might disable this if the room will be used for collaborating with external teams who have their own homeserver. This cannot be changed later.", - "unfederated_label_default_off": "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.", - "unfederated": "Block anyone not part of %(serverName)s from ever joining this room.", "topic_label": "Topic (optional)", "room_visibility_label": "Room visibility", - "name_validation_required": "Please enter a name for the room", - "join_rule_restricted_label": "Everyone in will be able to find and join this room.", - "join_rule_restricted": "Visible to space members", - "join_rule_public_parent_space_label": "Anyone will be able to find and join this room, not just members of .", - "join_rule_public_label": "Anyone will be able to find and join this room.", - "join_rule_knock_label": "Anyone can request to join, but admins or moderators need to grant access. You can change this later.", - "join_rule_invite_label": "Only people invited will be able to find and join this room.", "join_rule_invite": "Private room (invite only)", - "join_rule_change_notice": "You can change this at any time from room settings.", - "encryption_label": "Enable end-to-end encryption", - "encryption_forced": "Your server requires encryption to be enabled in private rooms.", - "encrypted_warning": "You can't disable this later. Bridges & most bots won't work yet.", - "encrypted_video_room_warning": "You can't disable this later. The room will be encrypted but the embedded call will not." + "join_rule_restricted": "Visible to space members", + "unfederated": "Block anyone not part of %(serverName)s from ever joining this room.", + "action_create_video_room": "Create video room", + "action_create_room": "Create room" }, "Anyone in will be able to find and join.": "Anyone in will be able to find and join.", "Anyone will be able to find and join this space, not just members of .": "Anyone will be able to find and join this space, not just members of .", @@ -3018,6 +3088,15 @@ "Are you sure you want to end this poll? This will show the final results of the poll and stop people from being able to vote.": "Are you sure you want to end this poll? This will show the final results of the poll and stop people from being able to vote.", "An error has occurred.": "An error has occurred.", "MB": "MB", + "feedback": { + "sent": "Feedback sent", + "comment_label": "Comment", + "platform_username": "Your platform and username will be noted to help us use your feedback as much as we can.", + "may_contact_label": "You may contact me if you want to follow up or to let me test out upcoming ideas", + "pro_type": "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", + "existing_issue_link": "Please view existing bugs on Github first. No match? Start a new one.", + "send_feedback_action": "Send feedback" + }, "You don't have permission to do this": "You don't have permission to do this", "Sending": "Sending", "Sent": "Sent", @@ -3689,84 +3768,5 @@ "Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Failed to set direct message tag": "Failed to set direct message tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", - "widget": { - "capability": { - "always_on_screen_viewing_another_room": "Remain on your screen when viewing another room, when running", - "always_on_screen_generic": "Remain on your screen while running", - "send_stickers_this_room": "Send stickers into this room", - "send_stickers_active_room": "Send stickers into your active room", - "switch_room": "Change which room you're viewing", - "switch_room_message_user": "Change which room, message, or user you're viewing", - "change_topic_this_room": "Change the topic of this room", - "see_topic_change_this_room": "See when the topic changes in this room", - "change_topic_active_room": "Change the topic of your active room", - "see_topic_change_active_room": "See when the topic changes in your active room", - "change_name_this_room": "Change the name of this room", - "see_name_change_this_room": "See when the name changes in this room", - "change_name_active_room": "Change the name of your active room", - "see_name_change_active_room": "See when the name changes in your active room", - "change_avatar_this_room": "Change the avatar of this room", - "see_avatar_change_this_room": "See when the avatar changes in this room", - "change_avatar_active_room": "Change the avatar of your active room", - "see_avatar_change_active_room": "See when the avatar changes in your active room", - "remove_ban_invite_leave_this_room": "Remove, ban, or invite people to this room, and make you leave", - "receive_membership_this_room": "See when people join, leave, or are invited to this room", - "remove_ban_invite_leave_active_room": "Remove, ban, or invite people to your active room, and make you leave", - "receive_membership_active_room": "See when people join, leave, or are invited to your active room", - "send_stickers_this_room_as_you": "Send stickers to this room as you", - "see_sticker_posted_this_room": "See when a sticker is posted in this room", - "send_stickers_active_room_as_you": "Send stickers to your active room as you", - "see_sticker_posted_active_room": "See when anyone posts a sticker to your active room", - "byline_empty_state_key": "with an empty state key", - "byline_state_key": "with state key %(stateKey)s", - "any_room": "The above, but in any room you are joined or invited to as well", - "specific_room": "The above, but in as well", - "send_event_type_this_room": "Send %(eventType)s events as you in this room", - "see_event_type_sent_this_room": "See %(eventType)s events posted to this room", - "send_event_type_active_room": "Send %(eventType)s events as you in your active room", - "see_event_type_sent_active_room": "See %(eventType)s events posted to your active room", - "capability": "The %(capability)s capability", - "send_messages_this_room": "Send messages as you in this room", - "send_messages_active_room": "Send messages as you in your active room", - "see_messages_sent_this_room": "See messages posted to this room", - "see_messages_sent_active_room": "See messages posted to your active room", - "send_text_messages_this_room": "Send text messages as you in this room", - "send_text_messages_active_room": "Send text messages as you in your active room", - "see_text_messages_sent_this_room": "See text messages posted to this room", - "see_text_messages_sent_active_room": "See text messages posted to your active room", - "send_emotes_this_room": "Send emotes as you in this room", - "send_emotes_active_room": "Send emotes as you in your active room", - "see_sent_emotes_this_room": "See emotes posted to this room", - "see_sent_emotes_active_room": "See emotes posted to your active room", - "send_images_this_room": "Send images as you in this room", - "send_images_active_room": "Send images as you in your active room", - "see_images_sent_this_room": "See images posted to this room", - "see_images_sent_active_room": "See images posted to your active room", - "send_videos_this_room": "Send videos as you in this room", - "send_videos_active_room": "Send videos as you in your active room", - "see_videos_sent_this_room": "See videos posted to this room", - "see_videos_sent_active_room": "See videos posted to your active room", - "send_files_this_room": "Send general files as you in this room", - "send_files_active_room": "Send general files as you in your active room", - "see_sent_files_this_room": "See general files posted to this room", - "see_sent_files_active_room": "See general files posted to your active room", - "send_msgtype_this_room": "Send %(msgtype)s messages as you in this room", - "send_msgtype_active_room": "Send %(msgtype)s messages as you in your active room", - "see_msgtype_sent_this_room": "See %(msgtype)s messages posted to this room", - "see_msgtype_sent_active_room": "See %(msgtype)s messages posted to your active room" - } - }, - "feedback": { - "sent": "Feedback sent", - "comment_label": "Comment", - "platform_username": "Your platform and username will be noted to help us use your feedback as much as we can.", - "may_contact_label": "You may contact me if you want to follow up or to let me test out upcoming ideas", - "pro_type": "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", - "existing_issue_link": "Please view existing bugs on Github first. No match? Start a new one.", - "send_feedback_action": "Send feedback" - }, - "Encrypted by an unverified user.": "Encrypted by an unverified user.", - "Encrypted by an unknown or deleted device.": "Encrypted by an unknown or deleted device.", - "Encrypted by a device not verified by its owner.": "Encrypted by a device not verified by its owner." -} \ No newline at end of file + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" +}