From b54445739a762c40edb8ceaa55f2a36a92b7de4d Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 5 Aug 2025 14:47:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20telephony=20info=20?= =?UTF-8?q?to=20meeting=20dialog=20with=20layout=20stability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add telephony information display to the later meeting dialog while preserving existing layout when telephony is disabled. Prevent layout shift on modal close by collapsing all modal content immediately when room becomes undefined. Critical enhancement for users creating meeting links to have complete connection information available. --- .../home/components/LaterMeetingDialog.tsx | 268 +++++++++++++----- .../src/features/home/routes/Home.tsx | 9 +- src/frontend/src/locales/de/home.json | 15 +- src/frontend/src/locales/en/home.json | 15 +- src/frontend/src/locales/fr/home.json | 15 +- src/frontend/src/locales/nl/home.json | 15 +- 6 files changed, 249 insertions(+), 88 deletions(-) diff --git a/src/frontend/src/features/home/components/LaterMeetingDialog.tsx b/src/frontend/src/features/home/components/LaterMeetingDialog.tsx index e61b579b..74d3ca50 100644 --- a/src/frontend/src/features/home/components/LaterMeetingDialog.tsx +++ b/src/frontend/src/features/home/components/LaterMeetingDialog.tsx @@ -1,20 +1,26 @@ -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { getRouteUrl } from '@/navigation/getRouteUrl' -import { Button, Dialog, type DialogProps, P, Text } from '@/primitives' +import { Bold, Button, Dialog, type DialogProps, P, Text } from '@/primitives' import { HStack } from '@/styled-system/jsx' import { RiCheckLine, RiFileCopyLine, RiSpam2Fill } from '@remixicon/react' import { css } from '@/styled-system/css' +import { ApiRoom } from '@/features/rooms/api/ApiRoom' +import { useTelephony } from '@/features/rooms/livekit/hooks/useTelephony' +import { formatPinCode } from '@/features/rooms/utils/telephony' // fixme - duplication with the InviteDialog export const LaterMeetingDialog = ({ - roomId, + room, ...dialogProps -}: { roomId: string } & Omit) => { - const { t } = useTranslation('home') - const roomUrl = getRouteUrl('room', roomId) +}: { room: null | ApiRoom } & Omit) => { + const { t } = useTranslation('home', { keyPrefix: 'laterMeetingDialog' }) + + const roomUrl = room && getRouteUrl('room', room?.slug) + const telephony = useTelephony() const [isCopied, setIsCopied] = useState(false) + const [isRoomUrlCopied, setIsRoomUrlCopied] = useState(false) useEffect(() => { if (isCopied) { @@ -25,76 +31,194 @@ export const LaterMeetingDialog = ({ const [isHovered, setIsHovered] = useState(false) + useEffect(() => { + if (isRoomUrlCopied) { + const timeout = setTimeout(() => setIsRoomUrlCopied(false), 3000) + return () => clearTimeout(timeout) + } + }, [isRoomUrlCopied]) + + const isTelephonyReadyForUse = useMemo(() => { + return telephony?.enabled && room?.pin_code + }, [telephony?.enabled, room?.pin_code]) + + const clipboardContent = useMemo(() => { + if (isTelephonyReadyForUse) { + return [ + t('clipboard.url', { roomUrl }), + t('clipboard.numberAndPin', { + phoneNumber: telephony?.internationalPhoneNumber, + pinCode: formatPinCode(room?.pin_code), + }), + ].join('\n') + } + return roomUrl + }, [ + isTelephonyReadyForUse, + roomUrl, + telephony?.internationalPhoneNumber, + room?.pin_code, + t, + ]) + return ( - -

{t('laterMeetingDialog.description')}

