|
|
|
@ -23,7 +23,7 @@ export function QuestionPhoto({ |
|
|
|
}: QuestionPhotoProps) { |
|
|
|
const inputId = useId(); |
|
|
|
const [localPreviewUrl, setLocalPreviewUrl] = useState<string | null>(null); |
|
|
|
const [isFlutterPicking, setIsFlutterPicking] = useState(false); |
|
|
|
const [isUploading, setIsUploading] = useState(false); |
|
|
|
|
|
|
|
const acceptedFiles = |
|
|
|
question.extras?.options && question.extras.options.length > 0 |
|
|
|
@ -41,13 +41,15 @@ export function QuestionPhoto({ |
|
|
|
setAnswerValue(question, response.path); |
|
|
|
setLocalPreviewUrl(response.path); |
|
|
|
} |
|
|
|
setIsUploading(false); |
|
|
|
}, |
|
|
|
onError: (error) => { |
|
|
|
console.error("Photo upload error:", error); |
|
|
|
setIsUploading(false); |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
const isPending = uploadTmpMediaMutation.isPending || isFlutterPicking; |
|
|
|
const isPending = uploadTmpMediaMutation.isPending || isUploading; |
|
|
|
|
|
|
|
// Listen for upload_file responses from Flutter WebView if active
|
|
|
|
useEffect(() => { |
|
|
|
@ -60,9 +62,10 @@ export function QuestionPhoto({ |
|
|
|
|
|
|
|
switch (event.status) { |
|
|
|
case "picking": |
|
|
|
setIsFlutterPicking(true); |
|
|
|
break; |
|
|
|
case "picked": |
|
|
|
case "progress": |
|
|
|
setIsUploading(true); |
|
|
|
if (event.data?.files?.[0]?.base64) { |
|
|
|
const b64 = event.data.files[0].base64; |
|
|
|
setLocalPreviewUrl(b64); |
|
|
|
@ -70,7 +73,7 @@ export function QuestionPhoto({ |
|
|
|
} |
|
|
|
break; |
|
|
|
case "completed": { |
|
|
|
setIsFlutterPicking(false); |
|
|
|
setIsUploading(false); |
|
|
|
isInitiatorRef.current = false; |
|
|
|
const file = event.data?.files?.[0]; |
|
|
|
const remoteUrl = |
|
|
|
@ -98,7 +101,7 @@ export function QuestionPhoto({ |
|
|
|
} |
|
|
|
case "cancelled": |
|
|
|
case "failed": |
|
|
|
setIsFlutterPicking(false); |
|
|
|
setIsUploading(false); |
|
|
|
isInitiatorRef.current = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
@ -115,7 +118,6 @@ export function QuestionPhoto({ |
|
|
|
); |
|
|
|
|
|
|
|
isInitiatorRef.current = true; |
|
|
|
setIsFlutterPicking(true); |
|
|
|
|
|
|
|
uploadFile({ |
|
|
|
requestId: String(question.id), |
|
|
|
@ -142,6 +144,7 @@ export function QuestionPhoto({ |
|
|
|
setAnswerValue(question, objectUrl); |
|
|
|
|
|
|
|
// Trigger background upload
|
|
|
|
setIsUploading(true); |
|
|
|
uploadTmpMediaMutation.mutate(file); |
|
|
|
}; |
|
|
|
|
|
|
|
@ -240,10 +243,10 @@ export function QuestionPhoto({ |
|
|
|
</span> |
|
|
|
)} |
|
|
|
|
|
|
|
{/* Loading spinner during upload */} |
|
|
|
{/* Loading spinner during upload matching Flutter ProfileAvatarWidget */} |
|
|
|
{isPending && ( |
|
|
|
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/40 backdrop-blur-[1px]"> |
|
|
|
<LoadingSkeleton className="size-12 rounded-full" /> |
|
|
|
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/40 backdrop-blur-[1px] z-10 transition-all duration-200"> |
|
|
|
<div className="h-7 w-7 animate-spin rounded-full border-[2.5px] border-white border-t-transparent shadow-sm" /> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
</div> |
|
|
|
|