diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index f2605959..9864bcf4 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -3,7 +3,6 @@ "{{count}} stars|other": "{{count}} stars", "{{displayName}} is presenting": "{{displayName}} is presenting", "{{displayName}}, your call has ended.": "{{displayName}}, your call has ended.", - "{{names}}, {{name}}": "{{names}}, {{name}}", "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.", "<0>Already have an account?<1><0>Log in Or <2>Access as a guest": "<0>Already have an account?<1><0>Log in Or <2>Access as a guest", "<0>Create an account Or <2>Access as a guest": "<0>Create an account Or <2>Access as a guest", diff --git a/src/Avatar.module.css b/src/Avatar.module.css deleted file mode 100644 index accff6ae..00000000 --- a/src/Avatar.module.css +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -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. -*/ - -.avatar { - position: relative; - color: var(--stopgap-color-on-solid-accent); - display: flex; - align-items: center; - justify-content: center; - pointer-events: none; - font-weight: 600; - overflow: hidden; - flex-shrink: 0; -} - -.avatar img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.avatar svg * { - fill: var(--cpd-color-text-primary); -} - -.avatar span { - padding-top: 1px; -} - -.xs { - width: 22px; - height: 22px; - border-radius: 22px; - font-size: 14px; -} - -.sm { - width: 32px; - height: 32px; - border-radius: 32px; - font-size: 15px; -} - -.md { - width: 36px; - height: 36px; - border-radius: 36px; - font-size: 20px; -} - -.lg { - width: 42px; - height: 42px; - border-radius: 42px; - font-size: 24px; -} - -.xl { - width: 90px; - height: 90px; - border-radius: 90px; - font-size: 48px; -} diff --git a/src/Avatar.tsx b/src/Avatar.tsx index c11d71d4..e23bd909 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -14,23 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useMemo, CSSProperties, HTMLAttributes, FC } from "react"; -import classNames from "classnames"; +import { useMemo, FC } from "react"; +import { Avatar as CompoundAvatar } from "@vector-im/compound-web"; import { getAvatarUrl } from "./matrix-utils"; import { useClient } from "./ClientContext"; -import styles from "./Avatar.module.css"; - -const backgroundColors = [ - "#5C56F5", - "#03B381", - "#368BD6", - "#AC3BA8", - "#E64F7A", - "#FF812D", - "#2DC2C5", - "#74D12C", -]; export enum Size { XS = "xs", @@ -48,50 +36,28 @@ export const sizes = new Map([ [Size.XL, 90], ]); -function hashStringToArrIndex(str: string, arrLength: number) { - let sum = 0; - - for (let i = 0; i < str.length; i++) { - sum += str.charCodeAt(i); - } - - return sum % arrLength; -} - -interface Props extends HTMLAttributes { - bgKey?: string; +interface Props { + id: string; + name: string; + className?: string; src?: string; size?: Size | number; - className?: string; - style?: CSSProperties; - fallback: string; } export const Avatar: FC = ({ - bgKey, - src, - fallback, - size = Size.MD, className, - style = {}, - ...rest + id, + name, + src, + size = Size.MD, }) => { const { client } = useClient(); - const [sizeClass, sizePx, sizeStyle] = useMemo( + const sizePx = useMemo( () => Object.values(Size).includes(size as Size) - ? [styles[size as string], sizes.get(size as Size), {}] - : [ - null, - size as number, - { - width: size, - height: size, - borderRadius: size, - fontSize: Math.round((size as number) / 2), - }, - ], + ? sizes.get(size as Size) + : (size as number), [size] ); @@ -100,28 +66,13 @@ export const Avatar: FC = ({ return src.startsWith("mxc://") ? getAvatarUrl(client, src, sizePx) : src; }, [client, src, sizePx]); - const backgroundColor = useMemo(() => { - const index = hashStringToArrIndex( - bgKey || fallback || src || "", - backgroundColors.length - ); - return backgroundColors[index]; - }, [bgKey, src, fallback]); - - /* eslint-disable jsx-a11y/alt-text */ return ( -
- {resolvedSrc ? ( - - ) : typeof fallback === "string" ? ( - {fallback} - ) : ( - fallback - )} -
+ ); }; diff --git a/src/Facepile.module.css b/src/Facepile.module.css deleted file mode 100644 index 0c911658..00000000 --- a/src/Facepile.module.css +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -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. -*/ - -.facepile { - width: 100%; - position: relative; -} - -.facepile.xs { - height: 24px; -} - -.facepile.sm { - height: 32px; -} - -.facepile.md { - height: 36px; -} - -.facepile .avatar { - position: absolute; - top: 0; - border: 1px solid var(--cpd-color-bg-canvas-default); -} - -.facepile.md .avatar { - border-width: 2px; -} diff --git a/src/Facepile.tsx b/src/Facepile.tsx deleted file mode 100644 index 0c9ec239..00000000 --- a/src/Facepile.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -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 { HTMLAttributes, useMemo } from "react"; -import classNames from "classnames"; -import { MatrixClient } from "matrix-js-sdk/src/client"; -import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { useTranslation } from "react-i18next"; - -import styles from "./Facepile.module.css"; -import { Avatar, Size, sizes } from "./Avatar"; - -const overlapMap: Partial> = { - [Size.XS]: 2, - [Size.SM]: 4, - [Size.MD]: 8, -}; - -interface Props extends HTMLAttributes { - className: string; - client: MatrixClient; - members: RoomMember[]; - max?: number; - size?: Size; -} - -export function Facepile({ - className, - client, - members, - max = 3, - size = Size.XS, - ...rest -}: Props) { - const { t } = useTranslation(); - - const _size = sizes.get(size)!; - const _overlap = overlapMap[size]!; - - const title = useMemo(() => { - return members.reduce( - (prev, curr) => - prev === null - ? curr.name - : t("{{names}}, {{name}}", { names: prev, name: curr.name }), - null - ) as string; - }, [members, t]); - - return ( -
- {members.slice(0, max).map((member, i) => { - const avatarUrl = member.getMxcAvatarUrl(); - return ( - - ); - })} - {members.length > max && ( - - )} -
- ); -} diff --git a/src/UserMenu.module.css b/src/UserMenu.module.css index d1db1071..575b71b9 100644 --- a/src/UserMenu.module.css +++ b/src/UserMenu.module.css @@ -24,17 +24,3 @@ limitations under the License. .userButton svg * { fill: var(--cpd-color-icon-primary); } - -.avatar { - width: 24px; - height: 24px; - font-size: var(--font-size-caption); -} - -@media (min-width: 800px) { - .avatar { - width: 32px; - height: 32px; - font-size: var(--font-size-body); - } -} diff --git a/src/UserMenu.tsx b/src/UserMenu.tsx index 9df3309d..515e71f0 100644 --- a/src/UserMenu.tsx +++ b/src/UserMenu.tsx @@ -35,6 +35,7 @@ interface UserMenuProps { preventNavigation: boolean; isAuthenticated: boolean; isPasswordlessUser: boolean; + userId: string; displayName: string; avatarUrl?: string; onAction: (value: string) => void; @@ -44,6 +45,7 @@ export function UserMenu({ preventNavigation, isAuthenticated, isPasswordlessUser, + userId, displayName, avatarUrl, onAction, @@ -109,10 +111,10 @@ export function UserMenu({ > {isAuthenticated && (!isPasswordlessUser || avatarUrl) ? ( ) : ( diff --git a/src/UserMenuContainer.tsx b/src/UserMenuContainer.tsx index 6a83133e..a03e5b5a 100644 --- a/src/UserMenuContainer.tsx +++ b/src/UserMenuContainer.tsx @@ -67,6 +67,7 @@ export function UserMenuContainer({ preventNavigation = false }: Props) { isPasswordlessUser={passwordlessUser} avatarUrl={avatarUrl} onAction={onAction} + userId={client?.getUserId() ?? ""} displayName={displayName || (userName ? userName.replace("@", "") : "")} /> {modalState.isOpen && client && ( diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 1e36360e..a82e4c13 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -19,7 +19,6 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { CopyButton } from "../button"; -import { Facepile } from "../Facepile"; import { Avatar, Size } from "../Avatar"; import styles from "./CallList.module.css"; import { getRoomUrl } from "../matrix-utils"; @@ -30,9 +29,8 @@ import { useRoomSharedKey } from "../e2ee/sharedKeyManagement"; interface CallListProps { rooms: GroupCallRoom[]; client: MatrixClient; - disableFacepile?: boolean; } -export function CallList({ rooms, client, disableFacepile }: CallListProps) { +export function CallList({ rooms, client }: CallListProps) { return ( <>
@@ -44,7 +42,6 @@ export function CallList({ rooms, client, disableFacepile }: CallListProps) { avatarUrl={avatarUrl} roomId={room.roomId} participants={participants} - disableFacepile={disableFacepile} /> ))} {rooms.length > 3 && ( @@ -63,39 +60,18 @@ interface CallTileProps { roomId: string; participants: RoomMember[]; client: MatrixClient; - disableFacepile?: boolean; } -function CallTile({ - name, - avatarUrl, - roomId, - participants, - client, - disableFacepile, -}: CallTileProps) { +function CallTile({ name, avatarUrl, roomId }: CallTileProps) { const roomSharedKey = useRoomSharedKey(roomId); return (
- +
{name} - {participants && !disableFacepile && ( - - )}
diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index 42aba9e8..0f8cc93c 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -170,7 +170,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { {t("Your recent calls")} - + )} diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx index 0218258e..8a069718 100644 --- a/src/input/AvatarInputField.tsx +++ b/src/input/AvatarInputField.tsx @@ -35,13 +35,23 @@ interface Props extends AllHTMLAttributes { id: string; label: string; avatarUrl: string | undefined; + userId: string; displayName: string; onRemoveAvatar: () => void; } export const AvatarInputField = forwardRef( ( - { id, label, className, avatarUrl, displayName, onRemoveAvatar, ...rest }, + { + id, + label, + className, + avatarUrl, + userId, + displayName, + onRemoveAvatar, + ...rest + }, ref ) => { const { t } = useTranslation(); @@ -80,9 +90,10 @@ export const AvatarInputField = forwardRef(
{ return { + userId: client.getUserId()!, displayName: displayName!, avatarUrl: avatarUrl!, roomId: groupCall.room.roomId, roomName: groupCall.room.name, roomAlias: groupCall.room.getCanonicalAlias(), }; - }, [displayName, avatarUrl, groupCall]); + }, [client, displayName, avatarUrl, groupCall]); const deviceContext = useMediaDevices(); const latestDevices = useRef(); diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 044a3221..c93f6b65 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -35,6 +35,7 @@ import { useMediaDevices } from "../livekit/MediaDevicesContext"; import { MuteStates } from "./MuteStates"; export type MatrixInfo = { + userId: string; displayName: string; avatarUrl: string; roomId: string; @@ -129,9 +130,10 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { {!muteStates.video.enabled && (
)} diff --git a/src/settings/ProfileSettingsTab.tsx b/src/settings/ProfileSettingsTab.tsx index e6a59634..4286a960 100644 --- a/src/settings/ProfileSettingsTab.tsx +++ b/src/settings/ProfileSettingsTab.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useCallback, useEffect, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { useTranslation } from "react-i18next"; @@ -29,6 +29,7 @@ interface Props { export function ProfileSettingsTab({ client }: Props) { const { t } = useTranslation(); const { error, displayName, avatarUrl, saveProfile } = useProfile(client); + const userId = useMemo(() => client.getUserId(), [client]); const formRef = useRef(null); @@ -77,12 +78,13 @@ export function ProfileSettingsTab({ client }: Props) { return (
- {displayName && ( + {userId && displayName && ( diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx index f15052e0..ceaf7e1e 100644 --- a/src/video-grid/VideoTile.tsx +++ b/src/video-grid/VideoTile.tsx @@ -169,9 +169,10 @@ export const VideoTile = forwardRef(