mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-04 06:35:38 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6811700911 |
+1
-1
@@ -24,6 +24,7 @@ and this project adheres to
|
|||||||
- ⬆️(frontend) upgrade posthog-js from 1.404.1 to 1.409.5
|
- ⬆️(frontend) upgrade posthog-js from 1.404.1 to 1.409.5
|
||||||
- ⚡️(frontend) apply frugal constraint to the active meeting audio track
|
- ⚡️(frontend) apply frugal constraint to the active meeting audio track
|
||||||
- ⚡️(backend) replace blocking Redis KEYS with cursor-based SCAN
|
- ⚡️(backend) replace blocking Redis KEYS with cursor-based SCAN
|
||||||
|
- ✨(summary) add hostname to analytics properties
|
||||||
|
|
||||||
## [1.28.0] - 2026-08-24
|
## [1.28.0] - 2026-08-24
|
||||||
|
|
||||||
@@ -62,7 +63,6 @@ and this project adheres to
|
|||||||
|
|
||||||
- 🔥(frontend) drop unused vendored ConnectionObserver
|
- 🔥(frontend) drop unused vendored ConnectionObserver
|
||||||
- 🐛(frontend) vendor formatChatMessageLinks and trim surrounding newlines
|
- 🐛(frontend) vendor formatChatMessageLinks and trim surrounding newlines
|
||||||
- ✨(summary) add hostname to analytics properties
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -42,23 +42,6 @@ export type IceCandidateReport = {
|
|||||||
working: IceCandidatePair[]
|
working: IceCandidatePair[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* True when the selected pair does not carry media over plain UDP: the browser
|
|
||||||
* fell back to a TURN relay over TCP or TLS, or even the direct route is not
|
|
||||||
* UDP. Media still flows, but quality usually degrades under load.
|
|
||||||
*
|
|
||||||
* Accepts the loosely typed `data` stored on the step result; anything that is
|
|
||||||
* not an IceCandidateReport simply yields false.
|
|
||||||
*/
|
|
||||||
export const isSuboptimalRoute = (data: unknown): boolean => {
|
|
||||||
const selected = (data as IceCandidateReport | null | undefined)?.selected
|
|
||||||
if (!selected) return false
|
|
||||||
const transport = selected.local.relayProtocol ?? selected.local.protocol
|
|
||||||
// An unreported transport is not evidence of a bad route.
|
|
||||||
if (!transport) return false
|
|
||||||
return transport.toLowerCase() !== 'udp'
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROBE_WIDTH = 320
|
const PROBE_WIDTH = 320
|
||||||
const PROBE_HEIGHT = 180
|
const PROBE_HEIGHT = 180
|
||||||
const PROBE_FPS = 15
|
const PROBE_FPS = 15
|
||||||
|
|||||||
@@ -2,33 +2,18 @@ import type { ReactNode } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ProgressBar } from 'react-aria-components'
|
import { ProgressBar } from 'react-aria-components'
|
||||||
import { css, cx } from '@/styled-system/css'
|
import { css, cx } from '@/styled-system/css'
|
||||||
import { A } from '@/primitives'
|
|
||||||
import type { ConnectionTestStats } from '../types'
|
import type { ConnectionTestStats } from '../types'
|
||||||
import { statusSquareClass } from './stepAppearance'
|
import { statusSquareClass } from './stepAppearance'
|
||||||
|
|
||||||
/**
|
type SummaryState = 'idle' | 'running' | 'passed' | 'partial' | 'failed'
|
||||||
* Network prerequisites (protocols, ports, TURN fallbacks), written for the
|
|
||||||
* reader's IT department rather than for the end user.
|
|
||||||
*/
|
|
||||||
const NETWORK_REQUIREMENTS_DOC_URL =
|
|
||||||
'https://docs.numerique.gouv.fr/docs/f2baa1b9-f29e-4d58-959d-65d4376fc6b8/'
|
|
||||||
|
|
||||||
type SummaryState =
|
/** Only a failure earns a colour: everything else stays near-black. */
|
||||||
| 'idle'
|
|
||||||
| 'running'
|
|
||||||
| 'passed'
|
|
||||||
| 'partial'
|
|
||||||
| 'failed'
|
|
||||||
| 'warning'
|
|
||||||
|
|
||||||
/** Only a failure or a degraded route earns a colour: everything else stays near-black. */
|
|
||||||
const stateColorClass: Record<SummaryState, string> = {
|
const stateColorClass: Record<SummaryState, string> = {
|
||||||
idle: css({ color: 'greyscale.1000' }),
|
idle: css({ color: 'greyscale.1000' }),
|
||||||
running: css({ color: 'greyscale.1000' }),
|
running: css({ color: 'greyscale.1000' }),
|
||||||
passed: css({ color: 'greyscale.1000' }),
|
passed: css({ color: 'greyscale.1000' }),
|
||||||
partial: css({ color: 'greyscale.1000' }),
|
partial: css({ color: 'greyscale.1000' }),
|
||||||
failed: css({ color: 'danger.600' }),
|
failed: css({ color: 'danger.600' }),
|
||||||
warning: css({ color: 'warning' }),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cardClass = css({
|
const cardClass = css({
|
||||||
@@ -191,28 +176,20 @@ const Counter = ({
|
|||||||
export const ConnectionTestSummary = ({
|
export const ConnectionTestSummary = ({
|
||||||
stats,
|
stats,
|
||||||
isRunning,
|
isRunning,
|
||||||
routeWarning = false,
|
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
stats: ConnectionTestStats
|
stats: ConnectionTestStats
|
||||||
isRunning: boolean
|
isRunning: boolean
|
||||||
/** The selected ICE route is usable but not UDP (e.g. TURN over TCP/TLS). */
|
|
||||||
routeWarning?: boolean
|
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('connectionTest')
|
const { t } = useTranslation('connectionTest')
|
||||||
|
|
||||||
// A hard failure still outranks the route warning; the warning outranks
|
|
||||||
// 'partial' because a measured degraded route matters more than skipped
|
|
||||||
// camera or microphone checks.
|
|
||||||
const state: SummaryState = isRunning
|
const state: SummaryState = isRunning
|
||||||
? 'running'
|
? 'running'
|
||||||
: !stats.hasStarted
|
: !stats.hasStarted
|
||||||
? 'idle'
|
? 'idle'
|
||||||
: stats.failed > 0
|
: stats.failed > 0
|
||||||
? 'failed'
|
? 'failed'
|
||||||
: routeWarning
|
|
||||||
? 'warning'
|
|
||||||
: stats.skipped > 0
|
: stats.skipped > 0
|
||||||
? 'partial'
|
? 'partial'
|
||||||
: 'passed'
|
: 'passed'
|
||||||
@@ -229,22 +206,7 @@ export const ConnectionTestSummary = ({
|
|||||||
: t(`summary.${state}`)}
|
: t(`summary.${state}`)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className={hintClass}>
|
<p className={hintClass}>{t(`summary.${state}Hint`)}</p>
|
||||||
{t(`summary.${state}Hint`)}
|
|
||||||
{state === 'warning' && (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<A
|
|
||||||
href={NETWORK_REQUIREMENTS_DOC_URL}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
{t('summary.warningDocLink')}
|
|
||||||
</A>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{stats.hasStarted && (
|
{stats.hasStarted && (
|
||||||
|
|||||||
@@ -53,9 +53,6 @@ const fromCheckInfo = (info: CheckInfo): Partial<ConnectionTestStepResult> => ({
|
|||||||
status: CHECK_STATUS_TO_STEP[info.status] ?? 'failed',
|
status: CHECK_STATUS_TO_STEP[info.status] ?? 'failed',
|
||||||
summary: info.description,
|
summary: info.description,
|
||||||
logs: info.logs,
|
logs: info.logs,
|
||||||
// Only SelectedCandidateCheck sets `data` (the ICE candidate report); for
|
|
||||||
// the stock LiveKit checks it is undefined and this is a no-op.
|
|
||||||
data: info.data as Record<string, unknown> | undefined,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const groupDevicesByKind = (devices: MediaDeviceInfo[]) => {
|
const groupDevicesByKind = (devices: MediaDeviceInfo[]) => {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { css } from '@/styled-system/css'
|
|||||||
import { Center, VStack } from '@/styled-system/jsx'
|
import { Center, VStack } from '@/styled-system/jsx'
|
||||||
import { Permissions } from '@/features/rooms/components/Permissions'
|
import { Permissions } from '@/features/rooms/components/Permissions'
|
||||||
import { useConnectionTestRunner } from '../hooks/useConnectionTestRunner'
|
import { useConnectionTestRunner } from '../hooks/useConnectionTestRunner'
|
||||||
import { isSuboptimalRoute } from '../checks/selectedCandidate'
|
|
||||||
import { ConnectionTestStepRow } from '../components/ConnectionTestStepRow'
|
import { ConnectionTestStepRow } from '../components/ConnectionTestStepRow'
|
||||||
import { ConnectionTestSummary } from '../components/ConnectionTestSummary'
|
import { ConnectionTestSummary } from '../components/ConnectionTestSummary'
|
||||||
import { CONNECTION_TEST_GROUPS, summarizeSteps } from '../types'
|
import { CONNECTION_TEST_GROUPS, summarizeSteps } from '../types'
|
||||||
@@ -71,13 +70,6 @@ const ConnectionTest = () => {
|
|||||||
() => new Map(steps.map((step) => [step.id, step] as const)),
|
() => new Map(steps.map((step) => [step.id, step] as const)),
|
||||||
[steps]
|
[steps]
|
||||||
)
|
)
|
||||||
// The summary headline downgrades to a warning when media flows but over a
|
|
||||||
// fallback route: TURN over TCP/TLS, or anything else that is not UDP.
|
|
||||||
const routeWarning = useMemo(() => {
|
|
||||||
const step = stepsById.get('selectedCandidate')
|
|
||||||
return step?.status === 'success' && isSuboptimalRoute(step.data)
|
|
||||||
}, [stepsById])
|
|
||||||
|
|
||||||
const isPublishVideoRunning =
|
const isPublishVideoRunning =
|
||||||
stepsById.get('publishVideo')?.status === 'running'
|
stepsById.get('publishVideo')?.status === 'running'
|
||||||
|
|
||||||
@@ -107,11 +99,7 @@ const ConnectionTest = () => {
|
|||||||
<CenteredContent withBackButton>
|
<CenteredContent withBackButton>
|
||||||
<Center>
|
<Center>
|
||||||
<VStack gap="1.5rem" maxWidth="40rem" width="100%">
|
<VStack gap="1.5rem" maxWidth="40rem" width="100%">
|
||||||
<ConnectionTestSummary
|
<ConnectionTestSummary stats={stats} isRunning={isRunning}>
|
||||||
stats={stats}
|
|
||||||
isRunning={isRunning}
|
|
||||||
routeWarning={routeWarning}
|
|
||||||
>
|
|
||||||
{isRunning ? (
|
{isRunning ? (
|
||||||
// A disabled "run" button while the test runs is dead weight:
|
// A disabled "run" button while the test runs is dead weight:
|
||||||
// cancelling is the only thing left to do.
|
// cancelling is the only thing left to do.
|
||||||
|
|||||||
@@ -46,9 +46,6 @@
|
|||||||
"passedHint": "Ihr Browser, Ihre Geräte und Ihr Netzwerk sind für eine Besprechung bereit.",
|
"passedHint": "Ihr Browser, Ihre Geräte und Ihr Netzwerk sind für eine Besprechung bereit.",
|
||||||
"partial": "Teilweiser Test",
|
"partial": "Teilweiser Test",
|
||||||
"partialHint": "Einige Prüfungen wurden übersprungen. Erlauben Sie den Zugriff auf Ihre Kamera und Ihr Mikrofon, um diese zu testen.",
|
"partialHint": "Einige Prüfungen wurden übersprungen. Erlauben Sie den Zugriff auf Ihre Kamera und Ihr Mikrofon, um diese zu testen.",
|
||||||
"warning": "Verbindung nicht optimal",
|
|
||||||
"warningHint": "Achtung: Sie befinden sich nicht in optimalen Bedingungen für die Nutzung des Tools. Ihre Medien werden nicht über UDP übertragen (Rückgriff auf ein TURN-Relay über TCP oder TLS), was die Audio- und Videoqualität beeinträchtigen kann.",
|
|
||||||
"warningDocLink": "Technische Dokumentation für Ihre IT-Abteilung",
|
|
||||||
"failed_one": "{{count}} Prüfung fehlgeschlagen",
|
"failed_one": "{{count}} Prüfung fehlgeschlagen",
|
||||||
"failed_other": "{{count}} Prüfungen fehlgeschlagen",
|
"failed_other": "{{count}} Prüfungen fehlgeschlagen",
|
||||||
"failedHint": "Öffnen Sie die fehlgeschlagenen Prüfungen für weitere Details und senden Sie den Bericht an Ihre IT-Abteilung."
|
"failedHint": "Öffnen Sie die fehlgeschlagenen Prüfungen für weitere Details und senden Sie den Bericht an Ihre IT-Abteilung."
|
||||||
|
|||||||
@@ -46,9 +46,6 @@
|
|||||||
"passedHint": "Your browser, your devices and your network are ready for a meeting.",
|
"passedHint": "Your browser, your devices and your network are ready for a meeting.",
|
||||||
"partial": "Partially tested",
|
"partial": "Partially tested",
|
||||||
"partialHint": "Some checks were skipped. Allow access to your camera and microphone to test them.",
|
"partialHint": "Some checks were skipped. Allow access to your camera and microphone to test them.",
|
||||||
"warning": "Suboptimal connection",
|
|
||||||
"warningHint": "Warning: you are not in optimal conditions to use the tool. Your media is not carried over UDP (it falls back to a TURN relay over TCP or TLS), which can degrade audio and video quality.",
|
|
||||||
"warningDocLink": "Technical documentation for your IT department",
|
|
||||||
"failed_one": "{{count}} check failed",
|
"failed_one": "{{count}} check failed",
|
||||||
"failed_other": "{{count}} checks failed",
|
"failed_other": "{{count}} checks failed",
|
||||||
"failedHint": "Open the failed checks below for details, then send the report to your IT department."
|
"failedHint": "Open the failed checks below for details, then send the report to your IT department."
|
||||||
|
|||||||
@@ -46,9 +46,6 @@
|
|||||||
"passedHint": "Votre navigateur, vos périphériques et votre réseau sont prêts pour une réunion.",
|
"passedHint": "Votre navigateur, vos périphériques et votre réseau sont prêts pour une réunion.",
|
||||||
"partial": "Test partiel",
|
"partial": "Test partiel",
|
||||||
"partialHint": "Certaines vérifications ont été ignorées. Autorisez l'accès à votre caméra et à votre microphone pour les tester.",
|
"partialHint": "Certaines vérifications ont été ignorées. Autorisez l'accès à votre caméra et à votre microphone pour les tester.",
|
||||||
"warning": "Connexion non optimale",
|
|
||||||
"warningHint": "Attention : vous n'êtes pas dans les conditions optimales pour accéder à l'outil. Vos flux ne transitent pas en UDP (repli sur un relais TURN en TCP ou TLS), ce qui peut dégrader la qualité audio et vidéo.",
|
|
||||||
"warningDocLink": "Documentation technique à destination de votre service informatique",
|
|
||||||
"failed_one": "{{count}} vérification en échec",
|
"failed_one": "{{count}} vérification en échec",
|
||||||
"failed_other": "{{count}} vérifications en échec",
|
"failed_other": "{{count}} vérifications en échec",
|
||||||
"failedHint": "Ouvrez les vérifications en échec pour voir le détail, puis transmettez le rapport à votre service informatique."
|
"failedHint": "Ouvrez les vérifications en échec pour voir le détail, puis transmettez le rapport à votre service informatique."
|
||||||
|
|||||||
@@ -46,9 +46,6 @@
|
|||||||
"passedHint": "Je browser, apparaten en netwerk zijn klaar voor een vergadering.",
|
"passedHint": "Je browser, apparaten en netwerk zijn klaar voor een vergadering.",
|
||||||
"partial": "Gedeeltelijke test",
|
"partial": "Gedeeltelijke test",
|
||||||
"partialHint": "Sommige controles zijn overgeslagen. Geef toegang tot je camera en microfoon om deze te testen.",
|
"partialHint": "Sommige controles zijn overgeslagen. Geef toegang tot je camera en microfoon om deze te testen.",
|
||||||
"warning": "Verbinding niet optimaal",
|
|
||||||
"warningHint": "Let op: je bevindt je niet in optimale omstandigheden om de tool te gebruiken. Je media loopt niet via UDP (terugval op een TURN-relay via TCP of TLS), wat de audio- en videokwaliteit kan verminderen.",
|
|
||||||
"warningDocLink": "Technische documentatie voor je IT-afdeling",
|
|
||||||
"failed_one": "{{count}} controle mislukt",
|
"failed_one": "{{count}} controle mislukt",
|
||||||
"failed_other": "{{count}} controles mislukt",
|
"failed_other": "{{count}} controles mislukt",
|
||||||
"failedHint": "Open de mislukte controles voor meer details en stuur het rapport door naar je IT-afdeling."
|
"failedHint": "Open de mislukte controles voor meer details en stuur het rapport door naar je IT-afdeling."
|
||||||
|
|||||||
Reference in New Issue
Block a user