fix(lotus): explain a failed voice connection and offer Try again

"Failed to connect to Livekit server (Reason: ServerUnreachable)" left a
user with nothing to try; they retried for a long time and gave up.

- Title "Couldn't connect to voice". The message depends on the reason:
  unreachable/timeout-style errors explain that voice needs its own live
  connection, which VPNs, antivirus web shields and some networks block,
  even when chat works, and say what to try; NotAllowed says the call may
  be full or access changed.
- The raw reason, status and message move under Technical details.
- LivekitConnectionError and PeerConnectionTimeoutError now get a
  Try again button (the existing reconnect path), shown as primary: the
  Lotus theme renders `secondary` as dark-on-dark.

Verified in the Lotus client against a local LiveKit with the signal
socket and validate request blocked: the dialog appears after ~20 s;
unblocking and pressing Try again joins with the mic published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-23 20:51:43 -04:00
co-authored by Claude Opus 5.5
parent e9723a21b5
commit 3e69a18a39
5 changed files with 214 additions and 75 deletions
+19 -4
View File
@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { t } from "i18next";
import { type ConnectionError } from "livekit-client";
import { type ConnectionError, ConnectionErrorReason } from "livekit-client";
import { i18nKey } from "./i18n";
@@ -53,6 +53,9 @@ export class ElementCallError extends Error {
public localisedMessageKey?: string;
public localisedMessageValues?: Record<string, string>;
/** [lotus] Raw detail shown under a collapsed "Technical details", not in the message. */
public technicalDetails?: string;
protected constructor(
localisedTitle: string,
code: ErrorCode,
@@ -307,9 +310,21 @@ export class LivekitConnectionError extends ElementCallError {
ErrorCode.SFU_ERROR,
ErrorCategory.NETWORK_CONNECTIVITY,
);
this.localisedMessageKey = i18nKey(
"error.livekit_connection_error_description",
);
// [lotus] "An error occurred while connecting to the Livekit server
// (Reason: ServerUnreachable)" left people with nothing to try (Gitea
// element-call: a friend retried for ages while Element worked). Say what
// it means and what to do; keep the raw reason under Technical details.
this.localisedMessageKey =
cause.reason === ConnectionErrorReason.NotAllowed
? i18nKey("error.livekit_not_allowed_description")
: i18nKey("error.livekit_unreachable_description");
this.localisedMessageValues = { reason: cause.reasonName };
this.technicalDetails = [
`Reason: ${cause.reasonName}`,
cause.status ? `Status: ${cause.status}` : null,
cause.message,
]
.filter(Boolean)
.join("\n");
}
}