forked from mirror/crumbs-original
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1813c5b1a6 | ||
|
|
54416e392c | ||
|
|
8a21950e53 | ||
|
|
5992a12f76 | ||
|
|
b515b0593d | ||
|
|
ba665b66c2 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
crumbs:
|
||||
image: ghcr.io/bretzel-app/crumbs:0.6.2
|
||||
image: ghcr.io/bretzel-app/crumbs:0.7.1
|
||||
container_name: crumbs
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "crumbs",
|
||||
"private": true,
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.2",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"author": "Bretzel (https://bretzel.app)",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { optimizeImage } from '$lib/utils/image-optimize.js';
|
||||
import { browser } from '$app/environment';
|
||||
import { addPendingAttachment, type PendingAttachment } from '$lib/sync/idb.js';
|
||||
import { showToast } from '$lib/stores/toast.js';
|
||||
|
||||
interface Props {
|
||||
noteId: string;
|
||||
@@ -85,9 +86,13 @@
|
||||
if (res.ok) {
|
||||
const attachment = await res.json();
|
||||
onUpload(attachment);
|
||||
} else {
|
||||
console.error('Upload failed:', res.status, await res.text());
|
||||
showToast('Failed to upload image', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Upload failed:', err);
|
||||
showToast('Failed to upload image', 'error');
|
||||
}
|
||||
uploading = false;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,12 @@ sqlite.exec(`
|
||||
CREATE INDEX IF NOT EXISTS sync_log_timestamp_idx ON sync_log(timestamp);
|
||||
`);
|
||||
|
||||
// Idempotent migration: add featured column to attachments
|
||||
// Idempotent migrations: add columns that may be missing from older DBs
|
||||
try {
|
||||
sqlite.exec(`ALTER TABLE attachments ADD COLUMN thumbnail_path TEXT;`);
|
||||
} catch {
|
||||
// Column already exists
|
||||
}
|
||||
try {
|
||||
sqlite.exec(`ALTER TABLE attachments ADD COLUMN featured INTEGER NOT NULL DEFAULT 0;`);
|
||||
} catch {
|
||||
|
||||
+22
-5
@@ -63,9 +63,13 @@ function isNetworkError(err: unknown): boolean {
|
||||
|
||||
export async function loadNotes(filter: NoteFilter = 'all') {
|
||||
// Load from IDB first for instant display
|
||||
const cached = await getAllNotes();
|
||||
if (cached.length > 0) {
|
||||
notes.set(cached);
|
||||
try {
|
||||
const cached = await getAllNotes();
|
||||
if (cached.length > 0) {
|
||||
notes.set(cached);
|
||||
}
|
||||
} catch {
|
||||
// IDB unavailable — continue with server fetch
|
||||
}
|
||||
currentFilter.set(filter);
|
||||
notesLoaded.set(true);
|
||||
@@ -79,8 +83,16 @@ export async function loadNotes(filter: NoteFilter = 'all') {
|
||||
currentFilter.set(filter);
|
||||
|
||||
// Update IDB with server state
|
||||
await clearIdbNotes();
|
||||
await Promise.all(data.map((n) => putNote(n)));
|
||||
try {
|
||||
await clearIdbNotes();
|
||||
await Promise.all(data.map((n) => putNote(n)));
|
||||
} catch {
|
||||
// IDB write failed — server data is still in the store
|
||||
}
|
||||
} else if (res.status === 401) {
|
||||
window.location.href = '/login';
|
||||
} else {
|
||||
showToast('Failed to load notes', 'error');
|
||||
}
|
||||
} catch {
|
||||
// Offline — IDB data already loaded above
|
||||
@@ -100,6 +112,11 @@ export async function createNote(note: NoteCreate): Promise<Note | null> {
|
||||
await putNote(created);
|
||||
return created;
|
||||
}
|
||||
if (res.status === 401) {
|
||||
window.location.href = '/login';
|
||||
} else {
|
||||
showToast('Failed to save note', 'error');
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
if (isNetworkError(err)) {
|
||||
|
||||
+28
-13
@@ -29,7 +29,12 @@ export function getSyncStatus(): SyncStatus {
|
||||
* Push local changes to server.
|
||||
*/
|
||||
async function pushChanges(): Promise<boolean> {
|
||||
const queue = await getSyncQueue();
|
||||
let queue;
|
||||
try {
|
||||
queue = await getSyncQueue();
|
||||
} catch {
|
||||
return true; // IDB unavailable — nothing to push
|
||||
}
|
||||
if (queue.length === 0) return true;
|
||||
|
||||
try {
|
||||
@@ -78,27 +83,37 @@ async function pushPendingAttachments(): Promise<void> {
|
||||
* Pull remote changes and merge with local state.
|
||||
*/
|
||||
async function pullChanges(): Promise<Note[]> {
|
||||
const lastSync = (await getMeta('lastSyncTimestamp')) || '0';
|
||||
let lastSync = '0';
|
||||
try {
|
||||
lastSync = (await getMeta('lastSyncTimestamp')) || '0';
|
||||
} catch {
|
||||
// IDB unavailable — pull everything
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/sync?since=${lastSync}`);
|
||||
if (!res.ok) return [];
|
||||
|
||||
const remoteNotes: Note[] = await res.json();
|
||||
const localNotes = await getAllNotes();
|
||||
const localMap = new Map(localNotes.map((n) => [n.id, n]));
|
||||
|
||||
for (const remote of remoteNotes) {
|
||||
const local = localMap.get(remote.id);
|
||||
if (local) {
|
||||
const merged = mergeNotes(local, remote);
|
||||
await putNote(merged);
|
||||
} else {
|
||||
await putNote(remote);
|
||||
try {
|
||||
const localNotes = await getAllNotes();
|
||||
const localMap = new Map(localNotes.map((n) => [n.id, n]));
|
||||
|
||||
for (const remote of remoteNotes) {
|
||||
const local = localMap.get(remote.id);
|
||||
if (local) {
|
||||
const merged = mergeNotes(local, remote);
|
||||
await putNote(merged);
|
||||
} else {
|
||||
await putNote(remote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await setMeta('lastSyncTimestamp', String(Date.now()));
|
||||
await setMeta('lastSyncTimestamp', String(Date.now()));
|
||||
} catch {
|
||||
// IDB write failed — server data still returned for store use
|
||||
}
|
||||
return remoteNotes;
|
||||
} catch {
|
||||
return [];
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@
|
||||
<li><a href="https://github.com/bretzel-app/crumbs/blob/main/docs/DEPLOYMENT.md" target="_blank" rel="noopener">Docs</a></li>
|
||||
<li><a href="https://github.com/bretzel-app/crumbs/blob/main/LICENSE" target="_blank" rel="noopener">License</a></li>
|
||||
</ul>
|
||||
<span class="footer-version">v0.7.0</span>
|
||||
<span class="footer-version">v0.7.2</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user