Wire scanner feedback toasts and restore rescan uploads (#157)
Some checks are pending
Some checks are pending
This commit is contained in:
parent
8a5ec11573
commit
70be99421d
3 changed files with 37 additions and 14 deletions
|
|
@ -63,6 +63,7 @@ function readFileToImageData(file) {
|
|||
|
||||
export function useScannerIdentification({
|
||||
onCardScanned,
|
||||
onScanImageUploaded,
|
||||
onError,
|
||||
onTrackerComplete,
|
||||
onTrackerReset,
|
||||
|
|
@ -102,15 +103,20 @@ export function useScannerIdentification({
|
|||
}, 3000);
|
||||
|
||||
// Show result immediately — don't block on upload
|
||||
onCardScanned(buildScannedCardPayload(finalCard, { imageData, scanImageUrl: null, ocrMeta }));
|
||||
const queueCardId = onCardScanned(
|
||||
buildScannedCardPayload(finalCard, { imageData, scanImageUrl: null, ocrMeta })
|
||||
);
|
||||
finishTrackedCard(cardTracker);
|
||||
|
||||
// Upload capture in background (fire-and-forget)
|
||||
if (imageData) {
|
||||
if (imageData && queueCardId) {
|
||||
uploadScanCapture(imageData)
|
||||
.then((url) => {
|
||||
if (debugLog && isDebugMode) {
|
||||
debugLog('📤', `Background upload complete: ${url ? 'success' : '429'}`);
|
||||
if (url) {
|
||||
onScanImageUploaded?.(queueCardId, url);
|
||||
}
|
||||
if (isDebugMode()) {
|
||||
debugLog('📤', `Background upload complete: ${url ? 'success' : 'skipped'}`);
|
||||
}
|
||||
})
|
||||
.catch((uploadError) => {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ export function useScannerQueue({ user, sessionDestination, scanDefaults, deckMo
|
|||
persistCart(nextQueue, next);
|
||||
return next;
|
||||
});
|
||||
return cardEntry.id;
|
||||
};
|
||||
|
||||
const incrementCardQuantity = (cardId) => {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import { clearScannerCartStorage } from '../lib/scanner-session.js';
|
|||
import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js';
|
||||
|
||||
const TOAST_DURATION_MS = 2500;
|
||||
const NOTICE_TOAST_DURATION_MS = 8000;
|
||||
const ERROR_TOAST_DURATION_MS = 5000;
|
||||
|
||||
export default function Scanner() {
|
||||
const { user, loading: authLoading } = useAuth();
|
||||
|
|
@ -53,13 +55,24 @@ export default function Scanner() {
|
|||
const batchInputRef = useRef(null);
|
||||
const disambiguationActiveRef = useRef(false);
|
||||
|
||||
const showPageToast = useCallback((message, type = 'success', durationMs = TOAST_DURATION_MS) => {
|
||||
clearTimeout(toastTimerRef.current);
|
||||
setPageToast({ message, visible: true, type });
|
||||
toastTimerRef.current = setTimeout(() => {
|
||||
setPageToast((current) => ({ ...current, visible: false }));
|
||||
}, durationMs);
|
||||
}, []);
|
||||
|
||||
const { scanDefaults } = useScannerSession();
|
||||
const queue = useScannerQueue({ user, scanDefaults, deckMode: null });
|
||||
|
||||
const onVerifyCardRef = useRef(null);
|
||||
|
||||
const camera = useCameraScanner({
|
||||
onError: (msg) => console.error('Camera error:', msg),
|
||||
onError: (msg) => {
|
||||
console.error('Camera error:', msg);
|
||||
showPageToast(msg, 'error', ERROR_TOAST_DURATION_MS);
|
||||
},
|
||||
onVerifyCard: (cardTracker) => onVerifyCardRef.current?.(cardTracker),
|
||||
verificationPausedRef,
|
||||
autoDetectPausedRef,
|
||||
|
|
@ -67,7 +80,13 @@ export default function Scanner() {
|
|||
|
||||
const identification = useScannerIdentification({
|
||||
onCardScanned: queue.handleCardScanned,
|
||||
onError: (msg) => console.error('Identification error:', msg),
|
||||
onScanImageUploaded: (cardId, url) => {
|
||||
queue.updateCardMetadata(cardId, { scanImageUrl: url });
|
||||
},
|
||||
onError: (msg) => {
|
||||
console.error('Identification error:', msg);
|
||||
showPageToast(msg, 'error', ERROR_TOAST_DURATION_MS);
|
||||
},
|
||||
onTrackerComplete: camera.removeTrackedCard,
|
||||
onTrackerReset: camera.resetTrackedCard,
|
||||
videoRef: camera.videoRef,
|
||||
|
|
@ -102,6 +121,11 @@ export default function Scanner() {
|
|||
disambiguationActiveRef.current = Boolean(identification.disambiguation);
|
||||
}, [identification.disambiguation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!identification.scanNotice) return;
|
||||
showPageToast(identification.scanNotice, 'info', NOTICE_TOAST_DURATION_MS);
|
||||
}, [identification.scanNotice, showPageToast]);
|
||||
|
||||
useEffect(() => {
|
||||
const mobileCheckoutPauses =
|
||||
typeof window !== 'undefined' &&
|
||||
|
|
@ -137,14 +161,6 @@ export default function Scanner() {
|
|||
return () => clearTimeout(toastTimerRef.current);
|
||||
}, []);
|
||||
|
||||
const showPageToast = useCallback((message, type = 'success') => {
|
||||
clearTimeout(toastTimerRef.current);
|
||||
setPageToast({ message, visible: true, type });
|
||||
toastTimerRef.current = setTimeout(() => {
|
||||
setPageToast((current) => ({ ...current, visible: false }));
|
||||
}, TOAST_DURATION_MS);
|
||||
}, []);
|
||||
|
||||
const handleLeaveConfirm = () => {
|
||||
clearScannerCartStorage();
|
||||
queue.clearScannedCards();
|
||||
|
|
|
|||
Loading…
Reference in a new issue