server auth
This commit is contained in:
@@ -3,11 +3,107 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const syncQueue = document.querySelector("[data-sync-queue]");
|
||||
const syncQueueTitle = document.querySelector("[data-sync-queue-title]");
|
||||
const syncQueueSummary = document.querySelector("[data-sync-queue-summary]");
|
||||
const syncQueueList = document.querySelector("[data-sync-queue-list]");
|
||||
const syncNow = document.querySelector("[data-sync-now]");
|
||||
|
||||
function normalizeDocPath(path) {
|
||||
if (!path) return "";
|
||||
return path.replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
function formatPath(path) {
|
||||
return (path || "").replace(/^\/docs\//, "") || "/";
|
||||
}
|
||||
|
||||
function pluralize(count, singular, plural) {
|
||||
return count === 1 ? singular : plural;
|
||||
}
|
||||
|
||||
function emitSyncState(detail) {
|
||||
window.dispatchEvent(new CustomEvent("mdhub:sync-state", { detail: detail }));
|
||||
}
|
||||
|
||||
function renderPendingEdits(pending, state) {
|
||||
if (!syncQueue || !syncQueueTitle || !syncQueueSummary || !syncQueueList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conflicts = pending.filter(function (edit) {
|
||||
return Boolean(edit.conflict);
|
||||
});
|
||||
const clean = pending.filter(function (edit) {
|
||||
return !edit.conflict;
|
||||
});
|
||||
|
||||
syncQueue.hidden = pending.length === 0;
|
||||
if (pending.length === 0) {
|
||||
syncQueueList.replaceChildren();
|
||||
syncQueueTitle.textContent = "Synced";
|
||||
syncQueueSummary.textContent = "No pending changes.";
|
||||
if (syncNow) {
|
||||
syncNow.disabled = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const countLabel = pending.length + " pending " + pluralize(pending.length, "change", "changes");
|
||||
syncQueueTitle.textContent = conflicts.length > 0 ? countLabel + " with conflicts" : countLabel;
|
||||
if (!navigator.onLine) {
|
||||
syncQueueSummary.textContent = "Offline. Changes will sync when the connection returns.";
|
||||
} else if (state === "syncing") {
|
||||
syncQueueSummary.textContent = "Syncing queued edits...";
|
||||
} else if (conflicts.length > 0) {
|
||||
syncQueueSummary.textContent = conflicts.length + " " + pluralize(conflicts.length, "file needs", "files need") + " conflict resolution.";
|
||||
} else {
|
||||
syncQueueSummary.textContent = "Queued edits are ready to sync.";
|
||||
}
|
||||
|
||||
syncQueueList.replaceChildren();
|
||||
pending.slice(0, 5).forEach(function (edit) {
|
||||
const item = document.createElement("li");
|
||||
item.dataset.state = edit.conflict ? "conflict" : "queued";
|
||||
|
||||
const path = document.createElement("span");
|
||||
path.textContent = formatPath(edit.path);
|
||||
item.appendChild(path);
|
||||
|
||||
const status = document.createElement("span");
|
||||
status.textContent = edit.conflict ? "Conflict" : "Queued";
|
||||
item.appendChild(status);
|
||||
|
||||
syncQueueList.appendChild(item);
|
||||
});
|
||||
|
||||
if (pending.length > 5) {
|
||||
const overflow = document.createElement("li");
|
||||
overflow.textContent = "+" + (pending.length - 5) + " more";
|
||||
syncQueueList.appendChild(overflow);
|
||||
}
|
||||
|
||||
if (syncNow) {
|
||||
syncNow.disabled = !navigator.onLine || clean.length === 0 || state === "syncing";
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshSyncQueue(state) {
|
||||
const pending = await window.MDHubCache.getPendingEdits();
|
||||
pending.sort(function (a, b) {
|
||||
return (b.updatedAt || 0) - (a.updatedAt || 0);
|
||||
});
|
||||
renderPendingEdits(pending, state || "idle");
|
||||
emitSyncState({
|
||||
state: state || "idle",
|
||||
pending: pending.length,
|
||||
conflicts: pending.filter(function (edit) {
|
||||
return Boolean(edit.conflict);
|
||||
}).length,
|
||||
});
|
||||
return pending;
|
||||
}
|
||||
|
||||
async function postDocument(path, content, baseHash) {
|
||||
const response = await fetch("/api/documents/" + normalizeDocPath(path), {
|
||||
method: "POST",
|
||||
@@ -40,6 +136,7 @@
|
||||
try {
|
||||
const result = await postDocument(path, content, baseHash);
|
||||
await window.MDHubCache.deletePendingEdit(normalizedPath);
|
||||
await refreshSyncQueue("saved");
|
||||
return {
|
||||
status: "saved",
|
||||
result: result,
|
||||
@@ -48,12 +145,14 @@
|
||||
if (error.name === "DocumentConflictError") {
|
||||
await window.MDHubCache.putPendingEdit(normalizedPath, content, baseHash);
|
||||
await window.MDHubCache.markPendingEditConflict(normalizedPath, error.details);
|
||||
await refreshSyncQueue("conflict");
|
||||
return {
|
||||
status: "conflict",
|
||||
conflict: error.details,
|
||||
};
|
||||
}
|
||||
await window.MDHubCache.putPendingEdit(normalizedPath, content, baseHash);
|
||||
await refreshSyncQueue("queued");
|
||||
return {
|
||||
status: "queued",
|
||||
};
|
||||
@@ -62,15 +161,19 @@
|
||||
|
||||
async function syncPending() {
|
||||
if (!navigator.onLine) {
|
||||
await refreshSyncQueue("offline");
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = await window.MDHubCache.getPendingEdits();
|
||||
const pending = await refreshSyncQueue("syncing");
|
||||
pending.sort(function (a, b) {
|
||||
return (a.updatedAt || 0) - (b.updatedAt || 0);
|
||||
});
|
||||
|
||||
for (const edit of pending) {
|
||||
if (edit.conflict) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await postDocument(edit.path.replace(/^\/docs\//, ""), edit.content, edit.baseHash);
|
||||
await window.MDHubCache.deletePendingEdit(edit.path);
|
||||
@@ -81,16 +184,31 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
await refreshSyncQueue("idle");
|
||||
}
|
||||
|
||||
window.addEventListener("online", function () {
|
||||
syncPending().catch(function () {});
|
||||
});
|
||||
|
||||
window.addEventListener("offline", function () {
|
||||
refreshSyncQueue("offline").catch(function () {});
|
||||
});
|
||||
|
||||
if (syncNow) {
|
||||
syncNow.addEventListener("click", function () {
|
||||
syncPending().catch(function () {
|
||||
refreshSyncQueue("queued").catch(function () {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
syncPending().catch(function () {});
|
||||
refreshSyncQueue("idle").catch(function () {});
|
||||
|
||||
window.MDHubSync = {
|
||||
saveDocument: saveDocument,
|
||||
syncPending: syncPending,
|
||||
refreshSyncQueue: refreshSyncQueue,
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user