improvements
This commit is contained in:
+103
-5
@@ -25,6 +25,12 @@ const shareToken = new URLSearchParams(location.search).get("share");
|
||||
let accessToken = shareToken || getAccessToken("workspace", slug);
|
||||
let nickname = getNickname();
|
||||
getGuestId();
|
||||
const workspaceWatchClientId = `workspace_watch_${crypto.randomUUID()}`;
|
||||
let workspaceWatchSocket;
|
||||
let workspaceWatchPingTimer;
|
||||
let workspaceWatchReconnectTimer;
|
||||
let workspaceWatchIntentionalClose = false;
|
||||
let workspaceLockedForPassword = false;
|
||||
const dialog = document.querySelector("#password-dialog");
|
||||
const identityDialog = document.querySelector("#identity-dialog");
|
||||
const workspaceContent = document.querySelector("#workspace-content");
|
||||
@@ -45,6 +51,86 @@ function updateWorkspacePasswordControl() {
|
||||
workspacePasswordForm.hidden = Boolean(info?.protected || !info?.can_set_password);
|
||||
}
|
||||
|
||||
function stopWorkspaceWatch() {
|
||||
clearInterval(workspaceWatchPingTimer);
|
||||
clearTimeout(workspaceWatchReconnectTimer);
|
||||
workspaceWatchPingTimer = undefined;
|
||||
workspaceWatchReconnectTimer = undefined;
|
||||
if (workspaceWatchSocket) {
|
||||
workspaceWatchIntentionalClose = true;
|
||||
workspaceWatchSocket.close();
|
||||
workspaceWatchSocket = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function lockWorkspaceForPassword(message = "A password was set for this workspace. Enter it to continue.") {
|
||||
workspaceLockedForPassword = true;
|
||||
info = { ...info, protected: true, access_level: "none", can_set_password: false };
|
||||
stopWorkspaceWatch();
|
||||
workspaceContent.hidden = true;
|
||||
document.querySelector("#password-error").textContent = message;
|
||||
if (!dialog.open) dialog.showModal();
|
||||
document.querySelector("#open-password")?.focus();
|
||||
}
|
||||
|
||||
function scheduleWorkspaceWatchReconnect() {
|
||||
clearTimeout(workspaceWatchReconnectTimer);
|
||||
if (workspaceLockedForPassword || !nickname) return;
|
||||
workspaceWatchReconnectTimer = window.setTimeout(connectWorkspaceWatch, 1500);
|
||||
}
|
||||
|
||||
function connectWorkspaceWatch() {
|
||||
if (workspaceLockedForPassword || !nickname) return;
|
||||
if (workspaceWatchSocket?.readyState === WebSocket.OPEN || workspaceWatchSocket?.readyState === WebSocket.CONNECTING) return;
|
||||
clearTimeout(workspaceWatchReconnectTimer);
|
||||
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const socket = new WebSocket(`${protocol}//${location.host}/ws/watch/workspace/${encodeURIComponent(slug)}`);
|
||||
workspaceWatchSocket = socket;
|
||||
workspaceWatchIntentionalClose = false;
|
||||
socket.addEventListener("open", () => {
|
||||
if (socket !== workspaceWatchSocket) return;
|
||||
socket.send(JSON.stringify({
|
||||
type: "authenticate",
|
||||
access_token: accessToken || null,
|
||||
client_id: workspaceWatchClientId,
|
||||
}));
|
||||
clearInterval(workspaceWatchPingTimer);
|
||||
workspaceWatchPingTimer = window.setInterval(() => {
|
||||
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "ping", nonce: Date.now() }));
|
||||
}, 10000);
|
||||
});
|
||||
socket.addEventListener("message", event => {
|
||||
if (socket !== workspaceWatchSocket) return;
|
||||
let message;
|
||||
try { message = JSON.parse(event.data); } catch { return; }
|
||||
if (message.type === "password_required") {
|
||||
workspaceWatchIntentionalClose = true;
|
||||
lockWorkspaceForPassword();
|
||||
return;
|
||||
}
|
||||
if (message.type === "password_changed") {
|
||||
workspaceWatchIntentionalClose = true;
|
||||
socket.close();
|
||||
return;
|
||||
}
|
||||
if (message.type === "error") {
|
||||
workspaceWatchIntentionalClose = true;
|
||||
socket.close();
|
||||
if (/password/i.test(message.message || "")) lockWorkspaceForPassword(message.message);
|
||||
}
|
||||
});
|
||||
socket.addEventListener("close", () => {
|
||||
if (socket !== workspaceWatchSocket) return;
|
||||
clearInterval(workspaceWatchPingTimer);
|
||||
workspaceWatchPingTimer = undefined;
|
||||
workspaceWatchSocket = undefined;
|
||||
if (!workspaceWatchIntentionalClose) scheduleWorkspaceWatchReconnect();
|
||||
});
|
||||
socket.addEventListener("error", () => {
|
||||
if (socket.readyState !== WebSocket.CLOSING && socket.readyState !== WebSocket.CLOSED) socket.close();
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(v) { const e = document.createElement("div"); e.textContent = v; return e.innerHTML; }
|
||||
function formatBytes(value) {
|
||||
const bytes = Math.max(0, Number(value) || 0);
|
||||
@@ -143,11 +229,13 @@ async function openWorkspace() {
|
||||
renderNotes();
|
||||
renderNotesPagination(data.pagination);
|
||||
updateWorkspacePasswordControl();
|
||||
workspaceLockedForPassword = false;
|
||||
workspaceContent.hidden = false;
|
||||
if (dialog.open) dialog.close();
|
||||
connectWorkspaceWatch();
|
||||
} catch (e) {
|
||||
if (info?.protected || e.message.toLowerCase().includes("password")) {
|
||||
document.querySelector("#password-error").textContent = e.message;
|
||||
if (!dialog.open) dialog.showModal();
|
||||
lockWorkspaceForPassword(e.message);
|
||||
} else if (e.status === 403 || e.status === 404) {
|
||||
await showSystemNotFound();
|
||||
} else document.querySelector("#workspace-error").textContent = e.message;
|
||||
@@ -160,7 +248,8 @@ async function init() {
|
||||
document.querySelector("#workspace-title").textContent = info.title;
|
||||
document.querySelector("#workspace-url").textContent = location.pathname;
|
||||
updateWorkspacePasswordControl();
|
||||
if (info.protected && info.access_level === "none") dialog.showModal(); else openWorkspace();
|
||||
if (info.protected && info.access_level === "none") lockWorkspaceForPassword("Enter the workspace password to continue.");
|
||||
else await openWorkspace();
|
||||
} catch (e) {
|
||||
if (e.status === 403 || e.status === 404) await showSystemNotFound();
|
||||
else document.querySelector("#workspace-error").textContent = e.message;
|
||||
@@ -175,7 +264,7 @@ document.querySelector("#password-form").addEventListener("submit", async e => {
|
||||
accessToken = getAccessToken("workspace", slug);
|
||||
document.querySelector("#open-password").value = "";
|
||||
document.querySelector("#password-error").textContent = "";
|
||||
openWorkspace();
|
||||
await openWorkspace();
|
||||
} catch (error) { document.querySelector("#password-error").textContent = error.message; }
|
||||
});
|
||||
workspacePasswordForm.addEventListener("submit", async event => {
|
||||
@@ -191,7 +280,7 @@ workspacePasswordForm.addEventListener("submit", async event => {
|
||||
try {
|
||||
await api(`/api/workspaces/${encodeURIComponent(slug)}/password`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ password }),
|
||||
body: JSON.stringify({ password, client_id: workspaceWatchClientId }),
|
||||
});
|
||||
const result = await api("/api/access-token", {
|
||||
method: "POST",
|
||||
@@ -278,5 +367,14 @@ identityDialog.addEventListener("close", () => {
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addEventListener("cancel", event => {
|
||||
if (workspaceLockedForPassword) {
|
||||
event.preventDefault();
|
||||
document.querySelector("#open-password")?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("beforeunload", stopWorkspaceWatch);
|
||||
|
||||
setNotesView(notesView);
|
||||
startAuthorizedWorkspace();
|
||||
|
||||
Reference in New Issue
Block a user