fixes
This commit is contained in:
@@ -2809,7 +2809,7 @@ dialog::backdrop {
|
||||
z-index: 55;
|
||||
left: 0;
|
||||
bottom: calc(100% + 9px);
|
||||
width: min(430px, calc(100vw - 24px));
|
||||
width: min(400px, calc(100vw - 24px));
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 10px;
|
||||
@@ -5297,7 +5297,7 @@ dialog::backdrop {
|
||||
right: 12px;
|
||||
bottom: calc(64px + env(safe-area-inset-bottom, 0px));
|
||||
left: auto;
|
||||
width: min(430px, calc(100vw - 24px));
|
||||
width: min(400px, calc(100vw - 24px));
|
||||
max-height: calc(100dvh - 88px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
+6
-38
@@ -173,7 +173,7 @@
|
||||
<div class="connection-diagnostics-popover">
|
||||
<strong>Connection diagnostics</strong>
|
||||
<dl class="connection-diagnostics-grid">
|
||||
<div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Quality</dt>
|
||||
<dd data-connection-diagnostic="quality">Waiting</dd>
|
||||
</div>
|
||||
@@ -193,26 +193,10 @@
|
||||
<dt>Reconnects</dt>
|
||||
<dd data-connection-diagnostic="reconnects">0</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Transport</dt>
|
||||
<dd data-connection-diagnostic="transport">WebSocket</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Heartbeat</dt>
|
||||
<dd data-connection-diagnostic="heartbeat">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Network</dt>
|
||||
<dd data-connection-diagnostic="network">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Client</dt>
|
||||
<dd data-connection-diagnostic="client">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Server</dt>
|
||||
<dd data-connection-diagnostic="server">—</dd>
|
||||
</div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Last event</dt>
|
||||
<dd data-connection-diagnostic="last-event">—</dd>
|
||||
@@ -292,9 +276,9 @@
|
||||
guest</button><button id="show-register" class="text-button" type="button">Register</button><button
|
||||
id="show-login" class="text-button" type="button">Log in</button></div>
|
||||
<section id="auth-panel" class="auth-panel" hidden>
|
||||
<h3 id="auth-mode-title">Log in</h3><label id="auth-email-field">E-mail / LDAP or AD Username<input
|
||||
<h3 id="auth-mode-title">Log in</h3><label id="auth-email-field"><span id="auth-email-label">E-mail</span><input
|
||||
id="auth-email" name="username" type="email" maxlength="320" autocomplete="username"
|
||||
placeholder="you@example.com or name.second-name"></label><label>Password<input
|
||||
placeholder="you@example.com"></label><label>Password<input
|
||||
id="auth-password" name="password" type="password" minlength="8" maxlength="128"
|
||||
autocomplete="current-password"></label><button id="auth-submit" class="primary-button"
|
||||
type="submit">Log in and continue</button>
|
||||
@@ -357,7 +341,7 @@
|
||||
<div class="connection-diagnostics-popover mobile-connection-diagnostics-popover">
|
||||
<strong>Connection diagnostics</strong>
|
||||
<dl class="connection-diagnostics-grid">
|
||||
<div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Quality</dt>
|
||||
<dd data-connection-diagnostic="quality">Waiting</dd>
|
||||
</div>
|
||||
@@ -377,26 +361,10 @@
|
||||
<dt>Reconnects</dt>
|
||||
<dd data-connection-diagnostic="reconnects">0</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Transport</dt>
|
||||
<dd data-connection-diagnostic="transport">WebSocket</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Heartbeat</dt>
|
||||
<dd data-connection-diagnostic="heartbeat">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Network</dt>
|
||||
<dd data-connection-diagnostic="network">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Client</dt>
|
||||
<dd data-connection-diagnostic="client">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Server</dt>
|
||||
<dd data-connection-diagnostic="server">—</dd>
|
||||
</div>
|
||||
<div class="connection-diagnostics-grid__wide">
|
||||
<dt>Last event</dt>
|
||||
<dd data-connection-diagnostic="last-event">—</dd>
|
||||
|
||||
+2
-2
@@ -101,8 +101,8 @@
|
||||
<div class="identity-fields">
|
||||
<label>Nickname<input id="nickname" name="nickname" maxlength="40" autocomplete="off" data-bwignore="true"
|
||||
placeholder="Your nickname"></label>
|
||||
<label id="auth-email-field">E-mail / LDAP or AD Username<input id="auth-email" name="username" type="email"
|
||||
maxlength="320" autocomplete="username" required placeholder="you@example.com or name.second-name"></label>
|
||||
<label id="auth-email-field"><span id="auth-email-label">E-mail</span><input id="auth-email" name="username"
|
||||
type="email" maxlength="320" autocomplete="username" required placeholder="you@example.com"></label>
|
||||
<label>Password<input id="auth-password" name="password" type="password" minlength="8" maxlength="128"
|
||||
autocomplete="current-password" required placeholder="At least 8 characters"></label>
|
||||
</div>
|
||||
|
||||
+27
-25
@@ -20,6 +20,26 @@ const clearAuthSession = sessionStore.clearAuthSession || (() => {
|
||||
sessionStorage.removeItem("rustpad:nickname");
|
||||
});
|
||||
|
||||
function configureCredentialFields({ emailLabel, email, password, loginMode, externalAuth }) {
|
||||
const directoryLogin = loginMode && externalAuth;
|
||||
emailLabel.textContent = directoryLogin ? "E-mail / LDAP or AD Username" : "E-mail";
|
||||
email.type = directoryLogin ? "text" : "email";
|
||||
email.placeholder = directoryLogin ? "you@example.com or username" : "you@example.com";
|
||||
email.autocomplete = loginMode ? "username" : "off";
|
||||
password.autocomplete = loginMode ? "current-password" : "off";
|
||||
|
||||
if (directoryLogin) password.removeAttribute("minlength");
|
||||
else password.minLength = 8;
|
||||
|
||||
if (loginMode) {
|
||||
email.removeAttribute("data-bwignore");
|
||||
password.removeAttribute("data-bwignore");
|
||||
} else {
|
||||
email.dataset.bwignore = "true";
|
||||
password.dataset.bwignore = "true";
|
||||
}
|
||||
}
|
||||
|
||||
export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }) {
|
||||
if (dialog.querySelector("#auth-panel")) return bindLegacyIdentityDialog({ dialog, onIdentity });
|
||||
const form = dialog.querySelector("#identity-form");
|
||||
@@ -28,6 +48,7 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
|
||||
const title = dialog.querySelector("#identity-title");
|
||||
const copy = dialog.querySelector("#identity-copy");
|
||||
const emailField = dialog.querySelector("#auth-email-field");
|
||||
const emailLabel = dialog.querySelector("#auth-email-label");
|
||||
const email = dialog.querySelector("#auth-email");
|
||||
const password = dialog.querySelector("#auth-password");
|
||||
const submit = dialog.querySelector("#auth-submit");
|
||||
@@ -48,7 +69,9 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
|
||||
? "Enter the e-mail address assigned to your local account."
|
||||
: registering
|
||||
? "Register your account with an e-mail address and password."
|
||||
: "Use your e-mail address or organization login and password.";
|
||||
: externalAuth
|
||||
? "Use your e-mail address or LDAP/AD username and password."
|
||||
: "Use your e-mail address and password.";
|
||||
|
||||
nickname.closest("label").hidden = !registering;
|
||||
nickname.disabled = !registering;
|
||||
@@ -62,21 +85,10 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
|
||||
resetButton.hidden = resetting || registering || externalAuth;
|
||||
backButton.hidden = !resetting;
|
||||
const loginMode = mode === "login";
|
||||
email.type = externalAuth && loginMode ? "text" : "email";
|
||||
if (externalAuth && loginMode) password.removeAttribute("minlength");
|
||||
else password.minLength = 8;
|
||||
configureCredentialFields({ emailLabel, email, password, loginMode, externalAuth });
|
||||
form.autocomplete = loginMode ? "on" : "off";
|
||||
nickname.autocomplete = "off";
|
||||
nickname.dataset.bwignore = "true";
|
||||
email.autocomplete = loginMode ? "username" : "off";
|
||||
password.autocomplete = loginMode ? "current-password" : "off";
|
||||
if (loginMode) {
|
||||
email.removeAttribute("data-bwignore");
|
||||
password.removeAttribute("data-bwignore");
|
||||
} else {
|
||||
email.dataset.bwignore = "true";
|
||||
password.dataset.bwignore = "true";
|
||||
}
|
||||
message.textContent = "";
|
||||
|
||||
queueMicrotask(() => {
|
||||
@@ -168,6 +180,7 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
|
||||
const message = dialog.querySelector("#identity-error");
|
||||
const modeTitle = dialog.querySelector("#auth-mode-title");
|
||||
const emailField = dialog.querySelector("#auth-email-field");
|
||||
const emailLabel = dialog.querySelector("#auth-email-label");
|
||||
const email = dialog.querySelector("#auth-email");
|
||||
const password = dialog.querySelector("#auth-password");
|
||||
const authSubmit = dialog.querySelector("#auth-submit");
|
||||
@@ -205,21 +218,10 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
|
||||
modeTitle.textContent = mode === "register" ? "Register" : "Log in";
|
||||
authSubmit.textContent = mode === "register" ? "Register and continue" : "Log in and continue";
|
||||
const loginMode = mode === "login";
|
||||
email.type = externalAuth && loginMode ? "text" : "email";
|
||||
if (externalAuth && loginMode) password.removeAttribute("minlength");
|
||||
else password.minLength = 8;
|
||||
configureCredentialFields({ emailLabel, email, password, loginMode, externalAuth });
|
||||
form.autocomplete = loginMode ? "on" : "off";
|
||||
nickname.autocomplete = "off";
|
||||
nickname.dataset.bwignore = "true";
|
||||
email.autocomplete = loginMode ? "username" : "off";
|
||||
password.autocomplete = loginMode ? "current-password" : "off";
|
||||
if (loginMode) {
|
||||
email.removeAttribute("data-bwignore");
|
||||
password.removeAttribute("data-bwignore");
|
||||
} else {
|
||||
email.dataset.bwignore = "true";
|
||||
password.dataset.bwignore = "true";
|
||||
}
|
||||
message.textContent = "";
|
||||
queueMicrotask(() => (mode === "register" ? nickname : email).focus());
|
||||
};
|
||||
|
||||
@@ -249,20 +249,8 @@ export function startNoteEditor(adapter) {
|
||||
? formatDiagnosticDuration(runtime.uptime_ms)
|
||||
: runtime.last_connection_uptime_ms ? `last ${formatDiagnosticDuration(runtime.last_connection_uptime_ms)}` : "—");
|
||||
setDiagnosticField("reconnects", `${runtime.total_reconnects || 0}${runtime.reconnect_attempt ? ` · attempt ${runtime.reconnect_attempt}` : ""}`);
|
||||
const scheme = client.request_scheme ? `${client.request_scheme.toUpperCase()} / ` : "";
|
||||
setDiagnosticField("transport", `${scheme}${server.transport || "WebSocket"}`);
|
||||
setDiagnosticField("heartbeat", server.heartbeat_interval_ms
|
||||
? `${Math.round(server.heartbeat_interval_ms / 1000)}s ping · ${Math.round(server.heartbeat_timeout_ms / 1000)}s timeout`
|
||||
: "Waiting for server policy");
|
||||
const network = runtime.network || {};
|
||||
const networkParts = [network.effective_type || client.effective_type];
|
||||
if (Number.isFinite(network.downlink_mbps ?? client.downlink_mbps)) networkParts.push(`${network.downlink_mbps ?? client.downlink_mbps} Mb/s`);
|
||||
if (Number.isFinite(network.rtt_ms ?? client.network_rtt_ms)) networkParts.push(`system RTT ${Math.round(network.rtt_ms ?? client.network_rtt_ms)} ms`);
|
||||
if ((network.save_data ?? client.save_data) === true) networkParts.push("data saver");
|
||||
setDiagnosticField("network", networkParts.filter(Boolean).join(" · ") || (runtime.online === false ? "Offline" : "Not exposed by browser"));
|
||||
const clientParts = [client.platform, client.timezone, client.language || client.accept_language, client.id ? `id ${client.id}` : null, client.user_agent];
|
||||
setDiagnosticField("client", clientParts.filter(Boolean).join(" · ") || "Waiting for server data");
|
||||
setDiagnosticField("server", server.server_version ? `RustPad ${server.server_version} · connection ${server.connection_id}` : "Waiting for server data");
|
||||
const lastEvent = runtime.last_close
|
||||
? `Closed ${runtime.last_close.code}${runtime.last_close.reason ? `: ${runtime.last_close.reason}` : ""}`
|
||||
: runtime.last_message_at ? `Message ${new Date(runtime.last_message_at).toLocaleTimeString()}` : "No messages yet";
|
||||
|
||||
@@ -30,7 +30,6 @@ class RoomSocket {
|
||||
this.latencySampleWindow = DEFAULT_LATENCY_SAMPLE_WINDOW;
|
||||
this.latencySamples = [];
|
||||
this.totalReconnects = 0;
|
||||
this.connectedAt = null;
|
||||
this.authenticatedAt = null;
|
||||
this.lastConnectionUptimeMs = 0;
|
||||
this.lastMessageAt = null;
|
||||
@@ -51,15 +50,10 @@ class RoomSocket {
|
||||
get kind() { return "room"; }
|
||||
|
||||
clientDiagnostics() {
|
||||
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
return {
|
||||
language: navigator.language || null,
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || null,
|
||||
platform: navigator.userAgentData?.platform || navigator.platform || null,
|
||||
effective_type: connection?.effectiveType || null,
|
||||
downlink_mbps: Number.isFinite(connection?.downlink) ? connection.downlink : null,
|
||||
network_rtt_ms: Number.isFinite(connection?.rtt) ? Math.max(0, Math.round(connection.rtt)) : null,
|
||||
save_data: typeof connection?.saveData === "boolean" ? connection.saveData : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,7 +87,6 @@ class RoomSocket {
|
||||
socket.addEventListener("open", () => {
|
||||
if (socket !== this.socket || this.stopped) return;
|
||||
logInfo("websocket.open", { kind: this.kind });
|
||||
this.connectedAt = Date.now();
|
||||
this.serverDiagnostics = null;
|
||||
this.lastClose = null;
|
||||
this.send({
|
||||
@@ -161,7 +154,6 @@ class RoomSocket {
|
||||
this.onLatency?.(null);
|
||||
const closedAt = Date.now();
|
||||
this.lastConnectionUptimeMs = this.authenticatedAt ? Math.max(0, closedAt - this.authenticatedAt) : this.lastConnectionUptimeMs;
|
||||
this.connectedAt = null;
|
||||
this.authenticatedAt = null;
|
||||
this.lastClose = { code: event.code, reason: event.reason || "", at: closedAt };
|
||||
this.emitDiagnostics();
|
||||
@@ -260,7 +252,6 @@ class RoomSocket {
|
||||
this.latencySampleWindow = Math.round(positiveNumber(diagnostics.latency_sample_window, this.latencySampleWindow, 3, 100));
|
||||
while (this.latencySamples.length > this.latencySampleWindow) this.latencySamples.shift();
|
||||
if (this.socket?.readyState === WebSocket.OPEN && this.authenticatedAt) this.startPing();
|
||||
this.onServerDiagnostics?.(diagnostics);
|
||||
this.emitDiagnostics();
|
||||
}
|
||||
|
||||
@@ -282,31 +273,22 @@ class RoomSocket {
|
||||
}
|
||||
|
||||
emitDiagnostics() {
|
||||
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
const readyStates = ["connecting", "open", "closing", "closed"];
|
||||
this.onDiagnostics?.({
|
||||
server: this.serverDiagnostics,
|
||||
runtime: {
|
||||
state: readyStates[this.socket?.readyState ?? WebSocket.CLOSED] || "closed",
|
||||
online: navigator.onLine,
|
||||
visibility: document.visibilityState,
|
||||
uptime_ms: this.authenticatedAt ? Math.max(0, Date.now() - this.authenticatedAt) : 0,
|
||||
last_connection_uptime_ms: this.lastConnectionUptimeMs,
|
||||
reconnect_attempt: this.reconnectAttempt,
|
||||
total_reconnects: this.totalReconnects,
|
||||
connected_at: this.connectedAt,
|
||||
authenticated_at: this.authenticatedAt,
|
||||
last_message_at: this.lastMessageAt,
|
||||
last_close: this.lastClose,
|
||||
buffered_amount: this.socket?.bufferedAmount || 0,
|
||||
bytes_sent: this.bytesSent,
|
||||
bytes_received: this.bytesReceived,
|
||||
network: {
|
||||
effective_type: connection?.effectiveType || null,
|
||||
downlink_mbps: Number.isFinite(connection?.downlink) ? connection.downlink : null,
|
||||
rtt_ms: Number.isFinite(connection?.rtt) ? connection.rtt : null,
|
||||
save_data: typeof connection?.saveData === "boolean" ? connection.saveData : null,
|
||||
},
|
||||
latency: this.latencyStats(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
<section id="auth-panel" class="auth-panel" hidden>
|
||||
<h3 id="auth-mode-title">Log in</h3>
|
||||
<label id="auth-email-field">E-mail / organization login
|
||||
<label id="auth-email-field"><span id="auth-email-label">E-mail</span>
|
||||
<input id="auth-email" name="username" type="email" maxlength="320" autocomplete="username"
|
||||
placeholder="you@example.com">
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user