fix: move offline screenshots out of localStorage (Closes #473)
CI / lint (pull_request) Successful in 59s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-10 10:15:58 +00:00
1 parent 2b2e2cceb4
commit 8b7c56161c
5 files changed
+208 -19

No files matched your search

+9 -2
View File
@@ -65,13 +65,18 @@ function createIssueSyncStore({ transaction, indexedDB = globalThis.indexedDB, n
if (current.recordType === 'receipt-preference') continue;
const currentLane = current.outboxLane || 'issue';
if (currentLane !== outboxLane) continue;
const replacement = incoming.get(current.id);
let replacement = incoming.get(current.id);
if (!replacement) {
if (current.status !== 'sending' || Number(current.claimUntil) <= Number(now())) {
await records.delete(current.id);
}
continue;
}
if (current.operationId === replacement.operationId && current.attachment?.data &&
replacement.attachment?.stored && !replacement.attachment.data) {
replacement = { ...replacement, attachment: current.attachment };
incoming.set(current.id, replacement);
}
if ((current.status === 'sending' && Number(current.claimUntil) > Number(now())) ||
(current.status === 'attention' && replacement.status === 'attention') ||
current.status === 'sent') {
@@ -141,8 +146,10 @@ function createIssueSyncStore({ transaction, indexedDB = globalThis.indexedDB, n
if (current && ((current.status === 'sending' && Number(current.claimUntil) > Number(now())) ||
(current.status === 'attention' && item.status === 'attention') ||
current.status === 'sent')) return current;
const preservedAttachment = current?.attachment?.data && item?.attachment?.stored && !item.attachment.data
? { attachment: current.attachment } : {};
const next = current && current.operationId === item.operationId ? {
...item,
...item, ...preservedAttachment,
...(current.deliveredIssue ? { deliveredIssue: current.deliveredIssue } : {}),
...(current.attachmentMarkdown ? { attachmentMarkdown: current.attachmentMarkdown } : {}),
} : { ...item };
+54 -16
View File
@@ -9,7 +9,9 @@ function createIssueOutbox({ storage, fetchJson, coordinator, backgroundSync, ge
const contentType = String(value?.contentType || '');
const filename = String(value?.filename || '').slice(0, 255);
const data = String(value?.data || '');
if (!filename || !['image/png', 'image/jpeg', 'image/webp'].includes(contentType) || !data) return undefined;
if (!filename || !['image/png', 'image/jpeg', 'image/webp'].includes(contentType)) return undefined;
if (!data && value?.stored === true) return { filename, contentType, stored: true };
if (!data) return undefined;
return { filename, contentType, data };
}
@@ -31,7 +33,7 @@ function createIssueOutbox({ storage, fetchJson, coordinator, backgroundSync, ge
}
}
function enqueue(draft, mirror = true) {
function prepareItem(draft) {
const ownerLogin = String(getOwnerLogin() || '').trim();
if (!ownerLogin) throw new Error('Confirm your Gitea account before queueing an issue.');
const items = read();
@@ -55,28 +57,52 @@ function createIssueOutbox({ storage, fetchJson, coordinator, backgroundSync, ge
item.milestoneId = Number(draft.milestoneId);
}
if (/^\d{4}-\d{2}-\d{2}$/.test(String(draft?.dueDate || ''))) item.dueDate = String(draft.dueDate);
return item;
}
function localIndexItem(item) {
if (!item?.attachment?.data) return item;
return {
...item,
attachment: {
filename: item.attachment.filename,
contentType: item.attachment.contentType,
stored: true,
},
};
}
function enqueue(draft, mirror = true) {
const items = read();
const item = prepareItem(draft);
items.push(item);
write(items, mirror);
return item;
}
async function enqueueDurably(draft) {
const item = enqueue(draft, false);
if (!backgroundSync?.reconcile || !backgroundSync?.requestSync) {
const item = enqueue(draft, false);
return { item, background: false, durability: 'foreground-only' };
}
const item = prepareItem(draft);
const current = read();
try {
await backgroundSync.reconcile(read());
await backgroundSync.reconcile([...current, item]);
const localItem = localIndexItem(item);
write([...current, item].map(localIndexItem), false);
await backgroundSync.requestSync();
return { item, background: true, durability: 'background' };
return { item: localItem, background: true, durability: 'background' };
} catch (error) {
return { item, background: false, durability: 'foreground-only', error };
const persisted = read().find(candidate => candidate.id === item.id);
if (!persisted) throw error;
return { item: persisted, background: false, durability: 'foreground-only', error };
}
}
function update(id, draft, mirror = true) {
function prepareUpdate(id, draft) {
let updated = null;
write(read().map(item => {
const items = read().map(item => {
if (item.id !== id) return item;
const nextRepository = String(draft?.repository || '');
const nextTitle = String(draft?.title || '');
@@ -109,21 +135,33 @@ function createIssueOutbox({ storage, fetchJson, coordinator, backgroundSync, ge
delete updated.error;
delete updated.deliveryState;
return updated;
}), mirror);
});
return { items, updated };
}
function update(id, draft, mirror = true) {
const { items, updated } = prepareUpdate(id, draft);
write(items, mirror);
return updated;
}
async function updateDurably(id, draft) {
const item = update(id, draft, false);
if (!item || !backgroundSync?.reconcile || !backgroundSync?.requestSync) {
if (!backgroundSync?.reconcile || !backgroundSync?.requestSync) {
const item = update(id, draft, false);
return { item, background: false, durability: 'foreground-only' };
}
const { items, updated: item } = prepareUpdate(id, draft);
if (!item) return { item, background: false, durability: 'foreground-only' };
try {
await backgroundSync.reconcile(read());
await backgroundSync.reconcile(items);
const localItems = items.map(localIndexItem);
write(localItems, false);
await backgroundSync.requestSync();
return { item, background: true, durability: 'background' };
return { item: localIndexItem(item), background: true, durability: 'background' };
} catch (error) {
return { item, background: false, durability: 'foreground-only', error };
const persisted = read().find(candidate => candidate.id === id);
if (!persisted || persisted.operationId !== item.operationId) throw error;
return { item: persisted, background: false, durability: 'foreground-only', error };
}
}
@@ -318,10 +356,10 @@ function createIssueOutbox({ storage, fetchJson, coordinator, backgroundSync, ge
return [];
}
if (background?.status === 'attention') return [{
...item, ...deliveryStage, status: 'attention', error: String(background.error || 'Issue needs attention').slice(0, 240),
...localIndexItem(item), ...deliveryStage, status: 'attention', error: String(background.error || 'Issue needs attention').slice(0, 240),
...(background.deliveryState ? { deliveryState: background.deliveryState } : {}),
}];
return [{ ...item, ...deliveryStage }];
return [{ ...localIndexItem(item), ...deliveryStage }];
});
write(items);
return items;