@@ -7,6 +7,7 @@ import { Loader2 } from "lucide-react";
77import { computeSha256HexInBrowser } from "@/lib/evidence/sha256" ;
88import { resolveEvidenceMime } from "@/lib/evidence/accepted-mime" ;
99import { validateUploadFile } from "@/lib/evidence/validate-file" ;
10+ import { prepareFileForUpload } from "@/lib/evidence/compress-for-upload" ;
1011import { classifyDoc , type DocClass } from "@/lib/evidence/readability" ;
1112import type { PaperDocDef } from "@/lib/intake/paper-display" ;
1213
@@ -101,10 +102,9 @@ const DEFAULT_EXTRA_DOCS: PaperDocDef[] = [
101102 } ,
102103] ;
103104
104- // Snappier feedback: the verify API itself is ~0.5s, so poll fast early. Total
105- // window stays ~60s (40 × 1.5s) before the calm background-check fallback.
106- const POLL_INTERVAL_MS = 1500 ;
107- const POLL_MAX_ATTEMPTS = 40 ;
105+ // Fast early polls while AI runs in after(); total window ~45s then background.
106+ const POLL_INTERVAL_MS = 800 ;
107+ const POLL_MAX_ATTEMPTS = 55 ;
108108
109109function docClassOf ( v : PaperVerification ) : DocClass | null {
110110 if ( ! v ) return null ;
@@ -236,12 +236,19 @@ export function PapersChecklist({
236236 patchRow ( type , { status : "error" , error : fileProblem } ) ;
237237 return ;
238238 }
239- const mimeType = resolveEvidenceMime ( file ) ;
239+ // Shrink phone photos before hash/PUT so upload + confirm are not multi-MB.
240+ let uploadFile = file ;
241+ try {
242+ uploadFile = await prepareFileForUpload ( file ) ;
243+ } catch {
244+ uploadFile = file ;
245+ }
246+ const mimeType = resolveEvidenceMime ( uploadFile ) ;
240247
241248 // Compute the hash up front so the same file can't be added to two slots.
242249 let sha256 : string ;
243250 try {
244- sha256 = await computeSha256HexInBrowser ( file ) ;
251+ sha256 = await computeSha256HexInBrowser ( uploadFile ) ;
245252 } catch {
246253 patchRow ( type , { status : "error" , error : t ( "fileReadError" ) } ) ;
247254 return ;
@@ -272,9 +279,9 @@ export function PapersChecklist({
272279 headers : authHeaders ( ) ,
273280 body : JSON . stringify ( {
274281 evidence_type : type ,
275- filename : file . name ,
282+ filename : uploadFile . name ,
276283 mime_type : mimeType ,
277- file_size_bytes : file . size ,
284+ file_size_bytes : uploadFile . size ,
278285 } ) ,
279286 } ,
280287 ) ;
@@ -285,7 +292,7 @@ export function PapersChecklist({
285292 const putRes = await fetch ( urlJson . upload_url , {
286293 method : "PUT" ,
287294 headers : { "Content-Type" : mimeType } ,
288- body : file ,
295+ body : uploadFile ,
289296 } ) ;
290297 if ( ! putRes . ok ) throw new Error ( t ( "storageError" ) ) ;
291298
@@ -309,36 +316,38 @@ export function PapersChecklist({
309316
310317 router . refresh ( ) ;
311318
312- if ( ! isCore ) {
313- // Extras show a simple "Added" — the seal + background check still run.
314- patchRow ( type , { status : "done" , pending : true } ) ;
315- return ;
316- }
317-
318- patchRow ( type , { status : "checking" } ) ;
319- const verification = await pollVerification ( urlJson . evidence_id ) ;
320- if ( verification === "timeout" ) {
321- patchRow ( type , { status : "done" , pending : true , verification : null } ) ;
322- } else {
323- patchRow ( type , { status : "done" , verification, pending : false } ) ;
324- }
325-
326- if ( type === "freeze_sms" ) {
327- // Deliver the "AI explains your notice" promise — advisory, never blocks.
328- try {
329- await fetch ( `/api/v1/cases/${ caseId } /notice-analysis` , {
330- method : "POST" ,
331- headers : authHeaders ( ) ,
332- body : JSON . stringify ( {
333- input_kind : "image" ,
334- evidence_id : urlJson . evidence_id ,
335- } ) ,
336- } ) ;
337- } catch {
338- // best-effort — the papers page still works without the explanation
319+ // Upload is DONE as soon as confirm returns. AI/OCR continues on the
320+ // server (after() + job queue) — do not block the user on model latency.
321+ patchRow ( type , { status : "done" , pending : true , verification : null } ) ;
322+
323+ const evidenceId = urlJson . evidence_id as string ;
324+ void ( async ( ) => {
325+ if ( isCore ) {
326+ const verification = await pollVerification ( evidenceId ) ;
327+ if ( verification !== "timeout" ) {
328+ patchRow ( type , {
329+ status : "done" ,
330+ verification,
331+ pending : false ,
332+ } ) ;
333+ router . refresh ( ) ;
334+ }
339335 }
340- }
341- router . refresh ( ) ;
336+ if ( type === "freeze_sms" ) {
337+ try {
338+ await fetch ( `/api/v1/cases/${ caseId } /notice-analysis` , {
339+ method : "POST" ,
340+ headers : authHeaders ( ) ,
341+ body : JSON . stringify ( {
342+ input_kind : "image" ,
343+ evidence_id : evidenceId ,
344+ } ) ,
345+ } ) ;
346+ } catch {
347+ // best-effort advisory — papers page still works without it
348+ }
349+ }
350+ } ) ( ) ;
342351 } catch ( error ) {
343352 patchRow ( type , {
344353 status : "error" ,
0 commit comments