swarm-ui: let a closed stream reconnect and a failed caps read retry
When the server answers an event stream with an error status the browser gives up and the badge stayed on disconnected with nothing to press. The terminal, its state header and the messages page now offer a reconnect, and the one rule for the state after an error moved into a tested helper. The resource caps panel gets a retry beside its error, and its loading state announces itself.
This commit is contained in:
parent
9b1fe85706
commit
31b7452e56
9 changed files with 118 additions and 34 deletions
|
|
@ -50,7 +50,7 @@ const MessageRow = memo(function MessageRow({ m }: { m: ArrivedMessage }) {
|
|||
export function MessagesPage() {
|
||||
const [sinceMinutes, setSinceMinutes] = useState(DEFAULT_WINDOW_MINUTES);
|
||||
const [query, setQuery] = useState("");
|
||||
const { messages, connection } = useMessageStream(sinceMinutes);
|
||||
const { messages, connection, reconnect } = useMessageStream(sinceMinutes);
|
||||
const shown = filterMessages(messages, query);
|
||||
|
||||
const logRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -78,10 +78,20 @@ export function MessagesPage() {
|
|||
title="messages"
|
||||
icon="💬"
|
||||
actions={
|
||||
<Badge
|
||||
tone={CONNECTION_TONE[connection]}
|
||||
value={CONNECTION_LABEL[connection]}
|
||||
/>
|
||||
<>
|
||||
<Badge
|
||||
tone={CONNECTION_TONE[connection]}
|
||||
value={CONNECTION_LABEL[connection]}
|
||||
/>
|
||||
{connection === "closed" ? (
|
||||
<Badge
|
||||
variant="quiet"
|
||||
value="reconnect"
|
||||
title="open the message stream again"
|
||||
onClick={reconnect}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div class="ui-messages-bar">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,12 @@ import { useEffect, useRef, useState } from "preact/hooks";
|
|||
import type { ArrivedMessage, SwarmMessage } from "./messages.js";
|
||||
import { messagesUrl, newest, numbered } from "./messages.js";
|
||||
|
||||
export type ConnectionState = "connecting" | "open" | "reconnecting" | "closed";
|
||||
import {
|
||||
connectionAfterError,
|
||||
type ConnectionState,
|
||||
} from "../../ui/stream/connection.js";
|
||||
|
||||
export type { ConnectionState };
|
||||
|
||||
/** The most messages the page holds; older ones drop off. */
|
||||
export const MAX_MESSAGES = 2000;
|
||||
|
|
@ -19,12 +24,16 @@ export const MAX_MESSAGES = 2000;
|
|||
export function useMessageStream(sinceMinutes: number): {
|
||||
messages: ArrivedMessage[];
|
||||
connection: ConnectionState;
|
||||
/** Opens the stream again, for one the browser gave up on. */
|
||||
reconnect: () => void;
|
||||
} {
|
||||
const [messages, setMessages] = useState<ArrivedMessage[]>([]);
|
||||
const [connection, setConnection] = useState<ConnectionState>("connecting");
|
||||
// The next id to hand out; it never goes back, so an id is not reused after
|
||||
// a reconnect either.
|
||||
const nextId = useRef(0);
|
||||
// Bumped by `reconnect`, which reruns the effect below.
|
||||
const [attempt, setAttempt] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setMessages([]);
|
||||
|
|
@ -61,16 +70,12 @@ export function useMessageStream(sinceMinutes: number): {
|
|||
pending.push(m);
|
||||
if (frame === null) frame = requestAnimationFrame(flush);
|
||||
};
|
||||
es.onerror = () => {
|
||||
setConnection(
|
||||
es.readyState === 0 /* CONNECTING */ ? "reconnecting" : "closed",
|
||||
);
|
||||
};
|
||||
es.onerror = () => setConnection(connectionAfterError(es.readyState));
|
||||
return () => {
|
||||
drop();
|
||||
es.close();
|
||||
};
|
||||
}, [sinceMinutes]);
|
||||
}, [sinceMinutes, attempt]);
|
||||
|
||||
return { messages, connection };
|
||||
return { messages, connection, reconnect: () => setAttempt((n) => n + 1) };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue