Compare commits

...

2 Commits

5 changed files with 24 additions and 8 deletions

View File

@ -5,13 +5,16 @@ import '@fontsource/noto-naskh-arabic/700.css';
import '@fontsource/noto-nastaliq-urdu/400.css'; import '@fontsource/noto-nastaliq-urdu/400.css';
import '@fontsource/noto-nastaliq-urdu/700.css'; import '@fontsource/noto-nastaliq-urdu/700.css';
import '../styles/global.css'; import '../styles/global.css';
interface Props { title?: string; description?: string; q?: string; author?: string; jsonld?: object } interface Props { title?: string; description?: string; q?: string; author?: string; jsonld?: object; ogType?: string }
// author and jsonld describe a work for search engines and the browser's own reader view // author and jsonld describe a work for search engines and the browser's own reader view
const { title, description = 'اردو کا کلاسیکی ادب', q = '', author, jsonld } = Astro.props; const { title, description = 'اردو کا کلاسیکی ادب', q = '', author, jsonld, ogType = 'website' } = Astro.props;
// one address per page for search engines: the public origin, no query (?hl=, ?font=, ?kind= are views of the same page)
const canonical = siteUrl(Astro.url) + Astro.url.pathname;
const fullTitle = title ? `${title} · ${brand.name}` : `${brand.name} · ${brand.tagline}`; const fullTitle = title ? `${title} · ${brand.name}` : `${brand.name} · ${brand.tagline}`;
import Icon from '../components/Icon.astro'; import Icon from '../components/Icon.astro';
import logo from '../../public/logo/turanj.svg?raw'; // inline, so the page can turn its rays import logo from '../../public/logo/turanj.svg?raw'; // inline, so the page can turn its rays
import { brand } from '../lib/brand'; import { brand } from '../lib/brand';
import { siteUrl } from '../lib/site-url';
--- ---
<!doctype html> <!doctype html>
<html lang="ur-PK" dir="rtl"> <html lang="ur-PK" dir="rtl">
@ -20,6 +23,7 @@ import { brand } from '../lib/brand';
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{fullTitle}</title> <title>{fullTitle}</title>
<meta name="description" content={description} /> <meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" /> <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="alternate" type="application/rss+xml" title={`${brand.name}: نئی تبدیلیاں`} href="/rss.xml" /> <link rel="alternate" type="application/rss+xml" title={`${brand.name}: نئی تبدیلیاں`} href="/rss.xml" />
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" /> <link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
@ -29,11 +33,11 @@ import { brand } from '../lib/brand';
<meta name="theme-color" content="#14120f" media="(prefers-color-scheme: dark)" /> <meta name="theme-color" content="#14120f" media="(prefers-color-scheme: dark)" />
<!-- link previews (WhatsApp, Facebook, X, Slack): the logo and headline --> <!-- link previews (WhatsApp, Facebook, X, Slack): the logo and headline -->
<meta property="og:site_name" content={brand.name} /> <meta property="og:site_name" content={brand.name} />
<meta property="og:type" content="website" /> <meta property="og:type" content={ogType} />
<meta property="og:locale" content="ur_PK" /> <meta property="og:locale" content="ur_PK" />
<meta property="og:title" content={fullTitle} /> <meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />
<meta property="og:url" content={new URL(Astro.url.pathname, Astro.url.origin).href} /> <meta property="og:url" content={canonical} />
<meta property="og:image" content={new URL('/og-image.png', Astro.url.origin).href} /> <meta property="og:image" content={new URL('/og-image.png', Astro.url.origin).href} />
<meta property="og:image:width" content="1200" /> <meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" /> <meta property="og:image:height" content="630" />

View File

