diff --git a/src/analytics/AnalyticsNotice.tsx b/src/analytics/AnalyticsNotice.tsx index 8df2d742e..9ba78f0d2 100644 --- a/src/analytics/AnalyticsNotice.tsx +++ b/src/analytics/AnalyticsNotice.tsx @@ -8,14 +8,20 @@ Please see LICENSE in the repository root for full details. import { FC } from "react"; import { Trans } from "react-i18next"; -import { Link } from "../typography/Typography"; +import { ExternalLink } from "../button/Link"; export const AnalyticsNotice: FC = () => ( By participating in this beta, you consent to the collection of anonymous data, which we use to improve the product. You can find more information about which data we track in our{" "} - Privacy Policy and our{" "} - Cookie Policy. + + Privacy Policy + {" "} + and our{" "} + + Cookie Policy + + . ); diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index 5ee1c9eb9..392f8a7ab 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -19,7 +19,7 @@ import { captureException } from "@sentry/react"; import { sleep } from "matrix-js-sdk/src/utils"; import { Trans, useTranslation } from "react-i18next"; import { logger } from "matrix-js-sdk/src/logger"; -import { Button } from "@vector-im/compound-web"; +import { Button, Text } from "@vector-im/compound-web"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { useClientLegacy } from "../ClientContext"; @@ -28,10 +28,10 @@ import styles from "./LoginPage.module.css"; import Logo from "../icons/LogoLarge.svg?react"; import { LoadingView } from "../FullScreenView"; import { useRecaptcha } from "./useRecaptcha"; -import { Caption, Link } from "../typography/Typography"; import { usePageTitle } from "../usePageTitle"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { Config } from "../config/Config"; +import { ExternalLink, Link } from "../button/Link"; export const RegisterPage: FC = () => { const { t } = useTranslation(); @@ -201,24 +201,24 @@ export const RegisterPage: FC = () => { data-testid="register_confirm_password" /> - + This site is protected by ReCAPTCHA and the Google{" "} - + Privacy Policy - {" "} + {" "} and{" "} - + Terms of Service - {" "} + {" "} apply.
By clicking "Register", you agree to our{" "} - + End User Licensing Agreement (EULA) - +
- +
{error && ( diff --git a/src/button/Link.module.css b/src/button/Link.module.css new file mode 100644 index 000000000..6248bc407 --- /dev/null +++ b/src/button/Link.module.css @@ -0,0 +1,13 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +*/ + +.external { + /* By default links will be blue/purple (or whatever the user agent does), but + in our designs we generally want external links to be the same color as the + surrounding text */ + color: inherit; +} diff --git a/src/button/Link.tsx b/src/button/Link.tsx index 35c9af984..68c4dd136 100644 --- a/src/button/Link.tsx +++ b/src/button/Link.tsx @@ -15,10 +15,16 @@ import { import { Link as CpdLink } from "@vector-im/compound-web"; import { useHistory } from "react-router-dom"; import { createPath, LocationDescriptor, Path } from "history"; +import classNames from "classnames"; + +import { useLatest } from "../useLatest"; +import styles from "./Link.module.css"; export function useLink( to: LocationDescriptor, + state?: unknown, ): [Path, (e: MouseEvent) => void] { + const latestState = useLatest(state); const history = useHistory(); const path = useMemo( () => (typeof to === "string" ? to : createPath(to)), @@ -27,9 +33,9 @@ export function useLink( const onClick = useCallback( (e: MouseEvent) => { e.preventDefault(); - history.push(to); + history.push(to, latestState.current); }, - [history, to], + [history, to, latestState], ); return [path, onClick]; @@ -38,15 +44,37 @@ export function useLink( type Props = Omit< ComponentPropsWithoutRef, "href" | "onClick" -> & { to: LocationDescriptor }; +> & { to: LocationDescriptor; state?: unknown }; /** * A version of Compound's link component that integrates with our router setup. + * This is only for app-internal links. */ export const Link = forwardRef(function Link( - { to, ...props }, + { to, state, ...props }, ref, ) { - const [path, onClick] = useLink(to); + const [path, onClick] = useLink(to, state); return ; }); + +/** + * A link to an external web page, made to fit into blocks of text more subtly + * than the normal Compound link component. + */ +export const ExternalLink = forwardRef< + HTMLAnchorElement, + ComponentPropsWithoutRef<"a"> +>(function ExternalLink({ className, children, ...props }, ref) { + return ( + + {children} + + ); +}); diff --git a/src/home/CallList.module.css b/src/home/CallList.module.css index 8e988d034..21f6866f0 100644 --- a/src/home/CallList.module.css +++ b/src/home/CallList.module.css @@ -50,6 +50,12 @@ Please see LICENSE in the repository root for full details. margin-bottom: 0; } +.callName { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .facePile { margin-top: 8px; } diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index aa6db6f38..0446bb022 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -10,11 +10,11 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { Room } from "matrix-js-sdk/src/models/room"; import { FC } from "react"; +import { Text } from "@vector-im/compound-web"; import { Avatar, Size } from "../Avatar"; import styles from "./CallList.module.css"; import { getRelativeRoomUrl } from "../utils/matrix"; -import { Body } from "../typography/Typography"; import { GroupCallRoom } from "./useGroupCallRooms"; import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement"; @@ -65,9 +65,9 @@ const CallTile: FC = ({ name, avatarUrl, room }) => { >
- + {name} - +
diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index 34abe1130..db242414d 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -9,7 +9,7 @@ import { useState, useCallback, FormEvent, FormEventHandler, FC } from "react"; import { useHistory } from "react-router-dom"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { useTranslation } from "react-i18next"; -import { Heading } from "@vector-im/compound-web"; +import { Heading, Text } from "@vector-im/compound-web"; import { logger } from "matrix-js-sdk/src/logger"; import { Button } from "@vector-im/compound-web"; @@ -27,7 +27,6 @@ import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { CallList } from "./CallList"; import { UserMenuContainer } from "../UserMenuContainer"; import { JoinExistingCallModal } from "./JoinExistingCallModal"; -import { Caption } from "../typography/Typography"; import { Form } from "../form/Form"; import { AnalyticsNotice } from "../analytics/AnalyticsNotice"; import { E2eeType } from "../e2ee/e2eeType"; @@ -144,9 +143,9 @@ export const RegisteredView: FC = ({ client }) => { {optInAnalytics === null && ( - + - + )} {error && ( diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx index a1ea8514e..daafa9f81 100644 --- a/src/home/UnauthenticatedView.tsx +++ b/src/home/UnauthenticatedView.tsx @@ -9,7 +9,7 @@ import { FC, useCallback, useState, FormEventHandler } from "react"; import { useHistory } from "react-router-dom"; import { randomString } from "matrix-js-sdk/src/randomstring"; import { Trans, useTranslation } from "react-i18next"; -import { Button, Heading } from "@vector-im/compound-web"; +import { Button, Heading, Text } from "@vector-im/compound-web"; import { logger } from "matrix-js-sdk/src/logger"; import { useClient } from "../ClientContext"; @@ -25,7 +25,6 @@ import { import { useInteractiveRegistration } from "../auth/useInteractiveRegistration"; import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { useRecaptcha } from "../auth/useRecaptcha"; -import { Body, Caption, Link } from "../typography/Typography"; import { Form } from "../form/Form"; import styles from "./UnauthenticatedView.module.css"; import commonStyles from "./common.module.css"; @@ -34,6 +33,7 @@ import { AnalyticsNotice } from "../analytics/AnalyticsNotice"; import { Config } from "../config/Config"; import { E2eeType } from "../e2ee/e2eeType"; import { useOptInAnalytics } from "../settings/settings"; +import { ExternalLink, Link } from "../button/Link"; export const UnauthenticatedView: FC = () => { const { setClient } = useClient(); @@ -178,18 +178,18 @@ export const UnauthenticatedView: FC = () => { /> {optInAnalytics === null && ( - + - + )} - + By clicking "Go", you agree to our{" "} - + End User Licensing Agreement (EULA) - + - + {error && ( @@ -207,19 +207,19 @@ export const UnauthenticatedView: FC = () => {
- - + + {t("unauthenticated_view_login_button")} - - + + Not registered yet?{" "} - + Create an account - +
{onFinished && ( diff --git a/src/index.css b/src/index.css index b416dcca9..02f335f92 100644 --- a/src/index.css +++ b/src/index.css @@ -237,16 +237,6 @@ body[data-platform="desktop"] { line-height: var(--font-size-title); } - a { - color: var(--cpd-color-text-action-accent); - text-decoration: none; - } - - a:hover, - a:active { - opacity: 0.8; - } - hr { width: calc(100% - 24px); border: none; diff --git a/src/room/CallEndedView.tsx b/src/room/CallEndedView.tsx index 296bf2fb3..556dc6e54 100644 --- a/src/room/CallEndedView.tsx +++ b/src/room/CallEndedView.tsx @@ -9,12 +9,11 @@ import { FC, FormEventHandler, ReactNode, useCallback, useState } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Trans, useTranslation } from "react-i18next"; import { useHistory } from "react-router-dom"; -import { Button } from "@vector-im/compound-web"; +import { Button, Heading, Text } from "@vector-im/compound-web"; import styles from "./CallEndedView.module.css"; import feedbackStyle from "../input/FeedbackInput.module.css"; import { useProfile } from "../profile/useProfile"; -import { Body, Headline } from "../typography/Typography"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { FieldRow, InputField } from "../input/Input"; @@ -139,11 +138,11 @@ export const CallEndedView: FC = ({ return ( <>
- + You were disconnected from the call - +
{!confineToRoom && ( - + {t("return_home_button")} - + )} ); @@ -164,7 +163,7 @@ export const CallEndedView: FC = ({ return ( <>
- + {surveySubmitted ? t("call_ended_view.headline", { displayName, @@ -174,16 +173,16 @@ export const CallEndedView: FC = ({ }) + "\n" + t("call_ended_view.survey_prompt")} - + {(!surveySubmitted || confineToRoom) && PosthogAnalytics.instance.isEnabled() ? qualitySurveyDialog : createAccountDialog}
{!confineToRoom && ( - + {t("call_ended_view.not_now_button")} - + )} ); diff --git a/src/room/GroupCallLoader.tsx b/src/room/GroupCallLoader.tsx index 36bdcc4c0..f843f3f40 100644 --- a/src/room/GroupCallLoader.tsx +++ b/src/room/GroupCallLoader.tsx @@ -5,13 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { useCallback } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { useTranslation } from "react-i18next"; import { MatrixError } from "matrix-js-sdk/src/matrix"; -import { useHistory } from "react-router-dom"; -import { Heading, Link, Text } from "@vector-im/compound-web"; +import { Heading, Text } from "@vector-im/compound-web"; +import { Link } from "../button/Link"; import { useLoadGroupCall, GroupCallStatus, @@ -35,15 +34,6 @@ export function GroupCallLoader({ const { t } = useTranslation(); const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers); - const history = useHistory(); - const onHomeClick = useCallback( - (ev: React.MouseEvent) => { - ev.preventDefault(); - history.push("/"); - }, - [history], - ); - switch (groupCallState.kind) { case "loaded": case "waitForInvite": @@ -63,9 +53,7 @@ export function GroupCallLoader({ {t("group_call_loader.failed_text")} {/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have dupes of this flow, let's make a common component and put it here. */} - - {t("common.home")} - + {t("common.home")} ); } else if (groupCallState.error instanceof CallTerminatedMessage) { @@ -79,9 +67,7 @@ export function GroupCallLoader({ "{groupCallState.error.reason}" )} - - {t("common.home")} - + {t("common.home")} ); } else { diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index af97ce073..6892262b0 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -15,7 +15,7 @@ import { import { logger } from "matrix-js-sdk/src/logger"; import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; import { JoinRule } from "matrix-js-sdk/src/matrix"; -import { Heading, Link, Text } from "@vector-im/compound-web"; +import { Heading, Text } from "@vector-im/compound-web"; import { useTranslation } from "react-i18next"; import type { IWidgetApiRequest } from "matrix-widget-api"; @@ -40,6 +40,7 @@ import { useJoinRule } from "./useJoinRule"; import { InviteModal } from "./InviteModal"; import { useUrlParams } from "../UrlParams"; import { E2eeType } from "../e2ee/e2eeType"; +import { Link } from "../button/Link"; declare global { interface Window { @@ -281,14 +282,6 @@ export const GroupCallView: FC = ({ ); const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null; - const onHomeClick = useCallback( - (ev: React.MouseEvent) => { - ev.preventDefault(); - history.push("/"); - }, - [history], - ); - const { t } = useTranslation(); if (!isE2EESupportedBrowser() && e2eeSystem.kind !== E2eeType.NONE) { @@ -297,9 +290,7 @@ export const GroupCallView: FC = ({ {t("browser_media_e2ee_unsupported_heading")} {t("browser_media_e2ee_unsupported")} - - {t("common.home")} - + {t("common.home")} ); } diff --git a/src/room/RageshakeRequestModal.tsx b/src/room/RageshakeRequestModal.tsx index 03b0c1c0a..d22b0bea8 100644 --- a/src/room/RageshakeRequestModal.tsx +++ b/src/room/RageshakeRequestModal.tsx @@ -7,12 +7,11 @@ Please see LICENSE in the repository root for full details. import { FC, useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { Button } from "@vector-im/compound-web"; +import { Button, Text } from "@vector-im/compound-web"; import { Modal, Props as ModalProps } from "../Modal"; import { FieldRow, ErrorMessage } from "../input/Input"; import { useSubmitRageshake } from "../settings/submit-rageshake"; -import { Body } from "../typography/Typography"; interface Props extends Omit { rageshakeRequestId: string; @@ -40,7 +39,7 @@ export const RageshakeRequestModal: FC = ({ open={open} onDismiss={onDismiss} > - {t("rageshake_request_modal.body")} + {t("rageshake_request_modal.body")}