- + )} - )} - - )} - - -
- -
- - {t('laterMeetingDialog.permissions')} - -
+
+ + {t('phone.call')} ({telephony?.country}){' '} + {telephony?.internationalPhoneNumber} + + + {t('phone.pinCode')}{' '} + {formatPinCode(room?.pin_code)} + +
+ {clipboardContent && ( + + )} + + ) : ( + + )} + +
+ +
+ + {t('permissions')} + +
+ + )}
) } diff --git a/src/frontend/src/features/home/routes/Home.tsx b/src/frontend/src/features/home/routes/Home.tsx index ecfd243c..aa18afda 100644 --- a/src/frontend/src/features/home/routes/Home.tsx +++ b/src/frontend/src/features/home/routes/Home.tsx @@ -18,6 +18,7 @@ import { menuRecipe } from '@/primitives/menuRecipe.ts' import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices' import { useConfig } from '@/api/useConfig' import { LoginButton } from '@/components/LoginButton' +import { ApiRoom } from '@/features/rooms/api/ApiRoom' const Columns = ({ children }: { children?: ReactNode }) => { return ( @@ -153,7 +154,7 @@ export const Home = () => { } = usePersistentUserChoices() const { mutateAsync: createRoom } = useCreateRoom() - const [laterRoomId, setLaterRoomId] = useState(null) + const [laterRoom, setLaterRoom] = useState(null) const { data } = useConfig() @@ -202,7 +203,7 @@ export const Home = () => { onAction={() => { const slug = generateRoomId() createRoom({ slug, username }).then((data) => - setLaterRoomId(data.slug) + setLaterRoom(data) ) }} data-attr="create-option-later" @@ -251,8 +252,8 @@ export const Home = () => { setLaterRoomId(null)} + room={laterRoom} + onOpenChange={() => setLaterRoom(null)} /> diff --git a/src/frontend/src/locales/de/home.json b/src/frontend/src/locales/de/home.json index e8b02889..f991d20b 100644 --- a/src/frontend/src/locales/de/home.json +++ b/src/frontend/src/locales/de/home.json @@ -20,9 +20,18 @@ "laterMeetingDialog": { "heading": "Ihre Zugangsdaten", "description": "Teilen Sie diese Informationen mit den Gästen. Sie können dem Meeting beitreten, ohne sich anmelden zu müssen. Dieses Meeting ist dauerhaft und kann wiederverwendet werden.", - "copy": "Meeting-Link kopieren", - "copied": "Link in die Zwischenablage kopiert", - "permissions": "Personen mit diesem Link benötigen keine Genehmigung, um diesem Meeting beizutreten." + "permissions": "Personen mit diesem Link benötigen keine Genehmigung, um diesem Meeting beizutreten.", + "copy": "Informationen kopieren", + "copied": "Informationen kopiert", + "copyUrl": "Meeting-Link kopieren", + "phone": { + "call": "Rufen Sie an:", + "pinCode": "Code:" + }, + "clipboard": { + "url": "Um an der Videokonferenz teilzunehmen, klicken Sie auf diesen Link: {{roomUrl}}", + "numberAndPin": "Um telefonisch teilzunehmen, wählen Sie {{phoneNumber}} und geben Sie diesen Code ein: {{pinCode}}" + } }, "introSlider": { "previous": { diff --git a/src/frontend/src/locales/en/home.json b/src/frontend/src/locales/en/home.json index ae34cbfe..dad09df8 100644 --- a/src/frontend/src/locales/en/home.json +++ b/src/frontend/src/locales/en/home.json @@ -20,9 +20,18 @@ "laterMeetingDialog": { "heading": "Your connection details", "description": "Share this information with the guests. They will be able to join the meeting without needing to sign in. This meeting is permanent and can be reused.", - "copy": "Copy the meeting link", - "copied": "Link copied to clipboard", - "permissions": "People with this link do not need your permission to join this meeting." + "permissions": "People with this link do not need your permission to join this meeting.", + "copy": "Copy information", + "copied": "Information copied to clipboard", + "copyUrl": "Copy the meeting link", + "phone": { + "call": "Call:", + "pinCode": "Code:" + }, + "clipboard": { + "url": "To join the video conference, click on this link: {{roomUrl}}", + "numberAndPin": "To join by phone, dial {{phoneNumber}} and enter this code: {{pinCode}}" + } }, "introSlider": { "previous": { diff --git a/src/frontend/src/locales/fr/home.json b/src/frontend/src/locales/fr/home.json index c3beca8e..97cacaf2 100644 --- a/src/frontend/src/locales/fr/home.json +++ b/src/frontend/src/locales/fr/home.json @@ -19,10 +19,19 @@ }, "laterMeetingDialog": { "heading": "Vos informations de connexion", - "copy": "Copier le lien de la réunion", - "copied": "Lien copié dans le presse-papiers", - "permissions": "Les personnes disposant de ce lien n'ont pas besoin de votre autorisation pour rejoindre cette réunion." "description": "Partagez ces informations avec les invités. Ils pourront rejoindre la réunion sans avoir besoin de se connecter. Cette réunion est permanente et peut être réutilisée.", + "permissions": "Les personnes disposant de ce lien n'ont pas besoin de votre autorisation pour rejoindre cette réunion.", + "copy": "Copier les informations", + "copied": "Copiées dans le presse-papiers", + "copyUrl": "Copier le lien de la réunion", + "phone": { + "call": "Appelez le :", + "pinCode": "Code :" + }, + "clipboard": { + "url": "Pour participer à la visioconférence, cliquez sur ce lien : {{roomUrl}}", + "numberAndPin": "Pour participer par téléphone, composez le {{phoneNumber}} et saisissez ce code : {{pinCode}}" + } }, "introSlider": { "previous": { diff --git a/src/frontend/src/locales/nl/home.json b/src/frontend/src/locales/nl/home.json index f9f733f9..b072067d 100644 --- a/src/frontend/src/locales/nl/home.json +++ b/src/frontend/src/locales/nl/home.json @@ -20,9 +20,18 @@ "laterMeetingDialog": { "heading": "Uw verbindingsgegevens", "description": "Deel deze informatie met de genodigden. Zij kunnen deelnemen aan de vergadering zonder zich aan te melden. Deze vergadering is permanent en kan hergebruikt worden.", - "copy": "Kopieer de vergaderlink", - "copied": "Link gekopieerd naar klembord", - "permissions": "Mensen met deze link hebben uw toestemming niet nodig om deel te nemen aan deze vergadering." + "permissions": "Mensen met deze link hebben uw toestemming niet nodig om deel te nemen aan deze vergadering.", + "copy": "Informatie kopiëren", + "copied": "Informatie gekopieerd", + "copyUrl": "Kopieer de vergaderlink", + "phone": { + "call": "Bel:", + "pinCode": "Code:" + }, + "clipboard": { + "url": "Klik op deze link om deel te nemen aan de videoconferentie: {{roomUrl}}", + "numberAndPin": "Bel {{phoneNumber}} en voer deze code in om telefonisch deel te nemen: {{pinCode}}" + } }, "introSlider": { "previous": {