@ -88,10 +88,21 @@ const ebookCount = page.type === 'poet' ? ((await api<any[]>(`/api/ebooks?poet=$
const tagTarget = page.type === 'poem' ? `work/${poemId}` : `category/${page.category?.id}`; const tagTarget = page.type === 'poem' ? `work/${poemId}` : `category/${page.category?.id}`;
const bookmark = (c: number) => JSON.stringify({ kind: 'couplet', poemId, couplet: c }); const bookmark = (c: number) => JSON.stringify({ kind: 'couplet', poemId, couplet: c });
const title = page.type === 'poem' ? page.poem.title : page.type === 'poet' ? page.poet.nickname : page.category.title; const title = page.type === 'poem' ? page.poem.title : page.type === 'poet' ? page.poet.nickname : page.category.title;
// for search engines and link previews: a work's opening lines, a poet's intro, a book's contents (#83)
const clip = (t: string) => (t.length > 160 ? t.slice(0, 159) + '…' : t);
const description = clip(page.type === 'poem' ? `${blocks[0]?.lines.join(' / ') ?? page.poem.title} (${page.poet.nickname})`
: page.type === 'poet' ? page.poet.description || `${page.poet.nickname}: کلام، کتابیں اور ای بکس`
: `${page.category.title} · ${page.poet.nickname}: ${page.poems?.length ? `${ud(page.poems.length)} تخلیقات` : `${ud(page.children?.length ?? 0)} حصے`}`);
const here = siteUrl(Astro.url) + url, poetLd = { '@type': 'Person', name: page.poet?.nickname, url: siteUrl(Astro.url) + (crumbs[0]?.url ?? '') };
const jsonld = { '@context': 'https://schema.org', inLanguage: 'ur', url: here, ...(
page.type === 'poem' ? { '@type': 'CreativeWork', name: page.poem.title, author: poetLd, isPartOf: crumbs.at(-1)?.title }
: page.type === 'poet' ? { ...poetLd, url: here, description: page.poet.description || undefined }
: { '@type': 'CollectionPage', name: page.category.title, author: poetLd }) };
import { siteUrl } from '../lib/site-url';
import Icon from '../components/Icon.astro'; import Icon from '../components/Icon.astro';
import TagsBox from '../components/TagsBox.astro'; import TagsBox from '../components/TagsBox.astro';
--- ---
<Base author={page.poet?.nickname} jsonld={page.type === 'poem' ? { '@context': 'https://schema.org', '@type': 'CreativeWork', name: page.poem.title, inLanguage: 'ur', author: { '@type': 'Person', name: page.poet.nickname }, isPartOf: crumbs.at(-1)?.title, url: Astro.url.href.split('?')[0] } : undefined} title={page.type === 'poem' ? `${page.poem.title} · ${page.poet.nickname}` : title} description={page.type === 'poet' ? page.poet.description?.slice(0, 160) : undefined}> <Base author={page.poet?.nickname} jsonld={jsonld} title={page.type === 'poem' ? `${page.poem.title} · ${page.poet.nickname}` : title} description={description} ogType={page.type === 'poem' ? 'article' : 'website'}>
{page.type !== 'poet' && ( {page.type !== 'poet' && (
<nav class="crumbs"> <nav class="crumbs">
{crumbs.map((c: any, i: number) => ( {crumbs.map((c: any, i: number) => (

View File

@ -20,7 +20,7 @@ const mayAdd = token && ['mod-l2', 'mod-l1', 'admin'].includes(Astro.locals.user
&& (await asUser(token, `/api/mod/can?category=${page.category.id}`)).data.ebooks; && (await asUser(token, `/api/mod/can?category=${page.category.id}`)).data.ebooks;
import Icon from '../../components/Icon.astro'; import Icon from '../../components/Icon.astro';
--- ---
<Base title={`ای بکس · ${page.poet.nickname}`}> <Base title={`ای بکس · ${page.poet.nickname}`} description={`${page.poet.nickname} کی ای بکس: پی ڈی ایف، ای پب اور یونیکوڈ متن`}>
<nav class="crumbs"><a href={page.poet.url}>{page.poet.nickname}</a></nav> <nav class="crumbs"><a href={page.poet.url}>{page.poet.nickname}</a></nav>
<h1>ای بکس</h1> <h1>ای بکس</h1>
{mayAdd && <p class="center"><a class="btn primary" href={`/mod/ebook/new?poet=${page.poet.id}`}><Icon name="plus" size={18} /> ای بک شامل کریں</a></p>} {mayAdd && <p class="center"><a class="btn primary" href={`/mod/ebook/new?poet=${page.poet.id}`}><Icon name="plus" size={18} /> ای بک شامل کریں</a></p>}

View File

@ -20,7 +20,8 @@ const marks = token && b.published && b.kind === 'pdf' ? (await asUser(token, `/
const mb = !b.size ? '' : b.size < 1048576 ? `${ud(Math.max(1, Math.round(b.size / 1024)))} KB` : `${ud((b.size / 1048576).toFixed(1))} MB`; const mb = !b.size ? '' : b.size < 1048576 ? `${ud(Math.max(1, Math.round(b.size / 1024)))} KB` : `${ud((b.size / 1048576).toFixed(1))} MB`;
import Icon from '../../components/Icon.astro'; import Icon from '../../components/Icon.astro';
--- ---
<Base title={`${b.title} · ${b.poet}`}> <Base title={`${b.title} · ${b.poet}`} author={b.poet} ogType="book" description={`${b.title}: ${b.poet}${b.coauthors.length ? '، ' + b.coauthors.map((c: any) => c.name).join('، ') : ''} کی کتاب، ${KIND[b.kind]} میں`}
jsonld={{ '@context': 'https://schema.org', '@type': 'Book', name: b.title, inLanguage: 'ur', author: [b.poet, ...b.coauthors.map((c: any) => c.name)].map((name) => ({ '@type': 'Person', name })) }}>
<nav class="crumbs"><a href={b.poet_url}>{b.poet}</a> » <a href={`${b.poet_url}/ebooks?kind=${b.kind}`}>ای بکس</a></nav> <nav class="crumbs"><a href={b.poet_url}>{b.poet}</a> » <a href={`${b.poet_url}/ebooks?kind=${b.kind}`}>ای بکس</a></nav>
<h1>{b.title}</h1> <h1>{b.title}</h1>
{!b.published && <p class="form-error center">یہ کتاب ابھی جائزے میں ہے؛ صرف موڈریٹر دیکھ سکتے ہیں۔</p>} {!b.published && <p class="form-error center">یہ کتاب ابھی جائزے میں ہے؛ صرف موڈریٹر دیکھ سکتے ہیں۔</p>}

View File

@ -10,7 +10,7 @@ if (!res) return new Response(null, { status: 404, statusText: 'Not found' });
const { tag, categories, works, ebooks = [] } = res; const { tag, categories, works, ebooks = [] } = res;
const count = categories.length + works.length + ebooks.length; const count = categories.length + works.length + ebooks.length;
--- ---
<Base title={`${tag.type}: ${tag.name}`}> <Base title={`${tag.type}: ${tag.name}`} description={`${tag.type} «${tag.name}»: ${ud(count)} شاعر، کتابیں، تخلیقات اور اشعار`}>
<h1><small class="muted">{tag.type}:</small> {tag.name}</h1> <h1><small class="muted">{tag.type}:</small> {tag.name}</h1>
<p class="center muted">{ud(count)} جگہ · <a href={`/search?q=${encodeURIComponent(`tag:"${tag.name}"`)}`}>تلاش میں دیکھیں</a> · <a href="/tags">تمام ٹیگ</a></p> <p class="center muted">{ud(count)} جگہ · <a href={`/search?q=${encodeURIComponent(`tag:"${tag.name}"`)}`}>تلاش میں دیکھیں</a> · <a href="/tags">تمام ٹیگ</a></p>
{categories.length > 0 && ( {categories.length > 0 && (