- Icon component (Lucide shapes, ISC), currentColor, decorative (the control keeps its words in aria-label/title). - Colour roles: --action (primary, filled), --ok (approve, publish), --danger (log out, delete, reject); .btn variants and .icon-btn; light and dark. - Header: admin, moderation, saved, word book, account/sign in, font, theme as icons; log out in red. - Review: save/send filled, approve/publish green, return plain, reject red, with icons. Account: save, log out, password, delete. Reading pages: edit, history, arrange, tag. E-books: add, download, covers; the reader's controls as icons. - نشانات is now محفوظ شدہ; favourite writers have their own page, پسندیدہ لکھاری (/writers). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
6.9 KiB
Plaintext
137 lines
6.9 KiB
Plaintext
---
|
|
// محفوظ شدہ: the reader's saved books/chapters, works, couplets, phrases and e-book pages, each with its full path in
|
|
// the site (poet » book » chapter » work) and a link to the couplet. The word book is separate: /words.
|
|
import Base from '../layouts/Base.astro';
|
|
import { asUser, COOKIE } from '../lib/auth';
|
|
import { ud } from '../lib/urdu';
|
|
import { phraseParts, phraseRe } from '../lib/phrases';
|
|
import LibraryNav from '../components/LibraryNav.astro';
|
|
|
|
const me = Astro.locals.user;
|
|
if (!me) return Astro.redirect('/signin?next=/library');
|
|
const token = Astro.cookies.get(COOKIE)!.value;
|
|
if (Astro.request.method === 'POST') {
|
|
const f = await Astro.request.formData(), id = Number(f.get('id')) || 0;
|
|
if (f.get('act') === 'delete') await asUser(token, `/api/library/${id}/delete`, {});
|
|
if (f.get('act') === 'note') await asUser(token, `/api/library/${id}/note`, { note: f.get('note') });
|
|
return Astro.redirect(`/library#i${id}`, 303);
|
|
}
|
|
const lib = (await asUser(token, '/api/library')).data;
|
|
const total = lib.categories.length + lib.poems.length + lib.couplets.length + lib.phrases.length + (lib.pages?.length ?? 0);
|
|
// a bookmarked phrase shown inside its couplet: [before, phrase, after] per line
|
|
const marked = (lines: string[], phrase: string) => phraseParts(lines, phrase).map((part, i) => {
|
|
if (!part) return [lines[i], '', ''];
|
|
const m = lines[i].match(phraseRe(part))!;
|
|
return [lines[i].slice(0, m.index), m[0], lines[i].slice(m.index! + m[0].length)];
|
|
});
|
|
const at = (item: any) => (item.poem ? `${item.poem.url}${item.poem.couplet != null ? `#c${item.poem.couplet}` : ''}` : '#');
|
|
---
|
|
<Base title="محفوظ شدہ">
|
|
<h1>{me.full_name || 'محفوظ شدہ'}</h1>
|
|
{me.bio && <p class="profile-bio">{me.bio}</p>}
|
|
<LibraryNav current="bookmarks" />
|
|
<p class="muted center">{ud(total)} محفوظ شدہ</p>
|
|
|
|
{total === 0 && (
|
|
<p class="muted center">ابھی کچھ محفوظ نہیں۔ کسی کتاب، باب یا کلام پر "محفوظ کریں" دبائیں، کسی شعر کے ساتھ والا نشان دبائیں، یا کوئی عبارت منتخب کر کے "محفوظ کریں" دبائیں۔</p>
|
|
)}
|
|
|
|
{lib.categories.length > 0 && (
|
|
<section class="lib-section">
|
|
<h2>محفوظ کتابیں اور ابواب</h2>
|
|
<ul class="lib-list">
|
|
{lib.categories.map((c: any) => (
|
|
<li id={`i${c.id}`}>
|
|
{c.path.length ? <>
|
|
<nav class="crumbs">{c.path.slice(0, -1).map((x: any) => <span><a href={x.url}>{x.title}</a></span>)}</nav>
|
|
<a class="lib-title" href={c.path.at(-1).url}>{c.path.at(-1).title}</a>
|
|
</> : <span class="muted">یہ کتاب اب دستیاب نہیں</span>}
|
|
<form method="post" class="lib-remove"><input type="hidden" name="id" value={c.id} /><button name="act" value="delete" aria-label="ہٹائیں">✕</button></form>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
)}
|
|
|
|
{lib.poems.length > 0 && (
|
|
<section class="lib-section">
|
|
<h2>محفوظ کلام</h2>
|
|
<ul class="lib-list">
|
|
{lib.poems.map((p: any) => (
|
|
<li id={`i${p.id}`}>
|
|
{p.poem ? <>
|
|
<nav class="crumbs">{p.poem.path.map((c: any) => <span><a href={c.url}>{c.title}</a></span>)}</nav>
|
|
<a class="lib-title" href={p.poem.url}>{p.poem.title}</a>
|
|
</> : <span class="muted">یہ کلام اب دستیاب نہیں</span>}
|
|
<form method="post" class="lib-remove"><input type="hidden" name="id" value={p.id} /><button name="act" value="delete" aria-label="ہٹائیں">✕</button></form>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
)}
|
|
|
|
{lib.couplets.length > 0 && (
|
|
<section class="lib-section">
|
|
<h2>محفوظ اشعار</h2>
|
|
<ul class="lib-list">
|
|
{lib.couplets.map((c: any) => (
|
|
<li id={`i${c.id}`}>
|
|
{c.poem ? <>
|
|
<nav class="crumbs">{[...c.poem.path, { title: c.poem.title, url: c.poem.url }].map((x: any) => <span><a href={x.url}>{x.title}</a></span>)}</nav>
|
|
<a class="lib-couplet" href={at(c)}>{c.lines.map((l: string) => <p>{l}</p>)}</a>
|
|
</> : <span class="muted">یہ شعر اب دستیاب نہیں</span>}
|
|
<form method="post" class="lib-note">
|
|
<input type="hidden" name="id" value={c.id} />
|
|
<input name="note" value={c.note ?? ''} placeholder="نوٹ" maxlength="1000" dir="auto" />
|
|
<button name="act" value="note">محفوظ</button>
|
|
</form>
|
|
<form method="post" class="lib-remove"><input type="hidden" name="id" value={c.id} /><button name="act" value="delete" aria-label="ہٹائیں">✕</button></form>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
)}
|
|
|
|
{lib.phrases.length > 0 && (
|
|
<section class="lib-section">
|
|
<h2>محفوظ عبارتیں</h2>
|
|
<ul class="lib-list">
|
|
{lib.phrases.map((p: any) => (
|
|
<li id={`i${p.id}`}>
|
|
{p.poem ? <>
|
|
<nav class="crumbs">{[...p.poem.path, { title: p.poem.title, url: p.poem.url }].map((x: any) => <span><a href={x.url}>{x.title}</a></span>)}</nav>
|
|
<a class="lib-couplet" href={at(p)}>{marked(p.lines, p.phrase).map(([a, m, z]) => <p>{a}{m && <span class="bmk">{m}</span>}{z}</p>)}</a>
|
|
</> : <span class="muted">یہ عبارت اب دستیاب نہیں</span>}
|
|
<form method="post" class="lib-note">
|
|
<input type="hidden" name="id" value={p.id} />
|
|
<input name="note" value={p.note ?? ''} placeholder="نوٹ" maxlength="1000" dir="auto" />
|
|
<button name="act" value="note">محفوظ</button>
|
|
</form>
|
|
<form method="post" class="lib-remove"><input type="hidden" name="id" value={p.id} /><button name="act" value="delete" aria-label="ہٹائیں">✕</button></form>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
)}
|
|
|
|
{lib.pages?.length > 0 && (
|
|
<section class="lib-section">
|
|
<h2>کتابوں کے محفوظ صفحات</h2>
|
|
<ul class="lib-list">
|
|
{lib.pages.map((p: any) => (
|
|
<li id={`i${p.id}`}>
|
|
<nav class="crumbs"><span><a href={p.ebook.poet_url}>{p.ebook.poet}</a></span><span><a href={`${p.ebook.poet_url}/ebooks`}>ای بکس</a></span></nav>
|
|
<a class="lib-title" href={`/ebook/${p.ebook.id}?page=${p.page}`}>{p.ebook.title} · صفحہ {ud(p.page)}</a>
|
|
<form method="post" class="lib-note">
|
|
<input type="hidden" name="id" value={p.id} />
|
|
<input name="note" value={p.note ?? ''} placeholder="نوٹ" maxlength="1000" dir="auto" />
|
|
<button name="act" value="note">محفوظ</button>
|
|
</form>
|
|
<form method="post" class="lib-remove"><input type="hidden" name="id" value={p.id} /><button name="act" value="delete" aria-label="ہٹائیں">✕</button></form>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
)}
|
|
</Base>
|