feat: rebuild the book page with the jacket layout
This commit is contained in:
@@ -58,6 +58,52 @@ export function formatFileSize(bytes: number, decimals = 1, binary = true) {
|
||||
return `${formattedSize} ${units[exponent]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Presentation for a book identifier.
|
||||
*
|
||||
* The backend stores identifiers as free-form name/value pairs, so this only
|
||||
* decides how to label and link the ones we recognise. Anything unknown falls
|
||||
* back to the stored name and renders as plain text.
|
||||
*/
|
||||
const IDENTIFIER_TYPES: Record<string, { label: string; url?: (value: string) => string }> = {
|
||||
'isbn-13': { label: 'ISBN-13', url: (v) => `https://openlibrary.org/isbn/${isbnDigits(v)}` },
|
||||
'isbn-10': { label: 'ISBN-10', url: (v) => `https://openlibrary.org/isbn/${isbnDigits(v)}` },
|
||||
isbn: { label: 'ISBN', url: (v) => `https://openlibrary.org/isbn/${isbnDigits(v)}` },
|
||||
doi: { label: 'DOI', url: (v) => `https://doi.org/${encodeURI(v.trim())}` },
|
||||
asin: { label: 'ASIN', url: (v) => `https://www.amazon.com/dp/${encodeURIComponent(v.trim())}` }
|
||||
};
|
||||
|
||||
/** Order the ones worth reading first; everything else keeps its own order. */
|
||||
const IDENTIFIER_ORDER = ['isbn-13', 'isbn-10', 'isbn', 'doi', 'asin'];
|
||||
|
||||
function isbnDigits(value: string) {
|
||||
return value.replace(/[^0-9Xx]/g, '');
|
||||
}
|
||||
|
||||
function identifierKey(name: string) {
|
||||
return name.trim().toLowerCase().replace(/[\s_]+/g, '-');
|
||||
}
|
||||
|
||||
export function describeIdentifier(name: string, value: string) {
|
||||
const known = IDENTIFIER_TYPES[identifierKey(name)];
|
||||
|
||||
return {
|
||||
label: known?.label ?? name.toUpperCase(),
|
||||
href: known?.url?.(value)
|
||||
};
|
||||
}
|
||||
|
||||
export function sortIdentifiers(identifiers: Record<string, string>) {
|
||||
return Object.entries(identifiers).sort(([a], [b]) => {
|
||||
const ia = IDENTIFIER_ORDER.indexOf(identifierKey(a));
|
||||
const ib = IDENTIFIER_ORDER.indexOf(identifierKey(b));
|
||||
if (ia === -1 && ib === -1) return 0;
|
||||
if (ia === -1) return 1;
|
||||
if (ib === -1) return -1;
|
||||
return ia - ib;
|
||||
});
|
||||
}
|
||||
|
||||
export function getFileType(filename: string) {
|
||||
const parts = filename.split('.');
|
||||
if (parts.length < 2) return 'UNKNOWN';
|
||||
|
||||
Reference in New Issue
Block a user