diff --git a/src/components/structures/RoomStatusBar.tsx b/src/components/structures/RoomStatusBar.tsx index 24530d4cc51..7afc9025e29 100644 --- a/src/components/structures/RoomStatusBar.tsx +++ b/src/components/structures/RoomStatusBar.tsx @@ -30,6 +30,7 @@ import AccessibleButton from "../views/elements/AccessibleButton"; import InlineSpinner from "../views/elements/InlineSpinner"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { RoomStatusBarUnsentMessages } from "./RoomStatusBarUnsentMessages"; +import ExternalLink from "../views/elements/ExternalLink"; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -213,9 +214,9 @@ export default class RoomStatusBar extends React.PureComponent { {}, { consentLink: (sub) => ( - + {sub} - + ), }, ); diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 85806913110..c1614b4e0ec 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -77,6 +77,7 @@ import MainSplit from "./MainSplit"; import RightPanel from "./RightPanel"; import SpaceHierarchy, { showRoom } from "./SpaceHierarchy"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; +import ExternalLink from "../views/elements/ExternalLink"; interface IProps { space: Room; @@ -593,9 +594,9 @@ const SpaceSetupPrivateInvite: React.FC<{ { b: (sub) => {sub}, link: () => ( - + app.element.io - + ), }, )} diff --git a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx index f4cf78681b2..5ced239e9a9 100644 --- a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx +++ b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx @@ -22,6 +22,7 @@ import DialogButtons from "../elements/DialogButtons"; import Modal, { ComponentProps } from "../../../Modal"; import SdkConfig from "../../../SdkConfig"; import { getPolicyUrl } from "../../../toasts/AnalyticsToast"; +import ExternalLink from "../elements/ExternalLink"; export enum ButtonClicked { Primary, @@ -55,10 +56,10 @@ export const AnalyticsLearnMoreDialog: React.FC = ({ { PrivacyPolicyUrl: (sub) => { return ( - + {sub} - + ); }, }, diff --git a/src/components/views/dialogs/FeedbackDialog.tsx b/src/components/views/dialogs/FeedbackDialog.tsx index 5c8d1e4acf3..2ed1d967c28 100644 --- a/src/components/views/dialogs/FeedbackDialog.tsx +++ b/src/components/views/dialogs/FeedbackDialog.tsx @@ -27,6 +27,7 @@ import InfoDialog from "./InfoDialog"; import { submitFeedback } from "../../../rageshake/submit-rageshake"; import { useStateToggle } from "../../../hooks/useStateToggle"; import StyledCheckbox from "../elements/StyledCheckbox"; +import ExternalLink from "../elements/ExternalLink"; interface IProps { feature?: string; @@ -130,16 +131,20 @@ const FeedbackDialog: React.FC = (props: IProps) => { { existingIssuesLink: (sub) => { return ( - + {sub} - + ); }, newIssueLink: (sub) => { return ( - + {sub} - + ); }, }, diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx index aca963e422b..8627e8b3d28 100644 --- a/src/components/views/dialogs/ServerPickerDialog.tsx +++ b/src/components/views/dialogs/ServerPickerDialog.tsx @@ -28,6 +28,7 @@ import StyledRadioButton from "../elements/StyledRadioButton"; import TextWithTooltip from "../elements/TextWithTooltip"; import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig"; +import ExternalLink from "../elements/ExternalLink"; interface IProps { title?: string; @@ -236,9 +237,13 @@ export default class ServerPickerDialog extends React.PureComponent

{_t("Learn more")}

- + {_t("About homeservers")} - + ); diff --git a/src/components/views/dialogs/TermsDialog.tsx b/src/components/views/dialogs/TermsDialog.tsx index b0835c3afc5..ce697f1f1e2 100644 --- a/src/components/views/dialogs/TermsDialog.tsx +++ b/src/components/views/dialogs/TermsDialog.tsx @@ -22,6 +22,7 @@ import { _t, pickBestLanguage } from "../../../languageHandler"; import DialogButtons from "../elements/DialogButtons"; import BaseDialog from "./BaseDialog"; import { ServicePolicyPair } from "../../../Terms"; +import ExternalLink from "../elements/ExternalLink"; interface ITermsCheckboxProps { onChange: (url: string, checked: boolean) => void; @@ -148,9 +149,9 @@ export default class TermsDialog extends React.PureComponent{summary} {termDoc[termsLang].name} - + - + { }, { nativeLink: (sub) => ( - + {sub} - + ), }, )} @@ -217,9 +218,13 @@ export default class EventIndexPanel extends React.Component<{}, IState> { }, { desktopLink: (sub) => ( - + {sub} - + ), }, )} diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index f939cba64cc..af0525d6c55 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -31,6 +31,7 @@ import { Action } from "../../../../../dispatcher/actions"; import { UserTab } from "../../../dialogs/UserTab"; import dis from "../../../../../dispatcher/dispatcher"; import CopyableText from "../../../elements/CopyableText"; +import ExternalLink from "../../../elements/ExternalLink"; interface IProps { closeSettingsFn: () => void; @@ -114,9 +115,9 @@ export default class HelpUserSettingsTab extends React.Component for (const tocEntry of tocLinks) { legalLinks.push( , ); } @@ -143,27 +144,31 @@ export default class HelpUserSettingsTab extends React.Component {}, { photo: (sub) => ( - {sub} - + ), author: (sub) => ( - + {sub} - + ), terms: (sub) => ( - {sub} - + ), }, )} @@ -175,27 +180,27 @@ export default class HelpUserSettingsTab extends React.Component {}, { colr: (sub) => ( - {sub} - + ), author: (sub) => ( - + {sub} - + ), terms: (sub) => ( - {sub} - + ), }, )} @@ -208,23 +213,31 @@ export default class HelpUserSettingsTab extends React.Component {}, { twemoji: (sub) => ( - + {sub} - + ), author: (sub) => ( - + {sub} - + ), terms: (sub) => ( - {sub} - + ), }, )} @@ -256,9 +269,9 @@ export default class HelpUserSettingsTab extends React.Component }, { a: (sub) => ( - + {sub} - + ), }, ); @@ -273,9 +286,9 @@ export default class HelpUserSettingsTab extends React.Component }, { a: (sub) => ( - + {sub} - + ), }, )} @@ -321,13 +334,13 @@ export default class HelpUserSettingsTab extends React.Component {}, { a: (sub) => ( - {sub} - + ), }, )} diff --git a/src/utils/ErrorUtils.tsx b/src/utils/ErrorUtils.tsx index 365bd916c5d..bd37ed44271 100644 --- a/src/utils/ErrorUtils.tsx +++ b/src/utils/ErrorUtils.tsx @@ -20,6 +20,7 @@ import { MatrixError, ConnectionError } from "matrix-js-sdk/src/http-api"; import { _t, _td, Tags, TranslatedString } from "../languageHandler"; import SdkConfig from "../SdkConfig"; import { ValidatedServerConfig } from "./ValidatedServerConfig"; +import ExternalLink from "../components/views/elements/ExternalLink"; export const resourceLimitStrings = { "monthly_active_user": _td("This homeserver has hit its Monthly Active User limit."), @@ -183,9 +184,9 @@ export function messageForConnectionError( {}, { a: (sub) => ( - + {sub} - + ), }, )} diff --git a/test/components/structures/RoomStatusBar-test.tsx b/test/components/structures/RoomStatusBar-test.tsx index a11bd899d39..de8260b26c3 100644 --- a/test/components/structures/RoomStatusBar-test.tsx +++ b/test/components/structures/RoomStatusBar-test.tsx @@ -14,11 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from "react"; +import { render } from "@testing-library/react"; import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client"; import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; +import { MatrixError } from "matrix-js-sdk/src/http-api"; -import { getUnsentMessages } from "../../../src/components/structures/RoomStatusBar"; +import RoomStatusBar, { getUnsentMessages } from "../../../src/components/structures/RoomStatusBar"; +import MatrixClientContext from "../../../src/contexts/MatrixClientContext"; import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { mkEvent, stubClient } from "../../test-utils/test-utils"; import { mkThread } from "../../test-utils/threads"; @@ -34,6 +38,7 @@ describe("RoomStatusBar", () => { stubClient(); client = MatrixClientPeg.get(); + client.getSyncStateData = jest.fn().mockReturnValue({}); room = new Room(ROOM_ID, client, client.getUserId()!, { pendingEventOrdering: PendingEventOrdering.Detached, }); @@ -47,6 +52,13 @@ describe("RoomStatusBar", () => { event.status = EventStatus.NOT_SENT; }); + const getComponent = () => + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + describe("getUnsentMessages", () => { it("returns no unsent messages", () => { expect(getUnsentMessages(room)).toHaveLength(0); @@ -88,4 +100,55 @@ describe("RoomStatusBar", () => { expect(pendingEvents.every((ev) => ev.getId() !== event.getId())).toBe(true); }); }); + + describe("", () => { + it("should render nothing when room has no error or unsent messages", () => { + const { container } = getComponent(); + expect(container.firstChild).toBe(null); + }); + + describe("unsent messages", () => { + it("should render warning when messages are unsent due to consent", () => { + const unsentMessage = mkEvent({ + event: true, + type: "m.room.message", + user: "@user1:server", + room: "!room1:server", + content: {}, + }); + unsentMessage.status = EventStatus.NOT_SENT; + unsentMessage.error = new MatrixError({ + errcode: "M_CONSENT_NOT_GIVEN", + data: { consent_uri: "terms.com" }, + }); + + room.addPendingEvent(unsentMessage, "123"); + + const { container } = getComponent(); + + expect(container).toMatchSnapshot(); + }); + + it("should render warning when messages are unsent due to resource limit", () => { + const unsentMessage = mkEvent({ + event: true, + type: "m.room.message", + user: "@user1:server", + room: "!room1:server", + content: {}, + }); + unsentMessage.status = EventStatus.NOT_SENT; + unsentMessage.error = new MatrixError({ + errcode: "M_RESOURCE_LIMIT_EXCEEDED", + data: { limit_type: "monthly_active_user" }, + }); + + room.addPendingEvent(unsentMessage, "123"); + + const { container } = getComponent(); + + expect(container).toMatchSnapshot(); + }); + }); + }); }); diff --git a/test/components/structures/__snapshots__/RoomStatusBar-test.tsx.snap b/test/components/structures/__snapshots__/RoomStatusBar-test.tsx.snap new file mode 100644 index 00000000000..ed969114ec4 --- /dev/null +++ b/test/components/structures/__snapshots__/RoomStatusBar-test.tsx.snap @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RoomStatusBar unsent messages should render warning when messages are unsent due to consent 1`] = ` +
+
+
+
+
+ + ! + +
+
+
+
+ + You can't send any messages until you review and agree to + + our terms and conditions + + + . + +
+
+ You can select all or individual messages to retry or delete +
+
+
+
+ Delete all +
+
+ Retry all +
+
+
+
+
+`; + +exports[`RoomStatusBar unsent messages should render warning when messages are unsent due to resource limit 1`] = ` +
+
+
+
+
+ + ! + +
+
+
+
+ Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service. +
+
+ You can select all or individual messages to retry or delete +
+
+
+
+ Delete all +
+
+ Retry all +
+
+
+
+
+`; diff --git a/test/components/views/dialogs/__snapshots__/FeedbackDialog-test.tsx.snap b/test/components/views/dialogs/__snapshots__/FeedbackDialog-test.tsx.snap index 2682f5234cc..2c02b622479 100644 --- a/test/components/views/dialogs/__snapshots__/FeedbackDialog-test.tsx.snap +++ b/test/components/views/dialogs/__snapshots__/FeedbackDialog-test.tsx.snap @@ -38,19 +38,27 @@ exports[`FeedbackDialog should respect feedback config 1`] = ` Please view existing bugs on Github + first. No match? Start a new one + . diff --git a/test/utils/__snapshots__/ErrorUtils-test.ts.snap b/test/utils/__snapshots__/ErrorUtils-test.ts.snap index bad4e0a1447..572c14f91cd 100644 --- a/test/utils/__snapshots__/ErrorUtils-test.ts.snap +++ b/test/utils/__snapshots__/ErrorUtils-test.ts.snap @@ -6,11 +6,15 @@ exports[`messageForConnectionError should match snapshot for ConnectionError 1`] Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate + is trusted, and that a browser extension is not blocking requests.