pwa and mobile client
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CODE_TYPE_LABELS, textColorOn, type Card } from '@cardy/shared';
|
||||
import { setMaxBrightness } from '../platform.js';
|
||||
import { Banner, CardBadge, CodeImage, ConfirmDialog, Icon, IconButton, Screen, useStoredImage } from '../ui.js';
|
||||
|
||||
export function CardScreen({
|
||||
card,
|
||||
imageEpoch,
|
||||
onBack,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: {
|
||||
card: Card;
|
||||
imageEpoch: number;
|
||||
onBack: () => void;
|
||||
onEdit: () => void;
|
||||
onDelete: () => Promise<void>;
|
||||
}) {
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const [viewing, setViewing] = useState<string>();
|
||||
|
||||
// Scanners at the till read the code better at full brightness.
|
||||
useEffect(() => {
|
||||
void setMaxBrightness(true);
|
||||
return () => void setMaxBrightness(false);
|
||||
}, []);
|
||||
|
||||
const remove = async () => {
|
||||
setConfirmDelete(false);
|
||||
setDeleting(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
await onDelete();
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
setDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Screen
|
||||
className="card-screen"
|
||||
title={card.name}
|
||||
left={<IconButton icon="back" label="Back" onClick={onBack} />}
|
||||
right={
|
||||
<>
|
||||
<IconButton icon="edit" label="Edit" onClick={onEdit} disabled={deleting} />
|
||||
<IconButton icon="trash" label="Delete" onClick={() => setConfirmDelete(true)} disabled={deleting} />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="card-hero" style={{ background: card.color, color: textColorOn(card.color) }}>
|
||||
<CardBadge card={card} size={56} version={imageEpoch} />
|
||||
<div className="card-hero-name">{card.name}</div>
|
||||
</div>
|
||||
|
||||
{error && <Banner onClose={() => setError(undefined)}>{error}</Banner>}
|
||||
|
||||
<CodeImage code={card.code} codeType={card.codeType} large />
|
||||
<div className="code-caption">
|
||||
<span className="mono">{card.code}</span>
|
||||
<span className="muted">{CODE_TYPE_LABELS[card.codeType]}</span>
|
||||
</div>
|
||||
|
||||
{card.comment && <p className="card-comment">{card.comment}</p>}
|
||||
|
||||
{card.images.length > 0 && (
|
||||
<div className="gallery">
|
||||
{card.images.map((img) => (
|
||||
<Thumb key={img.id} cardId={card.id} imageId={img.id} version={imageEpoch} onOpen={() => setViewing(img.id)} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{viewing && <ImageViewer cardId={card.id} imageId={viewing} version={imageEpoch} onClose={() => setViewing(undefined)} />}
|
||||
|
||||
{confirmDelete && (
|
||||
<ConfirmDialog
|
||||
title="Delete card?"
|
||||
message={`“${card.name}” will be removed for everyone in your group.`}
|
||||
confirmLabel="Delete"
|
||||
danger
|
||||
onCancel={() => setConfirmDelete(false)}
|
||||
onConfirm={remove}
|
||||
/>
|
||||
)}
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
|
||||
function Thumb({ cardId, imageId, version, onOpen }: { cardId: string; imageId: string; version: number; onOpen: () => void }) {
|
||||
const url = useStoredImage(cardId, imageId, version);
|
||||
return (
|
||||
<button className="thumb" onClick={onOpen} disabled={!url} aria-label="Open image">
|
||||
{url ? <img src={url} alt="" /> : <span className="muted small">Not downloaded</span>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function ImageViewer({ cardId, imageId, version, onClose }: { cardId: string; imageId: string; version: number; onClose: () => void }) {
|
||||
const url = useStoredImage(cardId, imageId, version);
|
||||
return (
|
||||
<div className="viewer" onClick={onClose}>
|
||||
{url && <img src={url} alt="" />}
|
||||
<button className="icon-button viewer-close" aria-label="Close">
|
||||
<Icon name="close" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user