fix(canvas): add auth headers to API fetches + deduplicate sync events
- Add Authorization header to fetchTripData, fetchTripDetail, and fetchNotesData to prevent 401 errors on private spaces - Add #initialSyncFired flag to CommunitySync so the "synced" event only fires once per connection cycle instead of on every debounce gap Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
29c4f48634
commit
2c0fbb76ac
|
|
@ -161,6 +161,7 @@ export class CommunitySync extends EventTarget {
|
|||
#offlineStore: OfflineStore | null = null;
|
||||
#saveDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
#syncedDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
#initialSyncFired = false;
|
||||
#wsUrl: string | null = null;
|
||||
|
||||
// ── Undo/Redo state ──
|
||||
|
|
@ -292,6 +293,7 @@ export class CommunitySync extends EventTarget {
|
|||
|
||||
this.#ws.onclose = () => {
|
||||
console.log(`[CommunitySync] Disconnected from ${this.#communitySlug}`);
|
||||
this.#initialSyncFired = false;
|
||||
this.dispatchEvent(new CustomEvent("disconnected"));
|
||||
|
||||
if (!this.#disconnectedIntentionally) {
|
||||
|
|
@ -844,10 +846,12 @@ export class CommunitySync extends EventTarget {
|
|||
|
||||
// Debounce the synced event — during initial sync negotiation, #applyDocToDOM()
|
||||
// is called for every Automerge sync message (100+ round-trips). Debounce to
|
||||
// fire once after the burst settles.
|
||||
// fire once after the burst settles. Only fires once per connection cycle.
|
||||
if (this.#initialSyncFired) return;
|
||||
if (this.#syncedDebounceTimer) clearTimeout(this.#syncedDebounceTimer);
|
||||
this.#syncedDebounceTimer = setTimeout(() => {
|
||||
this.#syncedDebounceTimer = null;
|
||||
this.#initialSyncFired = true;
|
||||
this.dispatchEvent(new CustomEvent("synced", { detail: { shapes } }));
|
||||
}, 300);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3088,10 +3088,18 @@
|
|||
let _tripCache = null; // { trips: [], detail: null, fetchedAt: 0 }
|
||||
const TRIP_CACHE_TTL = 60000; // 1 minute
|
||||
|
||||
function _authHeaders() {
|
||||
try {
|
||||
const s = JSON.parse(localStorage.getItem('encryptid_session') || '{}');
|
||||
if (s?.accessToken) return { 'Authorization': 'Bearer ' + s.accessToken };
|
||||
} catch {}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function fetchTripData() {
|
||||
if (_tripCache && Date.now() - _tripCache.fetchedAt < TRIP_CACHE_TTL) return _tripCache;
|
||||
try {
|
||||
const res = await fetch(`/${communitySlug}/rtrips/api/trips`);
|
||||
const res = await fetch(`/${communitySlug}/rtrips/api/trips`, { headers: _authHeaders() });
|
||||
if (!res.ok) return { trips: [], detail: null, fetchedAt: Date.now() };
|
||||
const trips = await res.json();
|
||||
_tripCache = { trips, detail: null, fetchedAt: Date.now() };
|
||||
|
|
@ -3101,7 +3109,7 @@
|
|||
|
||||
async function fetchTripDetail(tripId) {
|
||||
try {
|
||||
const res = await fetch(`/${communitySlug}/rtrips/api/trips/${tripId}`);
|
||||
const res = await fetch(`/${communitySlug}/rtrips/api/trips/${tripId}`, { headers: _authHeaders() });
|
||||
if (!res.ok) return null;
|
||||
return await res.json();
|
||||
} catch { return null; }
|
||||
|
|
@ -3145,7 +3153,7 @@
|
|||
async function fetchNotesData() {
|
||||
if (_notesCache && Date.now() - _notesCache.fetchedAt < TRIP_CACHE_TTL) return _notesCache;
|
||||
try {
|
||||
const res = await fetch(`/${communitySlug}/rnotes/api/notes?limit=50`);
|
||||
const res = await fetch(`/${communitySlug}/rnotes/api/notes?limit=50`, { headers: _authHeaders() });
|
||||
if (!res.ok) return { notes: [], fetchedAt: Date.now() };
|
||||
const data = await res.json();
|
||||
_notesCache = { notes: data.notes || [], fetchedAt: Date.now() };
|
||||
|
|
|
|||
Loading…
Reference in New Issue