From ddd8bdc00e3fb966ec919c0b93e0785879d2d00f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 19 Nov 2020 11:15:42 -0700 Subject: [PATCH] Move all the capability copy to its own class --- .../WidgetCapabilitiesPromptDialog.tsx | 135 +------ src/i18n/strings/en_EN.json | 84 +++-- src/widgets/CapabilityText.tsx | 342 ++++++++++++++++++ 3 files changed, 413 insertions(+), 148 deletions(-) create mode 100644 src/widgets/CapabilityText.tsx diff --git a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx index 7e332e6e9d..9e2481f524 100644 --- a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx +++ b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx @@ -16,113 +16,19 @@ limitations under the License. import React from 'react'; import BaseDialog from "./BaseDialog"; -import { _t, _td, TranslatedString } from "../../../languageHandler"; +import { _t } from "../../../languageHandler"; import { IDialogProps } from "./IDialogProps"; -import { Capability, EventDirection, MatrixCapabilities, Widget, WidgetEventCapability } from "matrix-widget-api"; +import { + Capability, + Widget, + WidgetEventCapability, + WidgetKind +} from "matrix-widget-api"; import { objectShallowClone } from "../../../utils/objects"; -import { ElementWidgetCapabilities } from "../../../stores/widgets/ElementWidgetCapabilities"; -import { EventType, MsgType } from "matrix-js-sdk/lib/@types/event"; import StyledCheckbox from "../elements/StyledCheckbox"; import DialogButtons from "../elements/DialogButtons"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; - -// TODO: These messaging things can probably get their own store of some sort -const SIMPLE_CAPABILITY_MESSAGES = { - [MatrixCapabilities.AlwaysOnScreen]: _td("Remain on your screen while running"), - [MatrixCapabilities.StickerSending]: _td("Send stickers into your active room"), - [ElementWidgetCapabilities.CanChangeViewedRoom]: _td("Change which room you're viewing"), -}; -const SEND_RECV_EVENT_CAPABILITY_MESSAGES = { - [EventType.RoomTopic]: { - // TODO: We probably want to say "this room" when we can - [EventDirection.Send]: _td("Change the topic of your active room"), - [EventDirection.Receive]: _td("See when the topic changes in your active room"), - }, - [EventType.RoomName]: { - [EventDirection.Send]: _td("Change the name of your active room"), - [EventDirection.Receive]: _td("See when the name changes in your active room"), - }, - [EventType.RoomAvatar]: { - [EventDirection.Send]: _td("Change the avatar of your active room"), - [EventDirection.Receive]: _td("See when the avatar changes in your active room"), - }, - // TODO: Add more as needed -}; -function textForEventCapabilitiy(cap: WidgetEventCapability): { primary: TranslatedString, byline: TranslatedString } { - let primary: TranslatedString; - let byline: TranslatedString; - - if (cap.isState) { - byline = cap.keyStr - ? _t("with state key %(stateKey)s", {stateKey: cap.keyStr}) - : _t("with an empty state key"); - } - - const srMessages = SEND_RECV_EVENT_CAPABILITY_MESSAGES[cap.eventType]; - if (srMessages && srMessages[cap.direction]) { - primary = _t(srMessages[cap.direction]); - } else { - if (cap.eventType === EventType.RoomMessage) { - if (cap.direction === EventDirection.Receive) { - if (!cap.keyStr) { - primary = _t("See messages sent in your active room"); - } else { - if (cap.keyStr === MsgType.Text) { - primary = _t("See text messages sent in your active room"); - } else if (cap.keyStr === MsgType.Emote) { - primary = _t("See emotes sent in your active room"); - } else if (cap.keyStr === MsgType.Image) { - primary = _t("See images sent in your active room"); - } else if (cap.keyStr === MsgType.Video) { - primary = _t("See videos sent in your active room"); - } else if (cap.keyStr === MsgType.File) { - primary = _t("See general files sent in your active room"); - } else { - primary = _t( - "See %(msgtype)s messages sent in your active room", - {msgtype: cap.keyStr}, {code: sub => {sub}}, - ); - } - } - } else { - if (!cap.keyStr) { - primary = _t("Send messages as you in your active room"); - } else { - if (cap.keyStr === MsgType.Text) { - primary = _t("Send text messages as you in your active room"); - } else if (cap.keyStr === MsgType.Emote) { - primary = _t("Send emotes as you in your active room"); - } else if (cap.keyStr === MsgType.Image) { - primary = _t("Send images as you in your active room"); - } else if (cap.keyStr === MsgType.Video) { - primary = _t("Send videos as you in your active room"); - } else if (cap.keyStr === MsgType.File) { - primary = _t("Send general files as you in your active room"); - } else { - primary = _t( - "Send %(msgtype)s messages as you in your active room", - {msgtype: cap.keyStr}, {code: sub => {sub}}, - ); - } - } - } - } else { - if (cap.direction === EventDirection.Receive) { - primary = _t( - "See %(eventType)s events sent in your active room", - {eventType: cap.eventType}, {code: sub => {sub}}, - ); - } else { - primary = _t( - "Send %(eventType)s events as you in your active room", - {eventType: cap.eventType}, {code: sub => {sub}}, - ); - } - } - } - - return {primary, byline}; -} +import { CapabilityText } from "../../../widgets/CapabilityText"; export function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] { return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]"); @@ -135,6 +41,7 @@ function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) interface IProps extends IDialogProps { requestedCapabilities: Set; widget: Widget; + widgetKind: WidgetKind; // TODO: Refactor into the Widget class } interface IBooleanStates { @@ -194,22 +101,10 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent< public render() { const checkboxRows = Object.entries(this.state.booleanStates).map(([cap, isChecked], i) => { - const evCap = this.eventPermissionsMap.get(cap); - - let text: TranslatedString; - let byline: TranslatedString; - if (evCap) { - const t = textForEventCapabilitiy(evCap); - text = t.primary; - byline = t.byline; - } else if (SIMPLE_CAPABILITY_MESSAGES[cap]) { - text = _t(SIMPLE_CAPABILITY_MESSAGES[cap]); - } else { - text = _t( - "The %(capability)s capability", - {capability: cap}, {code: sub => {sub}}, - ); - } + const text = CapabilityText.for(cap, this.props.widgetKind); + const byline = text.byline + ? {text.byline} + : null; return (
@@ -217,8 +112,8 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent< key={cap + i} checked={isChecked} onChange={() => this.onToggle(cap)} - >{text} - {byline ? {byline} : null} + >{text.primary} + {byline}
); }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 046190c7af..03bdf512ee 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -569,6 +569,62 @@ "%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …", "%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …", "%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …", + "Remain on your screen when you leave this room (when running)": "Remain on your screen when you leave this room (when running)", + "Remain on your screen while running": "Remain on your screen while running", + "Send stickers into this room": "Send stickers into this room", + "Send stickers into your active room": "Send stickers into your active room", + "Change which room you're viewing": "Change which room you're viewing", + "Change the topic of this room": "Change the topic of this room", + "See when the topic changes in this room": "See when the topic changes in this room", + "Change the topic of your active room": "Change the topic of your active room", + "See when the topic changes in your active room": "See when the topic changes in your active room", + "Change the name of this room": "Change the name of this room", + "See when the name changes in this room": "See when the name changes in this room", + "Change the name of your active room": "Change the name of your active room", + "See when the name changes in your active room": "See when the name changes in your active room", + "Change the avatar of this room": "Change the avatar of this room", + "See when the avatar changes in this room": "See when the avatar changes in this room", + "Change the avatar of your active room": "Change the avatar of your active room", + "See when the avatar changes in your active room": "See when the avatar changes in your active room", + "Send stickers to this room as you": "Send stickers to this room as you", + "See when a sticker is posted in this room": "See when a sticker is posted in this room", + "Send stickers to your active room as you": "Send stickers to your active room as you", + "See when anyone posts a sticker to your active room": "See when anyone posts a sticker to your active room", + "with an empty state key": "with an empty state key", + "with state key %(stateKey)s": "with state key %(stateKey)s", + "Send %(eventType)s events as you in this room": "Send %(eventType)s events as you in this room", + "See %(eventType)s events posted to this room": "See %(eventType)s events posted to this room", + "Send %(eventType)s events as you in your active room": "Send %(eventType)s events as you in your active room", + "See %(eventType)s events posted to your active room": "See %(eventType)s events posted to your active room", + "The %(capability)s capability": "The %(capability)s capability", + "Send messages as you in this room": "Send messages as you in this room", + "Send messages as you in your active room": "Send messages as you in your active room", + "See messages posted to this room": "See messages posted to this room", + "See messages posted to your active room": "See messages posted to your active room", + "Send text messages as you in this room": "Send text messages as you in this room", + "Send text messages as you in your active room": "Send text messages as you in your active room", + "See text messages posted to this room": "See text messages posted to this room", + "See text messages posted to your active room": "See text messages posted to your active room", + "Send emotes as you in this room": "Send emotes as you in this room", + "Send emotes as you in your active room": "Send emotes as you in your active room", + "See emotes posted to this room": "See emotes posted to this room", + "See emotes posted to your active room": "See emotes posted to your active room", + "Send images as you in this room": "Send images as you in this room", + "Send images as you in your active room": "Send images as you in your active room", + "See images posted to this room": "See images posted to this room", + "See images posted to your active room": "See images posted to your active room", + "Send videos as you in this room": "Send videos as you in this room", + "Send videos as you in your active room": "Send videos as you in your active room", + "See videos posted to this room": "See videos posted to this room", + "See videos posted to your active room": "See videos posted to your active room", + "Send general files as you in this room": "Send general files as you in this room", + "Send general files as you in your active room": "Send general files as you in your active room", + "See general files posted to this room": "See general files posted to this room", + "See general files posted to your active room": "See general files posted to your active room", + "Send %(msgtype)s messages as you in this room": "Send %(msgtype)s messages as you in this room", + "Send %(msgtype)s messages as you in your active room": "Send %(msgtype)s messages as you in your active room", + "See %(msgtype)s messages posted to this room": "See %(msgtype)s messages posted to this room", + "See %(msgtype)s messages posted to your active room": "See %(msgtype)s messages posted to your active room", "Cannot reach homeserver": "Cannot reach homeserver", "Ensure you have a stable internet connection, or get in touch with the server admin": "Ensure you have a stable internet connection, or get in touch with the server admin", "Your %(brand)s is misconfigured": "Your %(brand)s is misconfigured", @@ -2123,34 +2179,6 @@ "Upload Error": "Upload Error", "Verify other session": "Verify other session", "Verification Request": "Verification Request", - "Remain on your screen while running": "Remain on your screen while running", - "Send stickers into your active room": "Send stickers into your active room", - "Change which room you're viewing": "Change which room you're viewing", - "Change the topic of your active room": "Change the topic of your active room", - "See when the topic changes in your active room": "See when the topic changes in your active room", - "Change the name of your active room": "Change the name of your active room", - "See when the name changes in your active room": "See when the name changes in your active room", - "Change the avatar of your active room": "Change the avatar of your active room", - "See when the avatar changes in your active room": "See when the avatar changes in your active room", - "with state key %(stateKey)s": "with state key %(stateKey)s", - "with an empty state key": "with an empty state key", - "See messages sent in your active room": "See messages sent in your active room", - "See text messages sent in your active room": "See text messages sent in your active room", - "See emotes sent in your active room": "See emotes sent in your active room", - "See images sent in your active room": "See images sent in your active room", - "See videos sent in your active room": "See videos sent in your active room", - "See general files sent in your active room": "See general files sent in your active room", - "See %(msgtype)s messages sent in your active room": "See %(msgtype)s messages sent in your active room", - "Send messages as you in your active room": "Send messages as you in your active room", - "Send text messages as you in your active room": "Send text messages as you in your active room", - "Send emotes as you in your active room": "Send emotes as you in your active room", - "Send images as you in your active room": "Send images as you in your active room", - "Send videos as you in your active room": "Send videos as you in your active room", - "Send general files as you in your active room": "Send general files as you in your active room", - "Send %(msgtype)s messages as you in your active room": "Send %(msgtype)s messages as you in your active room", - "See %(eventType)s events sent in your active room": "See %(eventType)s events sent in your active room", - "Send %(eventType)s events as you in your active room": "Send %(eventType)s events as you in your active room", - "The %(capability)s capability": "The %(capability)s capability", "Approve widget permissions": "Approve widget permissions", "This widget would like to:": "This widget would like to:", "Remember my selection for this widget": "Remember my selection for this widget", diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx new file mode 100644 index 0000000000..cb407dc686 --- /dev/null +++ b/src/widgets/CapabilityText.tsx @@ -0,0 +1,342 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { Capability, EventDirection, MatrixCapabilities, WidgetEventCapability, WidgetKind } from "matrix-widget-api"; +import { _t, _td, TranslatedString } from "../languageHandler"; +import { EventType, MsgType } from "matrix-js-sdk/src/@types/event"; +import { ElementWidgetCapabilities } from "../stores/widgets/ElementWidgetCapabilities"; +import React from "react"; + +type GENERIC_WIDGET_KIND = "generic"; +const GENERIC_WIDGET_KIND: GENERIC_WIDGET_KIND = "generic"; + +interface ISendRecvStaticCapText { + // @ts-ignore - TS wants the key to be a string, but we know better + [eventType: EventType]: { + // @ts-ignore - TS wants the key to be a string, but we know better + [widgetKind: WidgetKind | GENERIC_WIDGET_KIND]: { + // @ts-ignore - TS wants the key to be a string, but we know better + [direction: EventDirection]: string; + }; + }; +} + +interface IStaticCapText { + // @ts-ignore - TS wants the key to be a string, but we know better + [capability: Capability]: { + // @ts-ignore - TS wants the key to be a string, but we know better + [widgetKind: WidgetKind | GENERIC_WIDGET_KIND]: string; + }; +} + +export interface TranslatedCapabilityText { + primary: TranslatedString; + byline?: TranslatedString; +} + +export class CapabilityText { + private static simpleCaps: IStaticCapText = { + [MatrixCapabilities.AlwaysOnScreen]: { + [WidgetKind.Room]: _td("Remain on your screen when you leave this room (when running)"), + [GENERIC_WIDGET_KIND]: _td("Remain on your screen while running"), + }, + [MatrixCapabilities.StickerSending]: { + [WidgetKind.Room]: _td("Send stickers into this room"), + [GENERIC_WIDGET_KIND]: _td("Send stickers into your active room"), + }, + [ElementWidgetCapabilities.CanChangeViewedRoom]: { + [GENERIC_WIDGET_KIND]: _td("Change which room you're viewing"), + }, + }; + + private static stateSendRecvCaps: ISendRecvStaticCapText = { + [EventType.RoomTopic]: { + [WidgetKind.Room]: { + [EventDirection.Send]: _td("Change the topic of this room"), + [EventDirection.Receive]: _td("See when the topic changes in this room"), + }, + [GENERIC_WIDGET_KIND]: { + [EventDirection.Send]: _td("Change the topic of your active room"), + [EventDirection.Receive]: _td("See when the topic changes in your active room"), + }, + }, + [EventType.RoomName]: { + [WidgetKind.Room]: { + [EventDirection.Send]: _td("Change the name of this room"), + [EventDirection.Receive]: _td("See when the name changes in this room"), + }, + [GENERIC_WIDGET_KIND]: { + [EventDirection.Send]: _td("Change the name of your active room"), + [EventDirection.Receive]: _td("See when the name changes in your active room"), + }, + }, + [EventType.RoomAvatar]: { + [WidgetKind.Room]: { + [EventDirection.Send]: _td("Change the avatar of this room"), + [EventDirection.Receive]: _td("See when the avatar changes in this room"), + }, + [GENERIC_WIDGET_KIND]: { + [EventDirection.Send]: _td("Change the avatar of your active room"), + [EventDirection.Receive]: _td("See when the avatar changes in your active room"), + }, + }, + }; + + private static nonStateSendRecvCaps: ISendRecvStaticCapText = { + [EventType.Sticker]: { + [WidgetKind.Room]: { + [EventDirection.Send]: _td("Send stickers to this room as you"), + [EventDirection.Receive]: _td("See when a sticker is posted in this room"), + }, + [GENERIC_WIDGET_KIND]: { + [EventDirection.Send]: _td("Send stickers to your active room as you"), + [EventDirection.Receive]: _td("See when anyone posts a sticker to your active room"), + }, + }, + }; + + private static bylineFor(eventCap: WidgetEventCapability): TranslatedString { + if (eventCap.isState) { + return !eventCap.keyStr + ? _t("with an empty state key") + : _t("with state key %(stateKey)s", {stateKey: eventCap.keyStr}); + } + return null; // room messages are handled specially + } + + public static for(capability: Capability, kind: WidgetKind): TranslatedCapabilityText { + // First see if we have a super simple line of text to provide back + if (CapabilityText.simpleCaps[capability]) { + const textForKind = CapabilityText.simpleCaps[capability]; + if (textForKind[kind]) return {primary: _t(textForKind[kind])}; + if (textForKind[GENERIC_WIDGET_KIND]) return {primary: _t(textForKind[GENERIC_WIDGET_KIND])}; + + // ... we'll fall through to the generic capability processing at the end of this + // function if we fail to locate a simple string and the capability isn't for an + // event. + } + + // We didn't have a super simple line of text, so try processing the capability as the + // more complex event send/receive permission type. + const [eventCap] = WidgetEventCapability.findEventCapabilities([capability]); + if (eventCap) { + // Special case room messages so they show up a bit cleaner to the user. Result is + // effectively "Send images" instead of "Send messages... of type images" if we were + // to handle the msgtype nuances in this function. + if (!eventCap.isState && eventCap.eventType === EventType.RoomMessage) { + return CapabilityText.forRoomMessageCap(eventCap, kind); + } + + // See if we have a static line of text to provide for the given event type and + // direction. The hope is that we do for common event types for friendlier copy. + const evSendRecv = eventCap.isState + ? CapabilityText.stateSendRecvCaps + : CapabilityText.nonStateSendRecvCaps; + if (evSendRecv[eventCap.eventType]) { + const textForKind = evSendRecv[eventCap.eventType]; + const textForDirection = textForKind[kind] || textForKind[GENERIC_WIDGET_KIND]; + if (textForDirection && textForDirection[eventCap.direction]) { + return { + primary: _t(textForDirection[eventCap.direction]), + byline: CapabilityText.bylineFor(eventCap), + }; + } + } + + // We don't have anything simple, so just return a generic string for the event cap + if (kind === WidgetKind.Room) { + if (eventCap.direction === EventDirection.Send) { + return { + primary: _t("Send %(eventType)s events as you in this room", { + eventType: eventCap.eventType, + }, { + b: sub => {sub}, + }), + byline: CapabilityText.bylineFor(eventCap), + }; + } else { + return { + primary: _t("See %(eventType)s events posted to this room", { + eventType: eventCap.eventType, + }, { + b: sub => {sub}, + }), + byline: CapabilityText.bylineFor(eventCap), + }; + } + } else { // assume generic + if (eventCap.direction === EventDirection.Send) { + return { + primary: _t("Send %(eventType)s events as you in your active room", { + eventType: eventCap.eventType, + }, { + b: sub => {sub}, + }), + byline: CapabilityText.bylineFor(eventCap), + }; + } else { + return { + primary: _t("See %(eventType)s events posted to your active room", { + eventType: eventCap.eventType, + }, { + b: sub => {sub}, + }), + byline: CapabilityText.bylineFor(eventCap), + }; + } + } + } + + // We don't have enough context to render this capability specially, so we'll present it as-is + return { + primary: _t("The %(capability)s capability", {capability}, { + b: sub => {sub}, + }), + }; + } + + private static forRoomMessageCap(eventCap: WidgetEventCapability, kind: WidgetKind): TranslatedCapabilityText { + // First handle the case of "all messages" to make the switch later on a bit clearer + if (!eventCap.keyStr) { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send messages as you in this room") + : _t("Send messages as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See messages posted to this room") + : _t("See messages posted to your active room"), + }; + } + } + + // Now handle all the message types we care about. There are more message types available, however + // they are not as common so we don't bother rendering them. They'll fall into the generic case. + switch(eventCap.keyStr) { + case MsgType.Text: { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send text messages as you in this room") + : _t("Send text messages as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See text messages posted to this room") + : _t("See text messages posted to your active room"), + }; + } + } + case MsgType.Emote: { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send emotes as you in this room") + : _t("Send emotes as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See emotes posted to this room") + : _t("See emotes posted to your active room"), + }; + } + } + case MsgType.Image: { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send images as you in this room") + : _t("Send images as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See images posted to this room") + : _t("See images posted to your active room"), + }; + } + } + case MsgType.Video: { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send videos as you in this room") + : _t("Send videos as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See videos posted to this room") + : _t("See videos posted to your active room"), + }; + } + } + case MsgType.File: { + if (eventCap.direction === EventDirection.Send) { + return { + primary: kind === WidgetKind.Room + ? _t("Send general files as you in this room") + : _t("Send general files as you in your active room"), + }; + } else { + return { + primary: kind === WidgetKind.Room + ? _t("See general files posted to this room") + : _t("See general files posted to your active room"), + }; + } + } + default: { + let primary: TranslatedString; + if (eventCap.direction === EventDirection.Send) { + if (kind === WidgetKind.Room) { + primary = _t("Send %(msgtype)s messages as you in this room", { + msgtype: eventCap.keyStr, + }, { + b: sub => {sub}, + }); + } else { + primary = _t("Send %(msgtype)s messages as you in your active room", { + msgtype: eventCap.keyStr, + }, { + b: sub => {sub}, + }); + } + } else { + if (kind === WidgetKind.Room) { + primary = _t("See %(msgtype)s messages posted to this room", { + msgtype: eventCap.keyStr, + }, { + b: sub => {sub}, + }); + } else { + primary = _t("See %(msgtype)s messages posted to your active room", { + msgtype: eventCap.keyStr, + }, { + b: sub => {sub}, + }); + } + } + return {primary}; + } + } + } +}