Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1af179f726 | |||
| ba3b3fe0ba | |||
| 0c3bcd81c9 | |||
| dbc66c2f07 | |||
| 39be4697b0 | |||
| 2443fa63a5 | |||
| 214dc87b1f | |||
| 3dc23be101 | |||
| 6b5e8081bc | |||
| df671ea994 | |||
| 06a5b9b17e |
@@ -24,7 +24,7 @@ RUN npm run build
|
||||
# Inject PostHog sourcemap metadata into the built assets
|
||||
# This metadata is essential for correctly mapping errors to source maps in production
|
||||
RUN set -e && \
|
||||
npx @posthog/cli sourcemap inject --directory ./dist/assets
|
||||
npx @posthog/cli@0.4.8 sourcemap inject --directory ./dist/assets
|
||||
|
||||
COPY ./docker/dinum-frontend/dinum-styles.css \
|
||||
./dist/assets/
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ info:
|
||||
|
||||
#### Authentication Flow
|
||||
|
||||
1. Exchange application credentials for a JWT token via `/external-api/v1.0/applications/token`.
|
||||
1. Exchange application credentials for a JWT token via `/external-api/v1.0/application/token`.
|
||||
2. Use the JWT token in the `Authorization: Bearer <token>` header for all subsequent requests.
|
||||
3. Tokens are scoped and allow applications to act on behalf of specific users.
|
||||
|
||||
@@ -40,7 +40,7 @@ tags:
|
||||
description: Room management operations
|
||||
|
||||
paths:
|
||||
/applications/token:
|
||||
/application/token:
|
||||
post:
|
||||
tags:
|
||||
- Authentication
|
||||
@@ -283,7 +283,7 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
description: |
|
||||
JWT token obtained from the `/applications/token` endpoint.
|
||||
JWT token obtained from the `/application/token` endpoint.
|
||||
Include in requests as: `Authorization: Bearer <token>`
|
||||
|
||||
schemas:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "agents"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"livekit-agents==1.2.6",
|
||||
|
||||
@@ -326,6 +326,11 @@ class Base(Configuration):
|
||||
"is_silent_login_enabled": values.BooleanValue(
|
||||
True, environ_name="FRONTEND_IS_SILENT_LOGIN_ENABLED", environ_prefix=None
|
||||
),
|
||||
"idle_disconnect_warning_delay": values.PositiveIntegerValue(
|
||||
None,
|
||||
environ_name="FRONTEND_IDLE_DISCONNECT_WARNING_DELAY",
|
||||
environ_prefix=None,
|
||||
),
|
||||
"feedback": values.DictValue(
|
||||
{}, environ_name="FRONTEND_FEEDBACK", environ_prefix=None
|
||||
),
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meet",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"dependencies": {
|
||||
"@livekit/components-react": "2.9.13",
|
||||
"@livekit/components-styles": "1.1.6",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "panda codegen && vite",
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface ApiConfig {
|
||||
custom_css_url?: string
|
||||
use_french_gov_footer?: boolean
|
||||
use_proconnect_button?: boolean
|
||||
idle_disconnect_warning_delay?: number
|
||||
recording?: {
|
||||
is_enabled?: boolean
|
||||
available_modes?: RecordingMode[]
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Button, Dialog, H, P } from '@/primitives'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { connectionObserverStore } from '@/stores/connectionObserver'
|
||||
import { HStack } from '@/styled-system/jsx'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { navigateTo } from '@/navigation/navigateTo'
|
||||
import humanizeDuration from 'humanize-duration'
|
||||
import i18n from 'i18next'
|
||||
|
||||
const IDLE_DISCONNECT_TIMEOUT_MS = 120000 // 2 minutes
|
||||
|
||||
export const IsIdleDisconnectModal = () => {
|
||||
const connectionObserverSnap = useSnapshot(connectionObserverStore)
|
||||
const [timeRemaining, setTimeRemaining] = useState(IDLE_DISCONNECT_TIMEOUT_MS)
|
||||
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'isIdleDisconnectModal' })
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionObserverSnap.isIdleDisconnectModalOpen) {
|
||||
setTimeRemaining(IDLE_DISCONNECT_TIMEOUT_MS)
|
||||
const interval = setInterval(() => {
|
||||
setTimeRemaining((prev) => {
|
||||
if (prev <= 1000) {
|
||||
clearInterval(interval)
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = false
|
||||
navigateTo('feedback', { duplicateIdentity: false })
|
||||
return 0
|
||||
}
|
||||
return prev - 1000
|
||||
})
|
||||
}, 1000)
|
||||
return () => clearInterval(interval)
|
||||
}
|
||||
}, [connectionObserverSnap.isIdleDisconnectModalOpen])
|
||||
|
||||
const minutes = Math.floor(timeRemaining / 1000 / 60)
|
||||
const seconds = (timeRemaining / 1000) % 60
|
||||
const formattedTime = `${minutes}:${seconds.toString().padStart(2, '0')}`
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={connectionObserverSnap.isIdleDisconnectModalOpen}
|
||||
role="alertdialog"
|
||||
type="alert"
|
||||
aria-label={t('title')}
|
||||
onClose={() => {
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = false
|
||||
}}
|
||||
>
|
||||
{({ close }) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={css({
|
||||
height: '50px',
|
||||
width: '50px',
|
||||
backgroundColor: 'blue.100',
|
||||
borderRadius: '25px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontWeight: '500',
|
||||
color: 'blue.800',
|
||||
margin: 'auto',
|
||||
})}
|
||||
>
|
||||
{formattedTime}
|
||||
</div>
|
||||
<H lvl={2} centered>
|
||||
{t('title')}
|
||||
</H>
|
||||
<P>
|
||||
{t('body', {
|
||||
duration: humanizeDuration(IDLE_DISCONNECT_TIMEOUT_MS, {
|
||||
language: i18n.language,
|
||||
}),
|
||||
})}
|
||||
</P>
|
||||
<P>{t('settings')}</P>
|
||||
<HStack marginTop="2rem">
|
||||
<Button
|
||||
onPress={() => {
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = false
|
||||
navigateTo('feedback', { duplicateIdentity: false })
|
||||
}}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
>
|
||||
{t('leaveButton')}
|
||||
</Button>
|
||||
<Button onPress={close} size="sm" variant="primary">
|
||||
{t('stayButton')}
|
||||
</Button>
|
||||
</HStack>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,72 @@
|
||||
import { useRoomContext } from '@livekit/components-react'
|
||||
import {
|
||||
useRemoteParticipants,
|
||||
useRoomContext,
|
||||
} from '@livekit/components-react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { DisconnectReason, RoomEvent } from 'livekit-client'
|
||||
import { useIsAnalyticsEnabled } from '@/features/analytics/hooks/useIsAnalyticsEnabled'
|
||||
import posthog from 'posthog-js'
|
||||
import { connectionObserverStore } from '@/stores/connectionObserver'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
import { userPreferencesStore } from '@/stores/userPreferences'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export const useConnectionObserver = () => {
|
||||
const room = useRoomContext()
|
||||
const connectionStartTimeRef = useRef<number | null>(null)
|
||||
|
||||
const { data } = useConfig()
|
||||
const isAnalyticsEnabled = useIsAnalyticsEnabled()
|
||||
|
||||
const userPreferencesSnap = useSnapshot(userPreferencesStore)
|
||||
|
||||
const idleDisconnectModalTimeoutRef = useRef<ReturnType<
|
||||
typeof setTimeout
|
||||
> | null>(null)
|
||||
|
||||
const remoteParticipants = useRemoteParticipants({
|
||||
updateOnlyOn: [
|
||||
RoomEvent.ParticipantConnected,
|
||||
RoomEvent.ParticipantDisconnected,
|
||||
],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// Always clear existing timer on dependency change
|
||||
if (idleDisconnectModalTimeoutRef.current) {
|
||||
clearTimeout(idleDisconnectModalTimeoutRef.current)
|
||||
idleDisconnectModalTimeoutRef.current = null
|
||||
}
|
||||
|
||||
const isEnabled = userPreferencesSnap.is_idle_disconnect_modal_enabled
|
||||
const delay = data?.idle_disconnect_warning_delay
|
||||
|
||||
// Disabled or invalid delay: ensure modal is closed
|
||||
if (!isEnabled || !delay) {
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = false
|
||||
return
|
||||
}
|
||||
|
||||
if (remoteParticipants.length === 0) {
|
||||
idleDisconnectModalTimeoutRef.current = setTimeout(() => {
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = true
|
||||
}, delay)
|
||||
} else {
|
||||
connectionObserverStore.isIdleDisconnectModalOpen = false
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (idleDisconnectModalTimeoutRef.current) {
|
||||
clearTimeout(idleDisconnectModalTimeoutRef.current)
|
||||
idleDisconnectModalTimeoutRef.current = null
|
||||
}
|
||||
}
|
||||
}, [
|
||||
remoteParticipants.length,
|
||||
data?.idle_disconnect_warning_delay,
|
||||
userPreferencesSnap.is_idle_disconnect_modal_enabled,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAnalyticsEnabled) return
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
|
||||
import { Subtitles } from '@/features/subtitle/component/Subtitles'
|
||||
import { CarouselLayout } from '../components/layout/CarouselLayout'
|
||||
import { GridLayout } from '../components/layout/GridLayout'
|
||||
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
|
||||
|
||||
const LayoutWrapper = styled(
|
||||
'div',
|
||||
@@ -191,6 +192,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
||||
isOpen={isShareErrorVisible}
|
||||
onClose={() => setIsShareErrorVisible(false)}
|
||||
/>
|
||||
<IsIdleDisconnectModal />
|
||||
<div
|
||||
// todo - extract these magic values into constant
|
||||
style={{
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Field, H } from '@/primitives'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLanguageLabels } from '@/i18n/useLanguageLabels'
|
||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||
import { userPreferencesStore } from '@/stores/userPreferences'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export type GeneralTabProps = Pick<TabPanelProps, 'id'>
|
||||
|
||||
@@ -9,6 +11,8 @@ export const GeneralTab = ({ id }: GeneralTabProps) => {
|
||||
const { t, i18n } = useTranslation('settings')
|
||||
const { languagesList, currentLanguage } = useLanguageLabels()
|
||||
|
||||
const userPreferencesSnap = useSnapshot(userPreferencesStore)
|
||||
|
||||
return (
|
||||
<TabPanel padding={'md'} flex id={id}>
|
||||
<H lvl={2}>{t('language.heading')}</H>
|
||||
@@ -21,6 +25,20 @@ export const GeneralTab = ({ id }: GeneralTabProps) => {
|
||||
i18n.changeLanguage(lang as string)
|
||||
}}
|
||||
/>
|
||||
<H lvl={2}>{t('preferences.title')}</H>
|
||||
<Field
|
||||
type="switch"
|
||||
label={t('preferences.idleDisconnectModal.label')}
|
||||
description={t('preferences.idleDisconnectModal.description')}
|
||||
isSelected={userPreferencesSnap.is_idle_disconnect_modal_enabled}
|
||||
onChange={(value) =>
|
||||
(userPreferencesStore.is_idle_disconnect_modal_enabled = value)
|
||||
}
|
||||
wrapperProps={{
|
||||
noMargin: true,
|
||||
fullWidth: true,
|
||||
}}
|
||||
/>
|
||||
</TabPanel>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||
import { H, Switch } from '@/primitives'
|
||||
import { Field, H } from '@/primitives'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSnapshot } from 'valtio'
|
||||
@@ -23,15 +23,19 @@ export const NotificationsTab = ({ id }: NotificationsTabProps) => {
|
||||
{Array.from(notificationsSnap.soundNotifications).map(
|
||||
([key, value]) => (
|
||||
<li key={key}>
|
||||
<Switch
|
||||
aria-label={`${t(`actions.${value ? 'disable' : 'enable'}`)} ${t('label')} "${t(`items.${key}`)}"`}
|
||||
<Field
|
||||
type="switch"
|
||||
aria-label={`${t(`actions.${value ? 'disable' : 'enable'}`)} ${t('label')} "${t(`items.${key}.label`)}"`}
|
||||
label={t(`items.${key}.label`)}
|
||||
isSelected={value}
|
||||
onChange={(v) => {
|
||||
notificationsStore.soundNotifications.set(key, v)
|
||||
}}
|
||||
>
|
||||
{t(`items.${key}`)}
|
||||
</Switch>
|
||||
wrapperProps={{
|
||||
noMargin: true,
|
||||
fullWidth: true,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -149,6 +149,13 @@
|
||||
"newTab": "Neues Fenster"
|
||||
}
|
||||
},
|
||||
"isIdleDisconnectModal": {
|
||||
"title": "Bist du noch da?",
|
||||
"body": "Du bist der einzige Teilnehmer. Dieses Gespräch endet in {{duration}}. Möchtest du das Gespräch fortsetzen?",
|
||||
"settings": "Um diese Nachricht nicht mehr zu sehen, gehe zu Einstellungen > Allgemein.",
|
||||
"stayButton": "Gespräch fortsetzen",
|
||||
"leaveButton": "Jetzt verlassen"
|
||||
},
|
||||
"controls": {
|
||||
"microphone": "Mikrofon",
|
||||
"camera": "Kamera",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"authentication": "Authentifizierung",
|
||||
"nameError": "Ihr Name darf nicht leer sein"
|
||||
},
|
||||
"preferences": {
|
||||
"title": "Einstellungen",
|
||||
"idleDisconnectModal": {
|
||||
"label": "Anrufe ohne Teilnehmer verlassen",
|
||||
"description": "Verlässt automatisch einen Anruf nach einigen Minuten, wenn kein anderer Teilnehmer beitritt"
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"microphone": {
|
||||
"heading": "Mikrofon",
|
||||
@@ -69,9 +76,15 @@
|
||||
"enable": "Aktivieren"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Teilnehmer beigetreten",
|
||||
"handRaised": "Hand gehoben",
|
||||
"messageReceived": "Nachricht erhalten"
|
||||
"participantJoined": {
|
||||
"label": "Teilnehmer beigetreten"
|
||||
},
|
||||
"handRaised": {
|
||||
"label": "Hand gehoben"
|
||||
},
|
||||
"messageReceived": {
|
||||
"label": "Nachricht erhalten"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
|
||||
@@ -149,6 +149,13 @@
|
||||
"newTab": "New window"
|
||||
}
|
||||
},
|
||||
"isIdleDisconnectModal": {
|
||||
"title": "Are you still there?",
|
||||
"body": "You are the only participant. This call will end in {{duration}}. Would you like to continue the call?",
|
||||
"settings": "To stop seeing this message, go to Settings > General.",
|
||||
"stayButton": "Continue the call",
|
||||
"leaveButton": "Leave now"
|
||||
},
|
||||
"controls": {
|
||||
"microphone": "Microphone",
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"authentication": "Authentication",
|
||||
"nameError": "Your name cannot be empty"
|
||||
},
|
||||
"preferences": {
|
||||
"title": "Preferences",
|
||||
"idleDisconnectModal": {
|
||||
"label": "Leave calls with no participants",
|
||||
"description": "Automatically leaves a call after a few minutes if no other participant joins"
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"microphone": {
|
||||
"heading": "Microphone",
|
||||
@@ -69,9 +76,15 @@
|
||||
"enable": "Enable"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Participant joined",
|
||||
"handRaised": "Hand raised",
|
||||
"messageReceived": "Message received"
|
||||
"participantJoined": {
|
||||
"label": "Participant joined"
|
||||
},
|
||||
"handRaised": {
|
||||
"label": "Hand raised"
|
||||
},
|
||||
"messageReceived": {
|
||||
"label": "Message received"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
|
||||
@@ -149,6 +149,13 @@
|
||||
"newTab": "Nouvelle fenêtre"
|
||||
}
|
||||
},
|
||||
"isIdleDisconnectModal": {
|
||||
"title": "Êtes-vous toujours là ?",
|
||||
"body": "Vous êtes le seul participant. Cet appel va donc se terminer dans {{duration}}. Voulez-vous poursuivre l'appel ?",
|
||||
"settings": "Pour ne plus voir ce message, accèder à Paramètres > Général.",
|
||||
"stayButton": "Poursuivre l'appel",
|
||||
"leaveButton": "Quitter maintenant"
|
||||
},
|
||||
"controls": {
|
||||
"microphone": "Microphone",
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"authentication": "Authentification",
|
||||
"nameError": "Votre Nom ne peut pas être vide"
|
||||
},
|
||||
"preferences": {
|
||||
"title": "Préférences",
|
||||
"idleDisconnectModal": {
|
||||
"label": "Quitter les appels sans autre participant",
|
||||
"description": "Vous fait quitter un appel au bout de quelques minutes si aucun autre participant ne vous rejoint"
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"microphone": {
|
||||
"heading": "Micro",
|
||||
@@ -69,9 +76,15 @@
|
||||
"enable": "Activer"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Un nouveau participant",
|
||||
"handRaised": "Une main levée",
|
||||
"messageReceived": "Un message reçu"
|
||||
"participantJoined": {
|
||||
"label": "Un nouveau participant"
|
||||
},
|
||||
"handRaised": {
|
||||
"label": "Une main levée"
|
||||
},
|
||||
"messageReceived": {
|
||||
"label": "Un message reçu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
|
||||
@@ -149,6 +149,13 @@
|
||||
"newTab": "Nieuw venster"
|
||||
}
|
||||
},
|
||||
"isIdleDisconnectModal": {
|
||||
"title": "Ben je er nog?",
|
||||
"body": "Je bent de enige deelnemer. Dit gesprek wordt over {{duration}} beëindigd. Wil je het gesprek voortzetten?",
|
||||
"settings": "Om dit bericht niet meer te zien, ga naar Instellingen > Algemeen.",
|
||||
"stayButton": "Gesprek voortzetten",
|
||||
"leaveButton": "Nu verlaten"
|
||||
},
|
||||
"controls": {
|
||||
"microphone": "Microfoon",
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"authentication": "Authenticatie",
|
||||
"nameError": "Uw naam mag niet leeg zijn"
|
||||
},
|
||||
"preferences": {
|
||||
"title": "Voorkeuren",
|
||||
"idleDisconnectModal": {
|
||||
"label": "Verlaat oproepen zonder deelnemers",
|
||||
"description": "Verlaat automatisch een oproep na een paar minuten als geen andere deelnemer meedoet"
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"microphone": {
|
||||
"heading": "Microfoon",
|
||||
@@ -69,9 +76,15 @@
|
||||
"enable": "Inschakelen"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Deelnemer is toegevoegd",
|
||||
"handRaised": "Hand opgestoken",
|
||||
"messageReceived": "Bericht ontvangen"
|
||||
"participantJoined": {
|
||||
"label": "Deelnemer is toegevoegd"
|
||||
},
|
||||
"handRaised": {
|
||||
"label": "Hand opgestoken"
|
||||
},
|
||||
"messageReceived": {
|
||||
"label": "Bericht ontvangen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
|
||||
@@ -28,6 +28,10 @@ const box = cva({
|
||||
width: '30rem',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
alert: {
|
||||
width: '24rem',
|
||||
maxWidth: '100%',
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
light: {
|
||||
|
||||
@@ -80,7 +80,7 @@ export type DialogProps = RACDialogProps & {
|
||||
* after user interaction
|
||||
*/
|
||||
onOpenChange?: (isOpen: boolean) => void
|
||||
type?: 'flex'
|
||||
type?: 'flex' | 'alert'
|
||||
innerRef?: MutableRefObject<HTMLDivElement | null>
|
||||
size?: 'full' | 'large'
|
||||
}
|
||||
@@ -96,7 +96,12 @@ export const Dialog = ({
|
||||
...dialogProps
|
||||
}: DialogProps) => {
|
||||
const isAlert = dialogProps['role'] === 'alertdialog'
|
||||
const boxType = dialogProps['type'] !== 'flex' ? 'dialog' : undefined
|
||||
const boxType =
|
||||
dialogProps['type'] === 'alert'
|
||||
? 'alert'
|
||||
: dialogProps['type'] !== 'flex'
|
||||
? 'dialog'
|
||||
: undefined
|
||||
return (
|
||||
<StyledModalOverlay
|
||||
isKeyboardDismissDisabled={isAlert}
|
||||
|
||||
@@ -259,6 +259,7 @@ export const Field = <T extends object>({
|
||||
}
|
||||
|
||||
if (type === 'switch') {
|
||||
const SWITCH_COMPONENT_WIDTH = '41px'
|
||||
return (
|
||||
<FieldWrapper {...props.wrapperProps}>
|
||||
<div
|
||||
@@ -273,10 +274,11 @@ export const Field = <T extends object>({
|
||||
{description && (
|
||||
<Text
|
||||
variant="note"
|
||||
wrap={'pretty'}
|
||||
wrap={'balance'}
|
||||
className={css({
|
||||
textStyle: 'sm',
|
||||
marginBottom: '0.5rem',
|
||||
marginRight: SWITCH_COMPONENT_WIDTH,
|
||||
})}
|
||||
>
|
||||
{description}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { proxy } from 'valtio'
|
||||
|
||||
type State = {
|
||||
isIdleDisconnectModalOpen: boolean
|
||||
}
|
||||
|
||||
export const connectionObserverStore = proxy<State>({
|
||||
isIdleDisconnectModalOpen: false,
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import { proxy } from 'valtio'
|
||||
import { subscribe } from 'valtio/index'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
|
||||
type State = {
|
||||
is_idle_disconnect_modal_enabled: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
is_idle_disconnect_modal_enabled: true,
|
||||
}
|
||||
|
||||
function getUserPreferencesState(): State {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEYS.USER_PREFERENCES)
|
||||
if (!stored) return DEFAULT_STATE
|
||||
const parsed = JSON.parse(stored)
|
||||
return {
|
||||
...DEFAULT_STATE,
|
||||
...parsed,
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[UserPreferencesStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
export const userPreferencesStore = proxy<State>(getUserPreferencesState())
|
||||
|
||||
subscribe(userPreferencesStore, () => {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.USER_PREFERENCES,
|
||||
JSON.stringify(userPreferencesStore)
|
||||
)
|
||||
})
|
||||
@@ -3,4 +3,5 @@
|
||||
*/
|
||||
export const STORAGE_KEYS = {
|
||||
NOTIFICATIONS: 'app_notification_settings',
|
||||
USER_PREFERENCES: 'app_user_preferences',
|
||||
} as const
|
||||
|
||||
@@ -57,6 +57,7 @@ backend:
|
||||
FRONTEND_TRANSCRIPT: "{'form_beta_users': 'https://grist.numerique.gouv.fr/o/docs/forms/3fFfvJoTBEQ6ZiMi8zsQwX/17'}"
|
||||
FRONTEND_FEEDBACK: "{'url': 'https://grist.numerique.gouv.fr/o/docs/cbMv4G7pLY3Z/USER-RESEARCH-or-LA-SUITE/f/26'}"
|
||||
FRONTEND_MANIFEST_LINK: "https://docs.numerique.gouv.fr/docs/1ef86abf-f7e0-46ce-b6c7-8be8b8af4c3d/"
|
||||
FRONTEND_IDLE_DISCONNECT_WARNING_DELAY: 9000
|
||||
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.5.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"description": "An util to generate html and text django's templates from mjml templates",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sdk",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"./library",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "0.1.40",
|
||||
"version": "0.1.41",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "summary"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.105.0",
|
||||
"uvicorn>=0.24.0",
|
||||
|
||||
@@ -123,7 +123,7 @@ class MetadataManager:
|
||||
logger.error("Invalid number of arguments to enable metadata manager.")
|
||||
return
|
||||
|
||||
filename, email, _, received_at, *_ = task_args
|
||||
_, filename, email, _, received_at, *_ = task_args
|
||||
|
||||
initial_metadata = {
|
||||
**initial_metadata,
|
||||
|
||||
Reference in New Issue
Block a user