gsmol/web/js/transport.js
randogoth 84397a3fdd feat: restructure settings into identity, server, backup and advanced tabs
Drops the per-server pin list down to the one server your account is
bound to, adds an enforced network timeout, and replaces the global
status toast inside settings with inline feedback and a reusable
confirm dialog for rotate/log out. Mail-fetching preferences move into
the server tab; the now-superseded claim/recall forms are gone since
onboarding covers both.
2026-10-08 21:24:05 +03:00

29 lines
1.3 KiB
JavaScript

// The byte pipe to a smolmaild server. Browsers cannot open TCP sockets, so
// bytes travel through the local bridge's WebSocket relay unchanged; the
// Noise session lives in this page either way.
const BRIDGE = `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}`;
export function connectStream(host, port, timeoutMs = 0) {
return new Promise((resolve, reject) => {
const socket = new WebSocket(`${BRIDGE}/tcp/${host}/${port}`);
socket.binaryType = "arraybuffer";
const stream = {
send: bytes => socket.send(bytes),
close: () => socket.close(),
onData: null,
onClose: null,
};
// Settings' timeout only governs this; the WebSocket may otherwise never
// fire open, error, or close if something between here and the bridge
// just drops packets.
const timer = timeoutMs ? setTimeout(() => {
socket.close();
reject(new Error(`could not reach ${host}:${port} within ${timeoutMs / 1000}s`));
}, timeoutMs) : null;
socket.onopen = () => { clearTimeout(timer); resolve(stream); };
socket.onerror = () => { clearTimeout(timer); reject(new Error(`cannot reach the bridge at ${BRIDGE}`)); };
socket.onmessage = event => stream.onData?.(new Uint8Array(event.data));
socket.onclose = () => stream.onClose?.();
});
}