mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -472,6 +472,7 @@ button {
|
||||
gap: 1px;
|
||||
padding: 5px;
|
||||
background: rgba(252, 251, 247, 0.86);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
@@ -627,6 +628,7 @@ button {
|
||||
inset: 0;
|
||||
z-index: 20;
|
||||
background: rgba(35, 32, 27, 0.28);
|
||||
-webkit-backdrop-filter: blur(2px);
|
||||
backdrop-filter: blur(2px);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -771,3 +773,20 @@ button {
|
||||
background: var(--accent-wash);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 26px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 40;
|
||||
max-width: 460px;
|
||||
padding: 11px 16px;
|
||||
border-radius: var(--r-md);
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--t-3);
|
||||
box-shadow: var(--shadow-pop);
|
||||
cursor: pointer;
|
||||
}
|
||||
Reference in new issue
Block a user