🚸(frontend) add warning for full screen capture
Following user feedback about infinite loops risks, add warning overlay when users attempt to share their entire screen. The warning explains the risk and suggests sharing a specific tab instead, providing options to stop sharing or dismiss the warning while respecting user preferences for future sessions.
This commit is contained in:
committed by
aleb_the_flash
parent
b7fb6b1b69
commit
6f09eb3602
@@ -0,0 +1,121 @@
|
||||
import { css } from '@/styled-system/css'
|
||||
import { Button, Text } from '@/primitives'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { ScreenSharePreferenceStore } from '@/stores/ScreenSharePreferences'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useLocalParticipant } from '@livekit/components-react'
|
||||
import { useSize } from '../hooks/useResizeObserver'
|
||||
import { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const FullScreenShareWarning = ({
|
||||
trackReference,
|
||||
}: {
|
||||
trackReference: TrackReferenceOrPlaceholder
|
||||
}) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'fullScreenWarning' })
|
||||
|
||||
const warningContainerRef = useRef<HTMLDivElement>(null)
|
||||
const { width: containerWidth } = useSize(warningContainerRef)
|
||||
const { localParticipant } = useLocalParticipant()
|
||||
const screenSharePreferences = useSnapshot(ScreenSharePreferenceStore)
|
||||
|
||||
const isFullScreenCapture = useMemo(() => {
|
||||
const trackLabel =
|
||||
trackReference.publication?.track?.mediaStreamTrack?.label
|
||||
return trackLabel?.includes('screen')
|
||||
}, [trackReference])
|
||||
|
||||
const shouldShowWarning =
|
||||
screenSharePreferences.enabled && isFullScreenCapture
|
||||
|
||||
const handleStopScreenShare = async () => {
|
||||
if (!localParticipant.isScreenShareEnabled) return
|
||||
await localParticipant.setScreenShareEnabled(false, {}, {})
|
||||
}
|
||||
|
||||
const handleDismissWarning = () => {
|
||||
ScreenSharePreferenceStore.enabled = false
|
||||
}
|
||||
|
||||
if (!shouldShowWarning) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
position: 'absolute',
|
||||
zIndex: '1000',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
})}
|
||||
ref={warningContainerRef}
|
||||
>
|
||||
{(!containerWidth || containerWidth >= 300) && (
|
||||
<div
|
||||
className={css({
|
||||
position: 'absolute',
|
||||
zIndex: '1000',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
backgroundColor: 'rgba(22, 22, 34, 0.9)',
|
||||
padding: '2rem',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem',
|
||||
md: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: 'white',
|
||||
flexBasis: '55%',
|
||||
fontWeight: '500',
|
||||
}}
|
||||
margin={false}
|
||||
wrap={'pretty'}
|
||||
>
|
||||
{t('message')}
|
||||
</Text>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '1rem',
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
style={{
|
||||
height: 'fit-content',
|
||||
}}
|
||||
onPress={async () => {
|
||||
await handleStopScreenShare()
|
||||
}}
|
||||
>
|
||||
{t('stop')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primaryTextDark"
|
||||
size="sm"
|
||||
style={{
|
||||
height: 'fit-content',
|
||||
}}
|
||||
onPress={() => handleDismissWarning()}
|
||||
>
|
||||
{t('ignore')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import { HStack } from '@/styled-system/jsx'
|
||||
import { MutedMicIndicator } from './MutedMicIndicator'
|
||||
import { ParticipantPlaceholder } from './ParticipantPlaceholder'
|
||||
import { ParticipantTileFocus } from './ParticipantTileFocus'
|
||||
import { FullScreenShareWarning } from './FullScreenShareWarning'
|
||||
|
||||
export function TrackRefContextIfNeeded(
|
||||
props: React.PropsWithChildren<{
|
||||
@@ -100,6 +101,7 @@ export const ParticipantTile: (
|
||||
<div ref={ref} style={{ position: 'relative' }} {...elementProps}>
|
||||
<TrackRefContextIfNeeded trackRef={trackReference}>
|
||||
<ParticipantContextIfNeeded participant={trackReference.participant}>
|
||||
<FullScreenShareWarning trackReference={trackReference} />
|
||||
{children ?? (
|
||||
<>
|
||||
{isTrackReference(trackReference) &&
|
||||
|
||||
@@ -199,5 +199,10 @@
|
||||
"effects": "",
|
||||
"muteParticipant": "",
|
||||
"fullScreen": ""
|
||||
},
|
||||
"fullScreenWarning": {
|
||||
"message": "",
|
||||
"stop": "",
|
||||
"ignore": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,5 +198,10 @@
|
||||
"effects": "Apply visual effects",
|
||||
"muteParticipant": "Mute {{name}}",
|
||||
"fullScreen": "Full screen"
|
||||
},
|
||||
"fullScreenWarning": {
|
||||
"message": "To avoid infinite loop display, do not share your entire screen or browser window. Instead, share a tab or another window.",
|
||||
"stop": "Stop presenting",
|
||||
"ignore": "Ignore"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,5 +198,10 @@
|
||||
"effects": "Appliquer des effets",
|
||||
"muteParticipant": "Couper le micro de {{name}}",
|
||||
"fullScreen": "Plein écran"
|
||||
},
|
||||
"fullScreenWarning": {
|
||||
"message": "Pour éviter l'affichage en boucle infinie, ne partagez pas l'intégralité de votre écran ni de votre fenêtre de navigateur. Partagez plutôt un onglet ou une autre fenêtre.",
|
||||
"stop": "Arrêter la présentation",
|
||||
"ignore": "Ignorer"
|
||||
}
|
||||
}
|
||||
|
||||
9
src/frontend/src/stores/ScreenSharePreferences.ts
Normal file
9
src/frontend/src/stores/ScreenSharePreferences.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { proxy } from 'valtio'
|
||||
|
||||
type State = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export const ScreenSharePreferenceStore = proxy<State>({
|
||||
enabled: true,
|
||||
})
|
||||
Reference in New Issue
Block a user