diff --git a/src/frontend/src/components/Avatar.tsx b/src/frontend/src/components/Avatar.tsx index 90fe0618..d007d293 100644 --- a/src/frontend/src/components/Avatar.tsx +++ b/src/frontend/src/components/Avatar.tsx @@ -26,6 +26,11 @@ const avatar = cva({ height: '100%', }, }, + notification: { + true: { + border: '2px solid white', + }, + }, }, defaultVariants: { context: 'list', @@ -37,14 +42,22 @@ export type AvatarProps = React.HTMLAttributes & { bgColor?: string } & RecipeVariantProps -export const Avatar = ({ name, bgColor, context, ...props }: AvatarProps) => { +export const Avatar = ({ + name, + bgColor, + context, + notification, + style, + ...props +}: AvatarProps) => { const initial = name?.trim()?.charAt(0) || '' return (
{initial} diff --git a/src/frontend/src/features/notifications/MainNotificationToast.tsx b/src/frontend/src/features/notifications/MainNotificationToast.tsx index 1a9274ff..d8dfd213 100644 --- a/src/frontend/src/features/notifications/MainNotificationToast.tsx +++ b/src/frontend/src/features/notifications/MainNotificationToast.tsx @@ -1,13 +1,15 @@ import { useEffect, useRef, useState } from 'react' import { useRoomContext } from '@livekit/components-react' import { Participant, RemoteParticipant, RoomEvent } from 'livekit-client' -import { ToastProvider, toastQueue } from './components/ToastProvider' +import { ChatMessage, isMobileBrowser } from '@livekit/components-core' +import { useTranslation } from 'react-i18next' +import { Div } from '@/primitives' import { NotificationType } from './NotificationType' import { NotificationDuration } from './NotificationDuration' import { decodeNotificationDataReceived } from './utils' -import { Div } from '@/primitives' -import { ChatMessage, isMobileBrowser } from '@livekit/components-core' import { useNotificationSound } from '@/features/notifications/hooks/useSoundNotification' +import { ToastProvider, toastQueue } from './components/ToastProvider' +import { WaitingParticipantNotification } from './components/WaitingParticipantNotification' import { EMOJIS, Reaction, @@ -16,7 +18,6 @@ import { ANIMATION_DURATION, ReactionPortals, } from '@/features/rooms/livekit/components/ReactionPortal' -import { useTranslation } from 'react-i18next' export const MainNotificationToast = () => { const room = useRoomContext() @@ -195,6 +196,7 @@ export const MainNotificationToast = () => { return (
+
) diff --git a/src/frontend/src/features/notifications/components/Toast.tsx b/src/frontend/src/features/notifications/components/Toast.tsx index 59b5c1f3..4f146102 100644 --- a/src/frontend/src/features/notifications/components/Toast.tsx +++ b/src/frontend/src/features/notifications/components/Toast.tsx @@ -22,7 +22,7 @@ export const StyledToastContainer = styled('div', { }, }) -const StyledToast = styled('div', { +export const StyledToast = styled('div', { base: { display: 'flex', justifyContent: 'space-between', diff --git a/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx b/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx new file mode 100644 index 00000000..fc9fd3f0 --- /dev/null +++ b/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx @@ -0,0 +1,102 @@ +import { useListWaitingParticipants } from '@/features/rooms/api/listWaitingParticipants' +import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData' +import { StyledToastContainer } from './Toast' +import { HStack } from '@/styled-system/jsx' +import { Avatar } from '@/components/Avatar' +import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel' +import { Button, Text } from '@/primitives' +import { css } from '@/styled-system/css' +import { RiInfinityLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' + +export const WaitingParticipantNotification = () => { + const data = useRoomData() + const { t } = useTranslation('notifications', { + keyPrefix: 'waitingParticipants', + }) + const { isParticipantsOpen, toggleParticipants } = useSidePanel() + const { data: readOnlyData } = useListWaitingParticipants(data!.id, { + retry: false, + enabled: false, + }) + const participants = readOnlyData?.participants || [] + if (!participants.length) return + return ( + + 1 ? t('several') : t('one')} + aria-modal={false} + > + + + {participants.length > 1 && ( + + )} + {participants.length > 2 && ( + + {participants.length < 102 ? ( +

+{participants.length - 2}

+ ) : ( + + )} +
+ )} +
+ + {participants.length > 1 ? t('several') : t('one')} + + {!isParticipantsOpen && ( + + )} +
+
+ ) +} diff --git a/src/frontend/src/locales/de/notifications.json b/src/frontend/src/locales/de/notifications.json index 587e7364..aa8c1508 100644 --- a/src/frontend/src/locales/de/notifications.json +++ b/src/frontend/src/locales/de/notifications.json @@ -15,5 +15,10 @@ }, "reaction": { "description": "" + }, + "waitingParticipants": { + "one": "", + "several": "", + "open": "" } } diff --git a/src/frontend/src/locales/en/notifications.json b/src/frontend/src/locales/en/notifications.json index d81d6a2e..44f45cdf 100644 --- a/src/frontend/src/locales/en/notifications.json +++ b/src/frontend/src/locales/en/notifications.json @@ -15,5 +15,10 @@ }, "reaction": { "description": "{{name}} reacted with {{emoji}}" + }, + "waitingParticipants": { + "one": "One person wants to join this call.", + "several": "Several people want to join this call.", + "open": "Open" } } diff --git a/src/frontend/src/locales/fr/notifications.json b/src/frontend/src/locales/fr/notifications.json index 47ae5a83..af88f072 100644 --- a/src/frontend/src/locales/fr/notifications.json +++ b/src/frontend/src/locales/fr/notifications.json @@ -15,5 +15,10 @@ }, "reaction": { "description": "{{name}} a reagi avec {{emoji}}" + }, + "waitingParticipants": { + "one": "Une personne souhaite participer à cet appel.", + "several": "Plusieurs personnes souhaitent participer à cet appel.", + "open": "Afficher" } }