mirror of
https://github.com/vector-im/element-call.git
synced 2026-09-22 22:29:30 +00:00
remove disconnect banner from header (we have the in call reconnect
view)
This commit is contained in:
@@ -88,7 +88,7 @@ builds the view model with
|
|||||||
| Analytics | `src/analytics/PosthogEvents.ts`, `PosthogAnalytics.ts` | `rtcSession.statistics`, account data |
|
| Analytics | `src/analytics/PosthogEvents.ts`, `PosthogAnalytics.ts` | `rtcSession.statistics`, account data |
|
||||||
| Types only | `src/UrlParams.ts`, `src/state/MediaDevices.ts`, `AndroidControlledAudioOutput.ts`, `IOSControlledAudioOutput.ts`, `initialMuteState.ts`, `state/media/RingingMediaViewModel.ts` (`RTCCallIntent`), `src/useEvents.ts` (`TypedEventEmitter` types) | replaced by a local `CallIntent` type / kept as generic emitter typing |
|
| Types only | `src/UrlParams.ts`, `src/state/MediaDevices.ts`, `AndroidControlledAudioOutput.ts`, `IOSControlledAudioOutput.ts`, `initialMuteState.ts`, `state/media/RingingMediaViewModel.ts` (`RTCCallIntent`), `src/useEvents.ts` (`TypedEventEmitter` types) | replaced by a local `CallIntent` type / kept as generic emitter typing |
|
||||||
| Runtime misc | `src/useLocalStorage.ts` (`TypedEventEmitter`), `src/room/GroupCallErrorBoundary.tsx` (`MatrixError`), `src/room/KnockLobbyView.tsx` (shell) | see S6 |
|
| Runtime misc | `src/useLocalStorage.ts` (`TypedEventEmitter`), `src/room/GroupCallErrorBoundary.tsx` (`MatrixError`), `src/room/KnockLobbyView.tsx` (shell) | see S6 |
|
||||||
| Context | `src/ClientContext.tsx` | `useClient`/`useClientState` used by `Avatar`, `sharedKeyManagement`, `useReactionsSender`, `submit-rageshake`, `DisconnectedBanner` |
|
| Context | `src/ClientContext.tsx` | `useClient`/`useClientState` used by `Avatar`, `sharedKeyManagement`, `useReactionsSender`, `submit-rageshake` |
|
||||||
|
|
||||||
Hosts: `component/index.tsx:291`, `src/room/useLoadGroupCall.ts:335`,
|
Hosts: `component/index.tsx:291`, `src/room/useLoadGroupCall.ts:335`,
|
||||||
`sdk/main.ts:128` (own `MatrixRTCSessionManager`; waits on
|
`sdk/main.ts:128` (own `MatrixRTCSessionManager`; waits on
|
||||||
@@ -433,7 +433,6 @@ MSC4143: sticky member events, slots, the spec key message), in
|
|||||||
rageshake requests via `subscribeTimeline`.
|
rageshake requests via `subscribeTimeline`.
|
||||||
- `DeveloperSettingsTab`: sticky probe → `getCapabilities()`; custom LiveKit
|
- `DeveloperSettingsTab`: sticky probe → `getCapabilities()`; custom LiveKit
|
||||||
URL validation → `driver.getLivekitToken(...)`.
|
URL validation → `driver.getLivekitToken(...)`.
|
||||||
- `DisconnectedBanner` → `HomeserverUnreachable` in `status$` (C12).
|
|
||||||
- `window.rtcSession` debug handle → `window.matrixRtc = { participation }`.
|
- `window.rtcSession` debug handle → `window.matrixRtc = { participation }`.
|
||||||
|
|
||||||
### 4.5 Hosts
|
### 4.5 Hosts
|
||||||
@@ -737,7 +736,7 @@ participation, clientDriver, …)` sits next to it; both build a
|
|||||||
`VideoPreview.tsx`, `useRoomInfo()` (replaces `useRoomName/Avatar/JoinRule/State`),
|
`VideoPreview.tsx`, `useRoomInfo()` (replaces `useRoomName/Avatar/JoinRule/State`),
|
||||||
`InviteModal.tsx`, `Avatar.tsx`, `useOwnProfile.ts`, `ProfileSettingsTab.tsx`,
|
`InviteModal.tsx`, `Avatar.tsx`, `useOwnProfile.ts`, `ProfileSettingsTab.tsx`,
|
||||||
`SettingsModal.tsx`, `DeveloperSettingsTab.tsx`, `submit-rageshake.ts`,
|
`SettingsModal.tsx`, `DeveloperSettingsTab.tsx`, `submit-rageshake.ts`,
|
||||||
`DisconnectedBanner.tsx`, `analytics/PosthogEvents.ts`, `controls.ts`, and a
|
`analytics/PosthogEvents.ts`, `controls.ts`, and a
|
||||||
first `CallView.stories.tsx` (lobby, in call, ended) driven by
|
first `CallView.stories.tsx` (lobby, in call, ended) driven by
|
||||||
`MockMatrixDriver`.
|
`MockMatrixDriver`.
|
||||||
- **S4b ☑ (2026-09-16):** `ReactionsSenderProvider` takes `ownIdentifier`,
|
- **S4b ☑ (2026-09-16):** `ReactionsSenderProvider` takes `ownIdentifier`,
|
||||||
@@ -785,13 +784,14 @@ roomInfo.encrypted)` (`useRoomEncryptionSystem` keeps the client for the
|
|||||||
client driver's public `client`/`room` are gone. **Consequence:** the
|
client driver's public `client`/`room` are gone. **Consequence:** the
|
||||||
component and its dev harness (`ElementCallClientBased`) now run every
|
component and its dev harness (`ElementCallClientBased`) now run every
|
||||||
call on the crate.
|
call on the crate.
|
||||||
- **Banner on the driver (2026-09-16):** `useHomeserverConnected(drivers,
|
- **Banner dropped (2026-09-17):** the disconnected banner, its
|
||||||
graceMs)` (`src/driver/`) follows `rtcDriver.isHomeserverConnected()` /
|
`useHomeserverConnected` hook and the client state's `disconnected` flag
|
||||||
`subscribeConnectivity` and reports a lapse only after
|
are gone. The local membership already carries the connection state, so
|
||||||
`sync_disconnect_grace_period_ms`, since the driver reports every sync
|
the reconnecting overlay covers the in-call case where it matters; in the
|
||||||
hiccup where the client state waited for a `ConnectionError`;
|
lobby the banner added little. `HomeserverConnected` in
|
||||||
`DisconnectedBanner` uses it whenever drivers are provided and falls back
|
`state/CallViewModel/localMember/` remains the one consumer of
|
||||||
to the client state for the shell. The mock RTC driver keeps several
|
`rtcDriver.isHomeserverConnected()` / `subscribeConnectivity` and of
|
||||||
|
`sync_disconnect_grace_period_ms`. The mock RTC driver keeps several
|
||||||
connectivity sinks (the crate's and the UI's).
|
connectivity sinks (the crate's and the UI's).
|
||||||
- **S4 closed (2026-09-16):** `CallView.stories.tsx` (Lobby with a peer,
|
- **S4 closed (2026-09-16):** `CallView.stories.tsx` (Lobby with a peer,
|
||||||
NoTransport as the error path, Ended) over the mock drivers, no client;
|
NoTransport as the error path, Ended) over the mock drivers, no client;
|
||||||
|
|||||||
@@ -102,7 +102,6 @@
|
|||||||
"show_connection_stats": "Show connection statistics",
|
"show_connection_stats": "Show connection statistics",
|
||||||
"url_params": "URL parameters"
|
"url_params": "URL parameters"
|
||||||
},
|
},
|
||||||
"disconnected_banner": "Connectivity to the server has been lost.",
|
|
||||||
"error": {
|
"error": {
|
||||||
"call_is_not_supported": "Call is not supported",
|
"call_is_not_supported": "Call is not supported",
|
||||||
"call_not_found": "Call not found",
|
"call_not_found": "Call not found",
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ const TestComponent: FC<
|
|||||||
<ClientContextProvider
|
<ClientContextProvider
|
||||||
value={{
|
value={{
|
||||||
state: "valid",
|
state: "valid",
|
||||||
disconnected: false,
|
|
||||||
supportedFeatures: {
|
supportedFeatures: {
|
||||||
reactions: true,
|
reactions: true,
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-32
@@ -17,8 +17,7 @@ import {
|
|||||||
type JSX,
|
type JSX,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { type ISyncStateData, type SyncState } from "matrix-js-sdk/lib/sync";
|
import { type MatrixClient } from "matrix-js-sdk";
|
||||||
import { ClientEvent, type MatrixClient } from "matrix-js-sdk";
|
|
||||||
|
|
||||||
import { ErrorPage } from "./FullScreenView";
|
import { ErrorPage } from "./FullScreenView";
|
||||||
import { useHostBridge } from "./HostBridge";
|
import { useHostBridge } from "./HostBridge";
|
||||||
@@ -42,9 +41,6 @@ export type ClientState = ValidClientState | ErrorState;
|
|||||||
export type ValidClientState = {
|
export type ValidClientState = {
|
||||||
state: "valid";
|
state: "valid";
|
||||||
authenticated?: AuthenticatedClient;
|
authenticated?: AuthenticatedClient;
|
||||||
// 'Disconnected' rather than 'connected' because it tracks specifically
|
|
||||||
// whether the client is supposed to be connected but is not
|
|
||||||
disconnected: boolean;
|
|
||||||
supportedFeatures: {
|
supportedFeatures: {
|
||||||
reactions: boolean;
|
reactions: boolean;
|
||||||
};
|
};
|
||||||
@@ -276,7 +272,6 @@ export const ClientProvider: FC<Props> = ({ children, client }) => {
|
|||||||
}, [initClientState?.client, setAlreadyOpenedErr]),
|
}, [initClientState?.client, setAlreadyOpenedErr]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isDisconnected, setIsDisconnected] = useState(false);
|
|
||||||
const [supportsReactions, setSupportsReactions] = useState(false);
|
const [supportsReactions, setSupportsReactions] = useState(false);
|
||||||
|
|
||||||
const state: ClientState | undefined = useMemo(() => {
|
const state: ClientState | undefined = useMemo(() => {
|
||||||
@@ -300,7 +295,6 @@ export const ClientProvider: FC<Props> = ({ children, client }) => {
|
|||||||
state: "valid",
|
state: "valid",
|
||||||
authenticated,
|
authenticated,
|
||||||
setClient,
|
setClient,
|
||||||
disconnected: isDisconnected,
|
|
||||||
supportedFeatures: {
|
supportedFeatures: {
|
||||||
reactions: supportsReactions,
|
reactions: supportsReactions,
|
||||||
},
|
},
|
||||||
@@ -311,17 +305,9 @@ export const ClientProvider: FC<Props> = ({ children, client }) => {
|
|||||||
initClientState,
|
initClientState,
|
||||||
logout,
|
logout,
|
||||||
setClient,
|
setClient,
|
||||||
isDisconnected,
|
|
||||||
supportsReactions,
|
supportsReactions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onSync = useCallback(
|
|
||||||
(state: SyncState, _old: SyncState | null, data?: ISyncStateData) => {
|
|
||||||
setIsDisconnected(clientIsDisconnected(state, data));
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initClientState) {
|
if (!initClientState) {
|
||||||
return;
|
return;
|
||||||
@@ -333,20 +319,10 @@ export const ClientProvider: FC<Props> = ({ children, client }) => {
|
|||||||
if (PosthogAnalytics.hasInstance())
|
if (PosthogAnalytics.hasInstance())
|
||||||
PosthogAnalytics.instance.onLoginStatusChanged();
|
PosthogAnalytics.instance.onLoginStatusChanged();
|
||||||
|
|
||||||
if (initClientState.client) {
|
|
||||||
initClientState.client.on(ClientEvent.Sync, onSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hostBridge.supportsReactions)
|
if (!hostBridge.supportsReactions)
|
||||||
logger.warn("The host does not permit reactions");
|
logger.warn("The host does not permit reactions");
|
||||||
setSupportsReactions(hostBridge.supportsReactions);
|
setSupportsReactions(hostBridge.supportsReactions);
|
||||||
|
}, [initClientState, hostBridge]);
|
||||||
return (): void => {
|
|
||||||
if (initClientState.client) {
|
|
||||||
initClientState.client.removeListener(ClientEvent.Sync, onSync);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [initClientState, onSync, hostBridge]);
|
|
||||||
|
|
||||||
if (alreadyOpenedErr) {
|
if (alreadyOpenedErr) {
|
||||||
return <ErrorPage error={alreadyOpenedErr} />;
|
return <ErrorPage error={alreadyOpenedErr} />;
|
||||||
@@ -388,9 +364,3 @@ const loadSession = (): Session | undefined => {
|
|||||||
|
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clientIsDisconnected = (
|
|
||||||
syncState: SyncState,
|
|
||||||
syncData?: ISyncStateData,
|
|
||||||
): boolean =>
|
|
||||||
syncState === "ERROR" && syncData?.error?.name === "ConnectionError";
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.banner {
|
|
||||||
position: absolute;
|
|
||||||
padding: 29px;
|
|
||||||
background-color: var(--cpd-color-bg-subtle-primary);
|
|
||||||
vertical-align: middle;
|
|
||||||
font-size: var(--font-size-body);
|
|
||||||
text-align: center;
|
|
||||||
z-index: 1;
|
|
||||||
top: 76px;
|
|
||||||
width: calc(100% - 58px);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import classNames from "classnames";
|
|
||||||
import { type FC, type HTMLAttributes, type ReactNode } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import styles from "./DisconnectedBanner.module.css";
|
|
||||||
import { type ValidClientState, useClientState } from "./ClientContext";
|
|
||||||
import { useOptionalMatrixDrivers } from "./driver/MatrixDriverContext";
|
|
||||||
import { useHomeserverConnected } from "./driver/useHomeserverConnected";
|
|
||||||
|
|
||||||
interface Props extends HTMLAttributes<HTMLElement> {
|
|
||||||
children?: ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DisconnectedBanner: FC<Props> = ({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
// Under a call the host's RTC driver says whether the homeserver is
|
|
||||||
// reachable; the shell outside a call only has the client's sync state.
|
|
||||||
const drivers = useOptionalMatrixDrivers();
|
|
||||||
const homeserverConnected = useHomeserverConnected(drivers);
|
|
||||||
const clientState = useClientState();
|
|
||||||
let shouldShowBanner = false;
|
|
||||||
|
|
||||||
if (drivers !== null) {
|
|
||||||
shouldShowBanner = !homeserverConnected;
|
|
||||||
} else if (clientState?.state === "valid") {
|
|
||||||
const validClientState = clientState as ValidClientState;
|
|
||||||
shouldShowBanner = validClientState.disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{shouldShowBanner && (
|
|
||||||
<div className={classNames(styles.banner, className)} {...rest}>
|
|
||||||
{children}
|
|
||||||
{t("disconnected_banner")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+3
-18
@@ -23,38 +23,23 @@ import { Avatar, Size } from "./Avatar";
|
|||||||
import { EncryptionLock } from "./room/EncryptionLock";
|
import { EncryptionLock } from "./room/EncryptionLock";
|
||||||
import { useRootSizeMatches } from "./useRootSize";
|
import { useRootSizeMatches } from "./useRootSize";
|
||||||
import { useLeaveToHome } from "./LeaveToHomeContext";
|
import { useLeaveToHome } from "./LeaveToHomeContext";
|
||||||
import { DisconnectedBanner } from "./DisconnectedBanner";
|
|
||||||
|
|
||||||
interface HeaderProps extends HTMLAttributes<HTMLElement> {
|
interface HeaderProps extends HTMLAttributes<HTMLElement> {
|
||||||
ref?: Ref<HTMLElement>;
|
ref?: Ref<HTMLElement>;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
/**
|
|
||||||
* Whether the header should display an informational banner whenever the
|
|
||||||
* client is disconnected from the homeserver.
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
disconnectedBanner?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header: FC<HeaderProps> = ({
|
export const Header: FC<HeaderProps> = ({
|
||||||
ref,
|
ref,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
disconnectedBanner = true,
|
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<header ref={ref} className={classNames(styles.header, className)} {...rest}>
|
||||||
<header
|
{children}
|
||||||
ref={ref}
|
</header>
|
||||||
className={classNames(styles.header, className)}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</header>
|
|
||||||
{disconnectedBanner && <DisconnectedBanner />}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2026 Element Creations Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { act, renderHook } from "@testing-library/react";
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
import { type MatrixDrivers } from "./MatrixDriverContext";
|
|
||||||
import { MockElementCallMatrixClientDriver } from "./MockElementCallMatrixClientDriver";
|
|
||||||
import { MockRtcMatrixDriver } from "./MockRtcMatrixDriver";
|
|
||||||
import { useHomeserverConnected } from "./useHomeserverConnected";
|
|
||||||
|
|
||||||
describe("useHomeserverConnected", () => {
|
|
||||||
beforeEach(() => vi.useFakeTimers());
|
|
||||||
afterEach(() => vi.useRealTimers());
|
|
||||||
|
|
||||||
it("reports a lapse only after the grace period, and a return at once", () => {
|
|
||||||
const rtcDriver = new MockRtcMatrixDriver();
|
|
||||||
const drivers: MatrixDrivers = {
|
|
||||||
rtcDriver,
|
|
||||||
clientDriver: new MockElementCallMatrixClientDriver(),
|
|
||||||
};
|
|
||||||
const { result } = renderHook(() => useHomeserverConnected(drivers, 1000));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
|
|
||||||
act(() => rtcDriver.setHomeserverConnected(false));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
act(() => vi.advanceTimersByTime(999));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
act(() => vi.advanceTimersByTime(1));
|
|
||||||
expect(result.current).toBe(false);
|
|
||||||
|
|
||||||
act(() => rtcDriver.setHomeserverConnected(true));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
|
|
||||||
// A blip shorter than the grace period is never shown.
|
|
||||||
act(() => rtcDriver.setHomeserverConnected(false));
|
|
||||||
act(() => vi.advanceTimersByTime(500));
|
|
||||||
act(() => rtcDriver.setHomeserverConnected(true));
|
|
||||||
act(() => vi.advanceTimersByTime(1000));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("starts from the driver's current answer", () => {
|
|
||||||
const rtcDriver = new MockRtcMatrixDriver();
|
|
||||||
rtcDriver.setHomeserverConnected(false);
|
|
||||||
const drivers: MatrixDrivers = {
|
|
||||||
rtcDriver,
|
|
||||||
clientDriver: new MockElementCallMatrixClientDriver(),
|
|
||||||
};
|
|
||||||
const { result } = renderHook(() => useHomeserverConnected(drivers, 1000));
|
|
||||||
act(() => vi.advanceTimersByTime(1000));
|
|
||||||
expect(result.current).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("is connected without drivers", () => {
|
|
||||||
const { result } = renderHook(() => useHomeserverConnected(null, 1000));
|
|
||||||
expect(result.current).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2026 Element Creations Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
import { Config } from "../config/Config";
|
|
||||||
import { type MatrixDrivers } from "./MatrixDriverContext";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the homeserver is reachable, from the RTC driver's connectivity
|
|
||||||
* (the same signal the crate turns into `HomeserverUnreachable`). The driver
|
|
||||||
* reports every lapse of the sync loop; the UI should not, so a lapse counts
|
|
||||||
* only once it has lasted `graceMs` (the deployment's
|
|
||||||
* `sync_disconnect_grace_period_ms` by default). Coming back counts at once.
|
|
||||||
*
|
|
||||||
* `null` drivers (a host that provided none) read as connected: there is
|
|
||||||
* nothing to report on.
|
|
||||||
*/
|
|
||||||
export function useHomeserverConnected(
|
|
||||||
drivers: MatrixDrivers | null,
|
|
||||||
graceMs: number = Config.get().sync_disconnect_grace_period_ms,
|
|
||||||
): boolean {
|
|
||||||
const [connected, setConnected] = useState(true);
|
|
||||||
useEffect(() => {
|
|
||||||
if (drivers === null) {
|
|
||||||
setConnected(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { rtcDriver } = drivers;
|
|
||||||
let live = true;
|
|
||||||
let lapse: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
const report = (isConnected: boolean): void => {
|
|
||||||
if (lapse !== null) {
|
|
||||||
clearTimeout(lapse);
|
|
||||||
lapse = null;
|
|
||||||
}
|
|
||||||
if (isConnected) setConnected(true);
|
|
||||||
else lapse = setTimeout(() => setConnected(false), graceMs);
|
|
||||||
};
|
|
||||||
report(rtcDriver.isHomeserverConnected());
|
|
||||||
rtcDriver.subscribeConnectivity({
|
|
||||||
emit: (isConnected) => {
|
|
||||||
if (!live) return false;
|
|
||||||
report(isConnected);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (): void => {
|
|
||||||
live = false;
|
|
||||||
if (lapse !== null) clearTimeout(lapse);
|
|
||||||
};
|
|
||||||
}, [drivers, graceMs]);
|
|
||||||
return connected;
|
|
||||||
}
|
|
||||||
@@ -505,7 +505,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
[styles.hidden]: !showHeader,
|
[styles.hidden]: !showHeader,
|
||||||
})}
|
})}
|
||||||
ref={headerRef}
|
ref={headerRef}
|
||||||
disconnectedBanner={false} // This screen has its own 'reconnecting' toast
|
|
||||||
>
|
>
|
||||||
<LeftNav>
|
<LeftNav>
|
||||||
<RoomHeaderInfo
|
<RoomHeaderInfo
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ function renderWithMockClient(
|
|||||||
<ClientContextProvider
|
<ClientContextProvider
|
||||||
value={{
|
value={{
|
||||||
state: "valid",
|
state: "valid",
|
||||||
disconnected: false,
|
|
||||||
supportedFeatures: {
|
supportedFeatures: {
|
||||||
reactions: true,
|
reactions: true,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user