mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
group of bug fixes
This commit is contained in:
1 parent
cfc93fc97d
commit
bcd5eb9ebc
30 files changed
+973
-238
No files matched your search
+49
-7
@@ -7,6 +7,7 @@ import {
|
||||
gdriveDisconnect,
|
||||
gdriveRestore,
|
||||
gdriveStatus,
|
||||
gdriveSync,
|
||||
} from "../backup";
|
||||
import { isDesktop } from "../ipc";
|
||||
import { useBook } from "./useBook";
|
||||
@@ -18,6 +19,7 @@ interface BackupState {
|
||||
email: string | null;
|
||||
lastBackup: number | null;
|
||||
pending: boolean;
|
||||
needsReauth: boolean;
|
||||
phase: Phase;
|
||||
error: string | null;
|
||||
authUrl: string | null;
|
||||
@@ -33,6 +35,7 @@ interface BackupState {
|
||||
copyAuthUrl: () => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
backup: (silent?: boolean) => Promise<void>;
|
||||
sync: (silent?: boolean) => Promise<void>;
|
||||
restore: () => Promise<void>;
|
||||
restoreFromDrive: () => Promise<void>;
|
||||
openSettings: () => void;
|
||||
@@ -43,11 +46,31 @@ function notify(message: string) {
|
||||
useBook.getState().setNotice(message);
|
||||
}
|
||||
|
||||
const RECONNECT = /Connect again/;
|
||||
|
||||
function describe(error: unknown, fallback: string): string {
|
||||
const message = String(error).replace(/^Error:\s*/, "");
|
||||
return RECONNECT.test(message) ? message : `${fallback}: ${message}`;
|
||||
}
|
||||
|
||||
function plural(n: number, word: string): string {
|
||||
return `${n} ${word}${n === 1 ? "" : "s"}`;
|
||||
}
|
||||
|
||||
function syncMessage(uploaded: number, downloaded: number): string {
|
||||
if (!uploaded && !downloaded) return "Everything is already in sync";
|
||||
const parts = [];
|
||||
if (downloaded) parts.push(`brought back ${plural(downloaded, "file")}`);
|
||||
if (uploaded) parts.push(`backed up ${plural(uploaded, "file")}`);
|
||||
return `Synced with Google Drive: ${parts.join(", ")}`;
|
||||
}
|
||||
|
||||
export const useBackup = create<BackupState>((set, get) => ({
|
||||
connected: false,
|
||||
email: null,
|
||||
lastBackup: null,
|
||||
pending: false,
|
||||
needsReauth: false,
|
||||
phase: "idle",
|
||||
error: null,
|
||||
authUrl: null,
|
||||
@@ -60,6 +83,7 @@ export const useBackup = create<BackupState>((set, get) => ({
|
||||
email: status.email,
|
||||
lastBackup: status.lastBackup,
|
||||
pending: status.pending,
|
||||
needsReauth: status.needsReauth,
|
||||
}),
|
||||
refresh: async () => {
|
||||
if (!isDesktop) return;
|
||||
@@ -75,7 +99,7 @@ export const useBackup = create<BackupState>((set, get) => ({
|
||||
.then((url) => set({ authUrl: url }))
|
||||
.catch((e) => {
|
||||
set({ phase: "error", error: String(e), resolveConnect: null });
|
||||
notify(`Could not connect: ${e}`);
|
||||
notify(describe(e, "Could not connect"));
|
||||
resolve(false);
|
||||
});
|
||||
}),
|
||||
@@ -91,11 +115,13 @@ export const useBackup = create<BackupState>((set, get) => ({
|
||||
await get().refresh();
|
||||
set({ phase: "idle", authUrl: null, error: null, resolveConnect: null });
|
||||
notify("Connected to Google Drive");
|
||||
} else {
|
||||
set({ phase: "error", authUrl: null, error: error ?? "authorization failed", resolveConnect: null });
|
||||
notify(`Could not connect: ${error ?? "authorization failed"}`);
|
||||
await get().sync();
|
||||
resolve?.(true);
|
||||
return;
|
||||
}
|
||||
resolve?.(ok);
|
||||
set({ phase: "error", authUrl: null, error: error ?? "authorization failed", resolveConnect: null });
|
||||
notify(describe(error ?? "authorization failed", "Could not connect"));
|
||||
resolve?.(false);
|
||||
},
|
||||
openAuthUrl: () => {
|
||||
const url = get().authUrl;
|
||||
@@ -132,7 +158,22 @@ export const useBackup = create<BackupState>((set, get) => ({
|
||||
if (!silent) notify(outcome.uploaded > 0 ? "Backed up to Google Drive" : "Nothing new to back up");
|
||||
} catch (e) {
|
||||
set({ phase: "error", error: String(e) });
|
||||
if (!silent) notify(`Backup failed: ${e}`);
|
||||
await get().refresh();
|
||||
if (!silent || RECONNECT.test(String(e))) notify(describe(e, "Backup failed"));
|
||||
}
|
||||
},
|
||||
sync: async (silent = false) => {
|
||||
if (!get().connected || get().phase === "working") return;
|
||||
set({ phase: "working", error: null });
|
||||
try {
|
||||
const outcome = await gdriveSync();
|
||||
get().apply(outcome);
|
||||
set((s) => ({ phase: "idle", restoreNonce: outcome.downloaded > 0 ? s.restoreNonce + 1 : s.restoreNonce }));
|
||||
if (!silent) notify(syncMessage(outcome.uploaded, outcome.downloaded));
|
||||
} catch (e) {
|
||||
set({ phase: "error", error: String(e) });
|
||||
await get().refresh();
|
||||
if (!silent || RECONNECT.test(String(e))) notify(describe(e, "Sync failed"));
|
||||
}
|
||||
},
|
||||
restore: async () => {
|
||||
@@ -144,7 +185,8 @@ export const useBackup = create<BackupState>((set, get) => ({
|
||||
notify(result.restored > 0 ? `Restored ${result.restored} file${result.restored === 1 ? "" : "s"}` : "No backups found in Google Drive");
|
||||
} catch (e) {
|
||||
set({ phase: "error", error: String(e) });
|
||||
notify(`Restore failed: ${e}`);
|
||||
await get().refresh();
|
||||
notify(describe(e, "Restore failed"));
|
||||
}
|
||||
},
|
||||
restoreFromDrive: async () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
normalizeBook,
|
||||
} from "../model/book";
|
||||
import { loadActiveChapter, saveActiveChapter } from "../editor/positions";
|
||||
import { saveBook } from "../library";
|
||||
import { rememberLastBook, saveBook } from "../library";
|
||||
import { applyBookFonts, resetBookFonts } from "../book-style";
|
||||
|
||||
export const COVER_ID = "__cover__";
|
||||
@@ -70,10 +70,12 @@ export const useBook = create<BookState>((set, get) => ({
|
||||
const valid = saved === COVER_ID || normalized.chapters.some((c) => c.id === saved);
|
||||
const activeChapterId = valid ? (saved as string) : normalized.chapters[0]?.id ?? "";
|
||||
applyBookFonts(normalized.settings.fonts);
|
||||
rememberLastBook(normalized.id);
|
||||
set({ book: normalized, activeChapterId, dirty: false, pendingTitleFocus: null });
|
||||
},
|
||||
closeBook: () => {
|
||||
flushOutgoing(get().book, get().dirty);
|
||||
rememberLastBook(null);
|
||||
resetBookFonts();
|
||||
set({ book: null, activeChapterId: "", dirty: false, pendingTitleFocus: null });
|
||||
},
|
||||
@@ -163,6 +165,7 @@ export const useBook = create<BookState>((set, get) => ({
|
||||
const index = s.book.chapters.findIndex((c) => c.id === id);
|
||||
if (index === -1) return {};
|
||||
const chapters = s.book.chapters.filter((c) => c.id !== id);
|
||||
if (!chapters.length) chapters.push(createChapter(""));
|
||||
const activeChapterId =
|
||||
s.activeChapterId === id ? (chapters[index] ?? chapters[index - 1])?.id ?? COVER_ID : s.activeChapterId;
|
||||
return { dirty: true, activeChapterId, book: { ...s.book, chapters } };
|
||||
|
||||
Reference in new issue
Block a user