fix sitemap

This commit is contained in:
adueck 2023-12-18 14:31:42 +04:00
parent 35a093c14d
commit f914f3470b
5 changed files with 66 additions and 28 deletions

View File

@ -227,8 +227,10 @@ async function upload(content: Buffer | string, filename: string) {
metadata: { metadata: {
contentType: isBuffer contentType: isBuffer
? "application/octet-stream" ? "application/octet-stream"
: filename.slice(-5) === ".json" : filename.endsWith(".json")
? "application/json" ? "application/json"
: filename.endsWith(".xml")
? "application/xml"
: "text/plain; charset=UTF-8", : "text/plain; charset=UTF-8",
cacheControl: "no-cache", cacheControl: "no-cache",
}, },
@ -274,14 +276,21 @@ async function uploadDictionaryToStorage(dictionary: T.Dictionary) {
} }
function makeSitemap(dictionary: T.Dictionary): string { function makeSitemap(dictionary: T.Dictionary): string {
const pages = [
...["", "/about", "/settings", "/account", "/phrase-builder"],
...dictionary.entries.map((x) => `/word?id=${x.ts}`),
];
return `<?xml version="1.0" encoding="UTF-8"?> return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${dictionary.entries.map( ${pages
(entry) => .map(
` <url> (page) =>
<loc>https://dictionary.lingdocs.com/word?id=${entry.ts}</loc> `
<url>
<loc>https://dictionary.lingdocs.com${page}}</loc>
</url>` </url>`
)} )
.join("")}
</urlset> </urlset>
`; `;
} }

View File

@ -36,9 +36,11 @@ function Entry({
data-testid="entry" data-testid="entry"
> >
<div> <div>
<strong> <dt>
<InlinePs opts={textOptions}>{{ p: entry.p, f: entry.f }}</InlinePs> <strong>
</strong> <InlinePs opts={textOptions}>{{ p: entry.p, f: entry.f }}</InlinePs>
</strong>
</dt>
{` `} {` `}
<em>{entry.c}</em> <em>{entry.c}</em>
{entry.a && !nonClickable && ( {entry.a && !nonClickable && (
@ -49,7 +51,9 @@ function Entry({
)} )}
</div> </div>
<ExtraEntryInfo entry={entry} textOptions={textOptions} /> <ExtraEntryInfo entry={entry} textOptions={textOptions} />
<div className="entry-definition">{entry.e}</div> <dd>
<div className="entry-definition">{entry.e}</div>
</dd>
</div> </div>
); );
} }

View File

@ -0,0 +1,32 @@
import { Types as T, InlinePs } from "@lingdocs/ps-react";
import { getAudioPath } from "./PlayStorageAudio";
export function EntryAudioDisplay({
entry,
opts,
}: {
entry: T.DictionaryEntry;
opts: T.TextOptions;
}) {
if (!entry.a) {
return null;
}
return (
<figure>
<figcaption className="mb-1">
Listen to <InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
</figcaption>
<audio
controls
controlsList="nofullscreen"
src={getAudioPath(entry.ts)}
preload="auto"
>
<a href={getAudioPath(entry.ts)}>
Download audio for{" "}
<InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
</a>
</audio>
</figure>
);
}

View File

@ -1,6 +1,10 @@
export function getAudioPath(ts: number): string {
return `https://storage.lingdocs.com/audio/${ts}.mp3`;
}
export default function playStorageAudio(ts: number, callback: () => void) { export default function playStorageAudio(ts: number, callback: () => void) {
if (!ts) return; if (!ts) return;
let audio = new Audio(`https://storage.lingdocs.com/audio/${ts}.mp3`); let audio = new Audio(getAudioPath(ts));
audio.addEventListener("ended", () => { audio.addEventListener("ended", () => {
callback(); callback();
audio.remove(); audio.remove();

View File

@ -34,7 +34,7 @@ import { Modal } from "react-bootstrap";
import { getTextOptions } from "../lib/get-text-options"; import { getTextOptions } from "../lib/get-text-options";
import { entryFeeder } from "../lib/dictionary"; import { entryFeeder } from "../lib/dictionary";
import { State, DictionaryAPI } from "../types/dictionary-types"; import { State, DictionaryAPI } from "../types/dictionary-types";
import playStorageAudio from "../components/PlayStorageAudio"; import { EntryAudioDisplay } from "../components/EntryAudioDisplay";
function IsolatedEntry({ function IsolatedEntry({
state, state,
@ -46,7 +46,6 @@ function IsolatedEntry({
isolateEntry: (ts: number) => void; isolateEntry: (ts: number) => void;
}) { }) {
const [exploded, setExploded] = useState<boolean>(false); const [exploded, setExploded] = useState<boolean>(false);
const [playing, setPlaying] = useState<boolean>(false);
const [editing, setEditing] = useState<boolean>(false); const [editing, setEditing] = useState<boolean>(false);
const [comment, setComment] = useState<string>(""); const [comment, setComment] = useState<string>("");
const [editSubmitted, setEditSubmitted] = useState<boolean>(false); const [editSubmitted, setEditSubmitted] = useState<boolean>(false);
@ -57,7 +56,6 @@ function IsolatedEntry({
setEditing(false); setEditing(false);
setComment(""); setComment("");
setEditSubmitted(false); setEditSubmitted(false);
setPlaying(false);
}, [state]); }, [state]);
function flashClippedMessage(m: string) { function flashClippedMessage(m: string) {
setShowClipped(m); setShowClipped(m);
@ -154,17 +152,14 @@ function IsolatedEntry({
navigator.clipboard.writeText(JSON.stringify(entry)); navigator.clipboard.writeText(JSON.stringify(entry));
flashClippedMessage("entry data copied to clipboard"); flashClippedMessage("entry data copied to clipboard");
} }
function handlePlayStorageAudio() {
if (!entry) return;
setPlaying(true);
playStorageAudio(entry.ts, () => {
setPlaying(false);
});
}
return ( return (
<div className="wide-width-limiter"> <div className="wide-width-limiter">
<Helmet> <Helmet>
<title>{entry.p} - LingDocs Pashto Dictionary</title> <title>{entry.p} - LingDocs Pashto Dictionary</title>
<link
rel="canonical"
href={`https://dictionary.lingdocs.com/word?id=${entry.ts}`}
/>
</Helmet> </Helmet>
<div className="row"> <div className="row">
<div className="col-8"> <div className="col-8">
@ -177,13 +172,6 @@ function IsolatedEntry({
</div> </div>
<div className="col-4"> <div className="col-4">
<div className="d-flex flex-row justify-content-end"> <div className="d-flex flex-row justify-content-end">
{entry.a && (
<div className="clickable mr-3" onClick={handlePlayStorageAudio}>
<i
className={`fas fa-lg fa-volume-${playing ? "down" : "off"}`}
/>
</div>
)}
<div <div
className="clickable mr-3" className="clickable mr-3"
onClick={() => setExploded((os) => !os)} onClick={() => setExploded((os) => !os)}
@ -239,6 +227,7 @@ function IsolatedEntry({
</div> </div>
</div> </div>
</div> </div>
<EntryAudioDisplay entry={entry} opts={textOptions} />
{wordlistWord && ( {wordlistWord && (
<> <>
{hasAttachment(wordlistWord, "audio") && ( {hasAttachment(wordlistWord, "audio") && (