-
-
-
{
+ if (isEncrypted && !isAdminOrOwner) return null
+ if (isEncrypted) {
+ return (
+
- {tHome('warning')}
-
-
-
-
- {roomUrl?.replace(/^https?:\/\//, '')}
-
-
-
-
- {t('encryptedDisabledHeading')}
-
-
- }
- label={tFeatures('features.dialIn')}
- />
- }
- label={tFeatures('features.meetingRoom')}
- />
-
-
- ) : isTelephonyReadyForUse ? (
-
-
-
- {roomUrl?.replace(/^https?:\/\//, '')}
-
- {isTelephonyReadyForUse && roomUrl && (
-
+
+
+ {roomUrl?.replace(/^https?:\/\//, '')}
+
+
+
+
+ {t('encryptedDisabledHeading')}
+
+
+ }
+ label={tFeatures('features.dialIn')}
+ />
+ }
+ label={tFeatures('features.meetingRoom')}
+ />
+
+
+ )
+ }
+ if (isTelephonyReadyForUse) {
+ return (
+
+
+
+ {roomUrl?.replace(/^https?:\/\//, '')}
+
+ {isTelephonyReadyForUse && roomUrl && (
+
+ )}
+
+
+
+ {t('phone.call')} ({telephony?.country}){' '}
+ {telephony?.internationalPhoneNumber}
+
+
+ {t('phone.pinCode')}{' '}
+ {formatPinCode(roomData?.pin_code)}
+
+
+
+
- )}
-
-
-
- {t('phone.call')} ({telephony?.country}){' '}
- {telephony?.internationalPhoneNumber}
-
-
- {t('phone.pinCode')}{' '}
- {formatPinCode(roomData?.pin_code)}
-
-
-
+
+ )
+ }
+ return (
{
{t('access.description')}
{(() => {
- const isEncrypted = readOnlyData?.encryption_mode === 'basic'
+ const isEncrypted =
+ readOnlyData?.encryption_mode === ApiEncryptionMode.BASIC
return (
<>
{isEncrypted && (
diff --git a/src/frontend/src/features/rooms/livekit/components/Info.tsx b/src/frontend/src/features/rooms/livekit/components/Info.tsx
index 4a1acfcf..092164eb 100644
--- a/src/frontend/src/features/rooms/livekit/components/Info.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/Info.tsx
@@ -16,6 +16,7 @@ import { formatPinCode } from '../../utils/telephony'
import { useTelephony } from '../hooks/useTelephony'
import { useCopyRoomToClipboard } from '../hooks/useCopyRoomToClipboard'
import { FeaturePill } from '@/features/encryption'
+import { ApiEncryptionMode } from '../../api/ApiRoom'
export const Info = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'info' })
@@ -30,7 +31,7 @@ export const Info = () => {
: baseRoomUrl
const telephony = useTelephony()
- const isEncrypted = data?.encryption_mode === 'basic'
+ const isEncrypted = data?.encryption_mode === ApiEncryptionMode.BASIC
const isAdminOrOwner = !!data?.is_administrable
const isTelephonyReadyForUse = useMemo(() => {
diff --git a/src/frontend/src/features/rooms/livekit/components/Tools.tsx b/src/frontend/src/features/rooms/livekit/components/Tools.tsx
index 76ce0989..76399787 100644
--- a/src/frontend/src/features/rooms/livekit/components/Tools.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/Tools.tsx
@@ -14,6 +14,7 @@ import {
} from '@/features/recording'
import { useConfig } from '@/api/useConfig'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
+import { ApiEncryptionMode } from '@/features/rooms/api/ApiRoom'
export interface ToolsButtonProps {
icon: ReactNode
@@ -108,7 +109,7 @@ export const Tools = () => {
useSidePanel()
const { t } = useTranslation('rooms', { keyPrefix: 'moreTools' })
const roomData = useRoomData()
- const isEncrypted = roomData?.encryption_mode === 'basic'
+ const isEncrypted = roomData?.encryption_mode === ApiEncryptionMode.BASIC
// Restore focus to the element that opened the Tools panel
useRestoreFocus(isToolsOpen, {
diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Options/ScreenRecordingMenuItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Options/ScreenRecordingMenuItem.tsx
index bbc6e989..f3ce9dfc 100644
--- a/src/frontend/src/features/rooms/livekit/components/controls/Options/ScreenRecordingMenuItem.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/controls/Options/ScreenRecordingMenuItem.tsx
@@ -6,13 +6,14 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { RecordingMode, useHasRecordingAccess } from '@/features/recording'
import { FeatureFlags } from '@/features/analytics/enums'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
+import { ApiEncryptionMode } from '@/features/rooms/api/ApiRoom'
export const ScreenRecordingMenuItem = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
const { isScreenRecordingOpen, openScreenRecording, toggleTools } =
useSidePanel()
const roomData = useRoomData()
- const isEncrypted = roomData?.encryption_mode === 'basic'
+ const isEncrypted = roomData?.encryption_mode === ApiEncryptionMode.BASIC
const hasScreenRecordingAccess = useHasRecordingAccess(
RecordingMode.ScreenRecording,
diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Options/TranscriptMenuItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Options/TranscriptMenuItem.tsx
index c9d276b5..f744cb49 100644
--- a/src/frontend/src/features/rooms/livekit/components/controls/Options/TranscriptMenuItem.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/controls/Options/TranscriptMenuItem.tsx
@@ -6,12 +6,13 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { RecordingMode, useHasRecordingAccess } from '@/features/recording'
import { FeatureFlags } from '@/features/analytics/enums'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
+import { ApiEncryptionMode } from '@/features/rooms/api/ApiRoom'
export const TranscriptMenuItem = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
const { isTranscriptOpen, openTranscript, toggleTools } = useSidePanel()
const roomData = useRoomData()
- const isEncrypted = roomData?.encryption_mode === 'basic'
+ const isEncrypted = roomData?.encryption_mode === ApiEncryptionMode.BASIC
const hasTranscriptAccess = useHasRecordingAccess(
RecordingMode.Transcript,
diff --git a/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts b/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts
index cedf92b5..26f92533 100644
--- a/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts
+++ b/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts
@@ -2,7 +2,7 @@ import { useTelephony } from './useTelephony'
import { useTranslation } from 'react-i18next'
import { useEffect, useMemo, useState } from 'react'
import { formatPinCode } from '@/features/rooms/utils/telephony'
-import { ApiRoom } from '@/features/rooms/api/ApiRoom'
+import { ApiRoom, ApiEncryptionMode } from '@/features/rooms/api/ApiRoom'
import { getRouteUrl } from '@/navigation/getRouteUrl'
const COPY_SUCCESS_TIMEOUT = 3000
@@ -47,7 +47,9 @@ export const useCopyRoomToClipboard = (
// so make sure we don't paste a non-functional phone+PIN snippet either.
const hasTelephonyInfo = useMemo(() => {
return (
- telephony.enabled && room?.pin_code && room?.encryption_mode !== 'basic'
+ telephony.enabled &&
+ room?.pin_code &&
+ room?.encryption_mode !== ApiEncryptionMode.BASIC
)
}, [telephony.enabled, room?.pin_code, room?.encryption_mode])
diff --git a/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx b/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx
index 27f3405a..4bec83a5 100644
--- a/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx
+++ b/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx
@@ -77,10 +77,17 @@ export const CreateMeetingButton = () => {
// Communicate iframe height to parent for proper sizing
useEffect(() => {
+ const targetOrigin = (() => {
+ try {
+ return document.referrer ? new URL(document.referrer).origin : '/'
+ } catch {
+ return '/'
+ }
+ })()
const observer = new ResizeObserver(() => {
window.parent.postMessage(
{ type: 'RESIZE', data: { height: document.body.scrollHeight } },
- '*'
+ targetOrigin
)
})
observer.observe(document.body)
diff --git a/src/frontend/src/features/settings/components/EncryptionDefaultField.tsx b/src/frontend/src/features/settings/components/EncryptionDefaultField.tsx
index 94ffed9a..67d1c5c3 100644
--- a/src/frontend/src/features/settings/components/EncryptionDefaultField.tsx
+++ b/src/frontend/src/features/settings/components/EncryptionDefaultField.tsx
@@ -30,16 +30,21 @@ export const EncryptionDefaultField = () => {
const isOn = user?.default_encryption_mode === ApiEncryptionMode.BASIC
- const handleToggle = (next: boolean) => {
+ const handleToggle = async (next: boolean) => {
if (!user) return
- void mutateAsync({
- user: {
- id: user.id,
- default_encryption_mode: next
- ? ApiEncryptionMode.BASIC
- : ApiEncryptionMode.NONE,
- },
- })
+ try {
+ await mutateAsync({
+ user: {
+ id: user.id,
+ default_encryption_mode: next
+ ? ApiEncryptionMode.BASIC
+ : ApiEncryptionMode.NONE,
+ },
+ })
+ } catch {
+ // react-query stores the error on the mutation; the UI shows
+ // isPending while in flight and reverts the toggle on failure.
+ }
}
if (!isLoggedIn) {
diff --git a/src/frontend/src/navigation/navigateTo.ts b/src/frontend/src/navigation/navigateTo.ts
index b191940e..94369225 100644
--- a/src/frontend/src/navigation/navigateTo.ts
+++ b/src/frontend/src/navigation/navigateTo.ts
@@ -20,8 +20,7 @@ export const navigateTo = (
// Including the hash in the URL passed to `navigate` lets us avoid a
// brittle pushState + replaceState dance at the call site: the URL the
// app first renders already carries the fragment.
- const target = options?.hash ? `${to}#${options.hash}` : to
- const { hash: _hash, ...navigateOptions } = options ?? {}
- void _hash
+ const { hash, ...navigateOptions } = options ?? {}
+ const target = hash ? `${to}#${hash}` : to
return navigate(target, navigateOptions)
}