// Store
import { create } from 'zustand';
interface Props {
isDownloadProcessing: boolean;
downloadProgress: number;
setIsDownloadProcessing: (value: boolean) => void;
setDownloadProgress: (value: number) => void;
}
export const useGlobalDownloadProgressStore = create
()((set, get) => ({
isDownloadProcessing: false,
downloadProgress: 0,
setIsDownloadProcessing: (value: boolean) => {
console.log('[Zustand] setIsDownloadProcessing:', value);
set({ isDownloadProcessing: value });
},
setDownloadProgress: (value: number) => {
const isProcessing = get().isDownloadProcessing;
console.log('[Zustand] setDownloadProgress:', value, 'isProcessing:', isProcessing);
set({ downloadProgress: isProcessing ? value : 0 });
},
}));
< /code>
// Componentes React / React Native
import { useEffect, useRef } from 'react';
import { Platform } from 'react-native';
// Librerías de terceros
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import * as Sharing from 'expo-sharing';
import { AxiosError } from 'axios';
// Comunicación con endpoints
import { downloadDocumentUrlByHashName } from '@/core/actions/document/download.document.url.by.hash.name';
// Configuraciones e Interfaces (Declarada como tipos)
import { IDocumentDTO } from '@/core/infrastructure/interfaces/document.dto';
// Hooks y utilidades
import { useGlobalDownloadProgressStore } from '@store/documents/download.global.progress.store';
import { useAuthStore } from '@store/useAuthStore';
import { useGlobalAlertStore } from '@store/globalAlertStore';
// Assets
// Atoms
// Molecules
// Organisms
// Layouts
// Containers
// Screens
const URL = process.env.EXPO_PUBLIC_DOCUMENT_TRANSFER_URL
/**
* Custom hook para la descarga de documentos.
*/
export const useDownloadDocument = () => {
const setDownloadProgress = useGlobalDownloadProgressStore((state) => state.setDownloadProgress);
const setIsDownloadProcessing = useGlobalDownloadProgressStore((state) => state.setIsDownloadProcessing);
const showGlobalAlert = useGlobalAlertStore((state) => state.showGlobalAlert);
const userData = useAuthStore((state) => state.userData);
const prevUserIdRef = useRef(userData?.id ?? null);
useEffect(() => {
const currentUserId = userData?.id ?? null;
if (prevUserIdRef.current !== null && prevUserIdRef.current !== currentUserId) {
const controller = useAuthStore.getState().downloadAbortController;
if (controller) {
controller.abort();
useAuthStore.getState().setDownloadAbortController(null);
console.log('Descarga cancelada por cambio de usuario');
}
}
prevUserIdRef.current = currentUserId;
}, [userData]);
const handleFileDownload = async (document: IDocumentDTO, userId: number, setProgress?: (progress: number) => void) => {
setIsDownloadProcessing(true);
const DownloadController = new AbortController();
const { signal } = DownloadController;
useAuthStore.getState().setDownloadAbortController(DownloadController);
const title = document.title + '.' + document.type;
try {
await downloadDocumentUrlByHashName(
document.hash,
userId,
(progress: number) => {
setDownloadProgress(progress);
if (setProgress) {
console.log('Progress:', progress);
setProgress(progress);
}
},
signal
);
if (signal.aborted) {
setIsDownloadProcessing(false);
return;
}
const fileUrl = `${URL}/download/${document.hash}?userId=${userData?.id ?? 0}`;
const fileUri = `${FileSystem.documentDirectory}${title}`;
const { uri } = await FileSystem.downloadAsync(fileUrl, fileUri);
handleFileValidation(fileUrl, uri, title, document);
} catch (error: unknown) {
if (error instanceof AxiosError) {
handleAxiosError(error);
} else {
console.error('Error no identificado en la descarga:', error);
showGlobalAlert('Error al descargar el documento', 'error');
}
} finally {
setIsDownloadProcessing(false);
useAuthStore.getState().setDownloadAbortController(null);
}
};
const handleFileValidation = async (fileUrl: string, uri: string, title: string, document: IDocumentDTO) => {
if (fileUrl.length > 0 && userData) {
if (Platform.OS === 'ios') {
await handleIOSFileSharing(uri);
} else if (Platform.OS === 'android') {
await handleAndroidFileSaving(uri, title, document);
}
}
};
const handleIOSFileSharing = async (uri: string) => {
if (await Sharing.isAvailableAsync()) {
await Sharing.shareAsync(uri);
} else {
console.error('Error', 'Sharing is not available on this device.');
}
};
const handleAndroidFileSaving = async (uri: string, title: string, document: IDocumentDTO) => {
const { status } = await MediaLibrary.requestPermissionsAsync();
if (status !== 'granted') {
return;
}
const permissions = await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permissions.granted) {
const base64 = await FileSystem.readAsStringAsync(uri, { encoding: FileSystem.EncodingType.Base64 });
await FileSystem.StorageAccessFramework.createFileAsync(permissions.directoryUri, title, document.type)
.then(async (uri) => {
await FileSystem.writeAsStringAsync(uri, base64, { encoding: FileSystem.EncodingType.Base64 });
})
.catch((e) => console.error(e));
}
};
< /code>
Component
import { lightTheme } from '@/config/theme/Colors';
import { useGlobalDownloadProgressStore } from '@/presentation/store/documents/download.global.progress.store';
import React, { useEffect, useRef } from 'react';
import { View, Animated } from 'react-native';
const ProgressBar = () => {
const animatedProgress = useRef(new Animated.Value(0)).current;
const downloadProgress = useGlobalDownloadProgressStore((state) => state.downloadProgress);
const downloadProgressfinal = downloadProgress / 100;
const bgProgressBar = lightTheme.secondary1;
useEffect(() => {
Animated.timing(animatedProgress, {
toValue: downloadProgressfinal,
duration: 300,
useNativeDriver: false,
}).start();
}, [downloadProgressfinal]);
return (
);
};
export default ProgressBar;
< /code>
And dont get error just the store initalize in 0 and not update o change
android and ios simulator works also android apk but when i deploy in testlight the bar is not working, help plss
Подробнее здесь: https://stackoverflow.com/questions/795 ... ne-in-expo
Мобильная версия