Browse Source

fix(photo-upload): implement smooth circular spinner loading matching Flutter ProfileAvatarWidget

master
mortezaei 2 days ago
parent
commit
120a753efd
  1. 21
      src/components/Componentes/question-photo.tsx

21
src/components/Componentes/question-photo.tsx

@ -23,7 +23,7 @@ export function QuestionPhoto({
}: QuestionPhotoProps) { }: QuestionPhotoProps) {
const inputId = useId(); const inputId = useId();
const [localPreviewUrl, setLocalPreviewUrl] = useState<string | null>(null); const [localPreviewUrl, setLocalPreviewUrl] = useState<string | null>(null);
const [isFlutterPicking, setIsFlutterPicking] = useState(false);
const [isUploading, setIsUploading] = useState(false);
const acceptedFiles = const acceptedFiles =
question.extras?.options && question.extras.options.length > 0 question.extras?.options && question.extras.options.length > 0
@ -41,13 +41,15 @@ export function QuestionPhoto({
setAnswerValue(question, response.path); setAnswerValue(question, response.path);
setLocalPreviewUrl(response.path); setLocalPreviewUrl(response.path);
} }
setIsUploading(false);
}, },
onError: (error) => { onError: (error) => {
console.error("Photo upload error:", 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 // Listen for upload_file responses from Flutter WebView if active
useEffect(() => { useEffect(() => {
@ -60,9 +62,10 @@ export function QuestionPhoto({
switch (event.status) { switch (event.status) {
case "picking": case "picking":
setIsFlutterPicking(true);
break; break;
case "picked": case "picked":
case "progress":
setIsUploading(true);
if (event.data?.files?.[0]?.base64) { if (event.data?.files?.[0]?.base64) {
const b64 = event.data.files[0].base64; const b64 = event.data.files[0].base64;
setLocalPreviewUrl(b64); setLocalPreviewUrl(b64);
@ -70,7 +73,7 @@ export function QuestionPhoto({
} }
break; break;
case "completed": { case "completed": {
setIsFlutterPicking(false);
setIsUploading(false);
isInitiatorRef.current = false; isInitiatorRef.current = false;
const file = event.data?.files?.[0]; const file = event.data?.files?.[0];
const remoteUrl = const remoteUrl =
@ -98,7 +101,7 @@ export function QuestionPhoto({
} }
case "cancelled": case "cancelled":
case "failed": case "failed":
setIsFlutterPicking(false);
setIsUploading(false);
isInitiatorRef.current = false; isInitiatorRef.current = false;
break; break;
} }
@ -115,7 +118,6 @@ export function QuestionPhoto({
); );
isInitiatorRef.current = true; isInitiatorRef.current = true;
setIsFlutterPicking(true);
uploadFile({ uploadFile({
requestId: String(question.id), requestId: String(question.id),
@ -142,6 +144,7 @@ export function QuestionPhoto({
setAnswerValue(question, objectUrl); setAnswerValue(question, objectUrl);
// Trigger background upload // Trigger background upload
setIsUploading(true);
uploadTmpMediaMutation.mutate(file); uploadTmpMediaMutation.mutate(file);
}; };
@ -240,10 +243,10 @@ export function QuestionPhoto({
</span> </span>
)} )}
{/* Loading spinner during upload */}
{/* Loading spinner during upload matching Flutter ProfileAvatarWidget */}
{isPending && ( {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>
)} )}
</div> </div>

Loading…
Cancel
Save