✨(frontend) add meeting info side panel with copy functionality
Implement new side panel that provides easy access to meeting information with copy/paste capabilities. Introduces xs text size to accommodate longer URLs. Panel includes space for future documentation links about meeting functionality. Addresses direct user requests for simpler sharing of meeting details.
This commit is contained in:
committed by
aleb_the_flash
parent
d1a17d2aa9
commit
a5fb3b910f
@@ -397,6 +397,12 @@ const config: Config = {
|
||||
lineHeight: '1.25rem',
|
||||
},
|
||||
},
|
||||
xs: {
|
||||
value: {
|
||||
fontSize: '0.825rem',
|
||||
lineHeight: '1.15rem',
|
||||
},
|
||||
},
|
||||
badge: {
|
||||
value: {
|
||||
fontSize: '0.75rem',
|
||||
|
||||
75
src/frontend/src/features/rooms/livekit/components/Info.tsx
Normal file
75
src/frontend/src/features/rooms/livekit/components/Info.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { VStack } from '@/styled-system/jsx'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { RiCheckLine, RiFileCopyLine } from '@remixicon/react'
|
||||
import { Button, Div, Text } from '@/primitives'
|
||||
import { getRouteUrl } from '@/navigation/getRouteUrl'
|
||||
import { useRoomData } from '../hooks/useRoomData'
|
||||
|
||||
export const Info = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'info' })
|
||||
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timeout = setTimeout(() => setIsCopied(false), 3000)
|
||||
return () => clearTimeout(timeout)
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
const data = useRoomData()
|
||||
const roomUrl = getRouteUrl('room', data?.slug)
|
||||
|
||||
return (
|
||||
<Div
|
||||
display="flex"
|
||||
overflowY="scroll"
|
||||
padding="0 1.5rem"
|
||||
flexGrow={1}
|
||||
flexDirection="column"
|
||||
alignItems="start"
|
||||
>
|
||||
<VStack alignItems="start">
|
||||
<Text
|
||||
as="h3"
|
||||
className={css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
})}
|
||||
>
|
||||
{t('roomInformation.title')}
|
||||
</Text>
|
||||
<Text as="p" variant="xsNote" wrap="pretty">
|
||||
{roomUrl}
|
||||
</Text>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={isCopied ? 'success' : 'tertiaryText'}
|
||||
aria-label={t('roomInformation.button.ariaLabel')}
|
||||
onPress={() => {
|
||||
navigator.clipboard.writeText(roomUrl)
|
||||
setIsCopied(true)
|
||||
}}
|
||||
data-attr="copy-info-sidepannel"
|
||||
style={{
|
||||
marginLeft: '-8px',
|
||||
}}
|
||||
>
|
||||
{isCopied ? (
|
||||
<>
|
||||
<RiCheckLine size={24} style={{ marginRight: '6px' }} />
|
||||
{t('roomInformation.button.copied')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RiFileCopyLine size={24} style={{ marginRight: '6px' }} />
|
||||
{t('roomInformation.button.copy')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</VStack>
|
||||
</Div>
|
||||
)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { Chat } from '../prefabs/Chat'
|
||||
import { Effects } from './effects/Effects'
|
||||
import { Admin } from './Admin'
|
||||
import { Tools } from './Tools'
|
||||
import { Info } from './Info'
|
||||
|
||||
type StyledSidePanelProps = {
|
||||
title: string
|
||||
@@ -133,6 +134,7 @@ export const SidePanel = () => {
|
||||
isSidePanelOpen,
|
||||
isToolsOpen,
|
||||
isAdminOpen,
|
||||
isInfoOpen,
|
||||
isSubPanelOpen,
|
||||
activeSubPanelId,
|
||||
} = useSidePanel()
|
||||
@@ -167,6 +169,9 @@ export const SidePanel = () => {
|
||||
<Panel isOpen={isAdminOpen}>
|
||||
<Admin />
|
||||
</Panel>
|
||||
<Panel isOpen={isInfoOpen}>
|
||||
<Info />
|
||||
</Panel>
|
||||
</StyledSidePanel>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiInformationLine } from '@remixicon/react'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { ToggleButton } from '@/primitives'
|
||||
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const InfoToggle = ({
|
||||
onPress,
|
||||
...props
|
||||
}: Partial<ToggleButtonProps>) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.info' })
|
||||
|
||||
const { isInfoOpen, toggleInfo } = useSidePanel()
|
||||
const tooltipLabel = isInfoOpen ? 'open' : 'closed'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
})}
|
||||
>
|
||||
<ToggleButton
|
||||
square
|
||||
variant="primaryTextDark"
|
||||
aria-label={t(tooltipLabel)}
|
||||
tooltip={t(tooltipLabel)}
|
||||
isSelected={isInfoOpen}
|
||||
onPress={(e) => {
|
||||
toggleInfo()
|
||||
onPress?.(e)
|
||||
}}
|
||||
data-attr={`controls-info-${tooltipLabel}`}
|
||||
{...props}
|
||||
>
|
||||
<RiInformationLine />
|
||||
</ToggleButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export enum PanelId {
|
||||
CHAT = 'chat',
|
||||
TOOLS = 'tools',
|
||||
ADMIN = 'admin',
|
||||
INFO = 'info',
|
||||
}
|
||||
|
||||
export enum SubPanelId {
|
||||
@@ -24,6 +25,7 @@ export const useSidePanel = () => {
|
||||
const isChatOpen = activePanelId == PanelId.CHAT
|
||||
const isToolsOpen = activePanelId == PanelId.TOOLS
|
||||
const isAdminOpen = activePanelId == PanelId.ADMIN
|
||||
const isInfoOpen = activePanelId == PanelId.INFO
|
||||
const isTranscriptOpen = activeSubPanelId == SubPanelId.TRANSCRIPT
|
||||
const isScreenRecordingOpen = activeSubPanelId == SubPanelId.SCREEN_RECORDING
|
||||
const isSidePanelOpen = !!activePanelId
|
||||
@@ -54,6 +56,11 @@ export const useSidePanel = () => {
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleInfo = () => {
|
||||
layoutStore.activePanelId = isInfoOpen ? null : PanelId.INFO
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const openTranscript = () => {
|
||||
layoutStore.activeSubPanelId = SubPanelId.TRANSCRIPT
|
||||
layoutStore.activePanelId = PanelId.TOOLS
|
||||
@@ -72,6 +79,7 @@ export const useSidePanel = () => {
|
||||
toggleEffects,
|
||||
toggleTools,
|
||||
toggleAdmin,
|
||||
toggleInfo,
|
||||
openTranscript,
|
||||
openScreenRecording,
|
||||
isSubPanelOpen,
|
||||
@@ -81,6 +89,7 @@ export const useSidePanel = () => {
|
||||
isSidePanelOpen,
|
||||
isToolsOpen,
|
||||
isAdminOpen,
|
||||
isInfoOpen,
|
||||
isTranscriptOpen,
|
||||
isScreenRecordingOpen,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { css } from '@/styled-system/css'
|
||||
import { ChatToggle } from '../../components/controls/ChatToggle'
|
||||
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
|
||||
import { ToolsToggle } from '../../components/controls/ToolsToggle'
|
||||
import { InfoToggle } from '../../components/controls/InfoToggle'
|
||||
import { AdminToggle } from '../../components/AdminToggle'
|
||||
import { useSize } from '../../hooks/useResizeObserver'
|
||||
import { useState, RefObject } from 'react'
|
||||
@@ -18,6 +19,7 @@ const NavigationControls = ({
|
||||
tooltipType = 'instant',
|
||||
}: Partial<ToggleButtonProps>) => (
|
||||
<>
|
||||
<InfoToggle onPress={onPress} tooltipType={tooltipType} />
|
||||
<ChatToggle onPress={onPress} tooltipType={tooltipType} />
|
||||
<ParticipantsToggle onPress={onPress} tooltipType={tooltipType} />
|
||||
<ToolsToggle onPress={onPress} tooltipType={tooltipType} />
|
||||
|
||||
@@ -91,6 +91,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"open": "",
|
||||
"closed": ""
|
||||
},
|
||||
"hand": {
|
||||
"raise": "",
|
||||
"lower": ""
|
||||
@@ -171,7 +175,8 @@
|
||||
"transcript": "",
|
||||
"screenRecording": "",
|
||||
"admin": "",
|
||||
"tools": ""
|
||||
"tools": "",
|
||||
"info": ""
|
||||
},
|
||||
"content": {
|
||||
"participants": "",
|
||||
@@ -180,7 +185,8 @@
|
||||
"transcript": "",
|
||||
"screenRecording": "",
|
||||
"admin": "",
|
||||
"tools": ""
|
||||
"tools": "",
|
||||
"info": ""
|
||||
},
|
||||
"closeButton": ""
|
||||
},
|
||||
@@ -201,6 +207,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"roomInformation": {
|
||||
"title": "",
|
||||
"button": {
|
||||
"ariaLabel": "",
|
||||
"copy": "",
|
||||
"copied": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"transcript": {
|
||||
"start": {
|
||||
"heading": "",
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"open": "Hide information",
|
||||
"closed": "Show information"
|
||||
},
|
||||
"hand": {
|
||||
"raise": "Raise hand",
|
||||
"lower": "Lower hand"
|
||||
@@ -170,7 +174,8 @@
|
||||
"transcript": "Transcription",
|
||||
"screenRecording": "Recording",
|
||||
"admin": "Admin settings",
|
||||
"tools": "More tools"
|
||||
"tools": "More tools",
|
||||
"info": "Meeting information"
|
||||
},
|
||||
"content": {
|
||||
"participants": "participants",
|
||||
@@ -179,7 +184,8 @@
|
||||
"transcript": "transcription",
|
||||
"screenRecording": "recording",
|
||||
"admin": "admin settings",
|
||||
"tools": "more tools"
|
||||
"tools": "more tools",
|
||||
"info": "meeting information"
|
||||
},
|
||||
"closeButton": "Hide {{content}}"
|
||||
},
|
||||
@@ -200,6 +206,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"roomInformation": {
|
||||
"title": "Connection Information",
|
||||
"button": {
|
||||
"ariaLabel": "Copy your meeting address",
|
||||
"copy": "Copy address",
|
||||
"copied": "Address copied"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transcript": {
|
||||
"start": {
|
||||
"heading": "Transcribe this call",
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"open": "Masquer les informations",
|
||||
"closed": "Afficher les informations"
|
||||
},
|
||||
"hand": {
|
||||
"raise": "Lever la main",
|
||||
"lower": "Baisser la main"
|
||||
@@ -170,7 +174,8 @@
|
||||
"transcript": "Transcription",
|
||||
"screenRecording": "Enregistrement",
|
||||
"admin": "Commandes de l'organisateur",
|
||||
"tools": "Plus d'outils"
|
||||
"tools": "Plus d'outils",
|
||||
"info": "Informations sur la réunion"
|
||||
},
|
||||
"content": {
|
||||
"participants": "les participants",
|
||||
@@ -179,7 +184,8 @@
|
||||
"transcript": "transcription",
|
||||
"screenRecording": "enregistrement",
|
||||
"admin": "commandes de l'organisateur",
|
||||
"tools": "plus d'outils"
|
||||
"tools": "plus d'outils",
|
||||
"info": "informations sur la réunion"
|
||||
},
|
||||
"closeButton": "Masquer {{content}}"
|
||||
},
|
||||
@@ -200,6 +206,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"roomInformation": {
|
||||
"title": "Informations de connexions",
|
||||
"button": {
|
||||
"ariaLabel": "Copier l'adresse de votre réunion",
|
||||
"copy": "Copier l'adresse",
|
||||
"copied": "Adresse copiée"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transcript": {
|
||||
"start": {
|
||||
"heading": "Transcrire cet appel",
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"open": "Informatie verbergen",
|
||||
"closed": "Informatie tonen"
|
||||
},
|
||||
"hand": {
|
||||
"raise": "Hand opsteken",
|
||||
"lower": "Hand laten zakken"
|
||||
@@ -170,7 +174,8 @@
|
||||
"transcript": "Transcriptie",
|
||||
"screenRecording": "Schermopname",
|
||||
"admin": "Beheerdersbediening",
|
||||
"tools": "Meer tools"
|
||||
"tools": "Meer tools",
|
||||
"info": "Vergaderinformatie"
|
||||
},
|
||||
"content": {
|
||||
"participants": "deelnemers",
|
||||
@@ -179,7 +184,8 @@
|
||||
"screenRecording": "transcriptie",
|
||||
"transcript": "schermopname",
|
||||
"admin": "beheerdersbediening",
|
||||
"tools": "meer tools"
|
||||
"tools": "meer tools",
|
||||
"info": "vergaderinformatie"
|
||||
},
|
||||
"closeButton": "Verberg {{content}}"
|
||||
},
|
||||
@@ -200,6 +206,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"roomInformation": {
|
||||
"title": "Verbindingsinformatie",
|
||||
"button": {
|
||||
"ariaLabel": "Kopieer je vergaderadres",
|
||||
"copy": "Adres kopiëren",
|
||||
"copied": "Adres gekopieerd"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transcript": {
|
||||
"start": {
|
||||
"heading": "Transcribeer dit gesprek",
|
||||
|
||||
@@ -57,6 +57,10 @@ export const text = cva({
|
||||
color: 'default.subtle-text',
|
||||
textStyle: 'sm',
|
||||
},
|
||||
xsNote: {
|
||||
color: 'default.subtle-text',
|
||||
textStyle: 'xs',
|
||||
},
|
||||
inherits: {},
|
||||
},
|
||||
centered: {
|
||||
|
||||
Reference in New Issue
Block a user