mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
feat(ui): surface save and export errors as toasts; add webkit backdrop-filter
This commit is contained in:
1 parent
c4b69f3641
commit
cb506af374
2 files changed
+57
-17
No files matched your search
+38
-17
@@ -16,6 +16,7 @@ function App() {
|
||||
const [editor, setEditor] = useState<TiptapEditor | null>(null);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [exportOpen, setExportOpen] = useState(false);
|
||||
const [notice, setNotice] = useState<string | null>(null);
|
||||
const title = useBook((s) => s.book.metadata.title);
|
||||
const author = useBook((s) => s.book.metadata.author);
|
||||
const chapters = useBook((s) => s.book.chapters);
|
||||
@@ -30,18 +31,36 @@ function App() {
|
||||
const chapter = chapters[idx];
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
const { book, path } = useBook.getState();
|
||||
const target = path ?? (await chooseSavePath(book));
|
||||
if (!target) return;
|
||||
await writeBook(book, target);
|
||||
markSaved(target);
|
||||
try {
|
||||
const { book, path } = useBook.getState();
|
||||
const target = path ?? (await chooseSavePath(book));
|
||||
if (!target) return;
|
||||
await writeBook(book, target);
|
||||
markSaved(target);
|
||||
setNotice("Saved");
|
||||
} catch (e) {
|
||||
setNotice(`Save failed: ${e}`);
|
||||
}
|
||||
}, [markSaved]);
|
||||
|
||||
const handleOpen = useCallback(async () => {
|
||||
const result = await openBook();
|
||||
if (result) replaceBook(result.book, result.path);
|
||||
try {
|
||||
const result = await openBook();
|
||||
if (result) replaceBook(result.book, result.path);
|
||||
} catch (e) {
|
||||
setNotice(`Open failed: ${e}`);
|
||||
}
|
||||
}, [replaceBook]);
|
||||
|
||||
const runExport = async (label: string, fn: (book: Book) => Promise<void>) => {
|
||||
setExportOpen(false);
|
||||
try {
|
||||
await fn(useBook.getState().book);
|
||||
} catch (e) {
|
||||
setNotice(`${label} export failed: ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
const mod = e.metaKey || e.ctrlKey;
|
||||
@@ -58,14 +77,11 @@ function App() {
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [handleSave, handleOpen]);
|
||||
|
||||
const runExport = async (fn: (book: Book) => Promise<void>) => {
|
||||
setExportOpen(false);
|
||||
try {
|
||||
await fn(useBook.getState().book);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!notice) return;
|
||||
const timer = setTimeout(() => setNotice(null), 4000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [notice]);
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
@@ -90,8 +106,8 @@ function App() {
|
||||
<>
|
||||
<div className="menu-backdrop" onClick={() => setExportOpen(false)} />
|
||||
<div className="menu">
|
||||
<button onClick={() => runExport(exportPdf)}>Export PDF…</button>
|
||||
<button onClick={() => runExport(exportEpub)}>Export EPUB…</button>
|
||||
<button onClick={() => runExport("PDF", exportPdf)}>Export PDF…</button>
|
||||
<button onClick={() => runExport("EPUB", exportEpub)}>Export EPUB…</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -129,6 +145,11 @@ function App() {
|
||||
</div>
|
||||
|
||||
{settingsOpen && <Settings onClose={() => setSettingsOpen(false)} />}
|
||||
{notice && (
|
||||
<div className="toast" onClick={() => setNotice(null)}>
|
||||
{notice}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user