remove picture read access
This commit is contained in:
+1
-12
@@ -9,8 +9,7 @@
|
|||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true,
|
"supportsTablet": true,
|
||||||
"infoPlist": {
|
"infoPlist": {
|
||||||
"NSCameraUsageDescription": "VaultDrop a besoin d'accéder à votre caméra pour scanner des documents.",
|
"NSCameraUsageDescription": "VaultDrop a besoin d'accéder à votre caméra pour scanner des documents."
|
||||||
"NSPhotoLibraryUsageDescription": "VaultDrop a besoin d'accéder à votre galerie pour sélectionner des photos."
|
|
||||||
},
|
},
|
||||||
"bundleIdentifier": "com.anonymous.webui"
|
"bundleIdentifier": "com.anonymous.webui"
|
||||||
},
|
},
|
||||||
@@ -19,9 +18,6 @@
|
|||||||
"CAMERA",
|
"CAMERA",
|
||||||
"READ_EXTERNAL_STORAGE",
|
"READ_EXTERNAL_STORAGE",
|
||||||
"WRITE_EXTERNAL_STORAGE",
|
"WRITE_EXTERNAL_STORAGE",
|
||||||
"READ_MEDIA_IMAGES",
|
|
||||||
"READ_MEDIA_VIDEO",
|
|
||||||
"READ_MEDIA_AUDIO",
|
|
||||||
"RECEIVE_BOOT_COMPLETED"
|
"RECEIVE_BOOT_COMPLETED"
|
||||||
],
|
],
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
@@ -38,13 +34,6 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-secure-store",
|
"expo-secure-store",
|
||||||
[
|
|
||||||
"expo-media-library",
|
|
||||||
{
|
|
||||||
"photosPermission": "VaultDrop a besoin d'accéder à vos photos pour les afficher et les organiser.",
|
|
||||||
"videosPermission": "VaultDrop a besoin d'accéder à vos vidéos pour les afficher et les organiser."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"expo-image",
|
"expo-image",
|
||||||
"expo-sqlite",
|
"expo-sqlite",
|
||||||
"expo-background-task",
|
"expo-background-task",
|
||||||
|
|||||||
+1
-37
@@ -79,7 +79,7 @@ export function HomeScreen() {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const { data, isLoading, error, isFetching, refetch } = useFiles(null, page, PAGE_SIZE);
|
const { data, isLoading, error, isFetching, refetch } = useFiles(null, page, PAGE_SIZE);
|
||||||
const { hasPermission, requestPermission, pickAndScanRecursive, folders, refreshFolders, discovered } = useLocalFiles();
|
const { pickAndScanRecursive, folders, refreshFolders, discovered } = useLocalFiles();
|
||||||
const freeLocalSpace = useFreeLocalSpace();
|
const freeLocalSpace = useFreeLocalSpace();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@@ -395,19 +395,7 @@ export function HomeScreen() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
{!hasPermission ? (
|
|
||||||
<View style={styles.permissionBanner}>
|
|
||||||
<MaterialIcons name="photo-library" size={28} color="#1976D2" />
|
|
||||||
<Text style={styles.permissionText}>
|
|
||||||
Autorisez l'accès à vos photos pour les afficher ici
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity style={styles.permissionBtn} onPress={requestPermission}>
|
|
||||||
<Text style={styles.permissionBtnText}>Autoriser</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<Text style={styles.infoText}>Chargement...</Text>
|
<Text style={styles.infoText}>Chargement...</Text>
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -426,17 +414,6 @@ export function HomeScreen() {
|
|||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
keyboardVerticalOffset={Platform.OS === 'ios' ? 96 : 0}
|
keyboardVerticalOffset={Platform.OS === 'ios' ? 96 : 0}
|
||||||
>
|
>
|
||||||
{!hasPermission && (
|
|
||||||
<View style={styles.permissionBanner}>
|
|
||||||
<MaterialIcons name="photo-library" size={28} color="#1976D2" />
|
|
||||||
<Text style={styles.permissionText}>
|
|
||||||
Autorisez l'accès à vos photos pour les afficher ici
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity style={styles.permissionBtn} onPress={requestPermission}>
|
|
||||||
<Text style={styles.permissionBtnText}>Autoriser</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{files.length === 0 && !searchQuery && (
|
{files.length === 0 && !searchQuery && (
|
||||||
<View style={styles.folderScanBanner}>
|
<View style={styles.folderScanBanner}>
|
||||||
{folders.length > 0 ? (
|
{folders.length > 0 ? (
|
||||||
@@ -657,19 +634,6 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: '#f5f5f5',
|
backgroundColor: '#f5f5f5',
|
||||||
},
|
},
|
||||||
permissionBanner: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#E3F2FD',
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 14,
|
|
||||||
gap: 10,
|
|
||||||
},
|
|
||||||
permissionText: {
|
|
||||||
flex: 1,
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#333',
|
|
||||||
},
|
|
||||||
permissionBtn: {
|
permissionBtn: {
|
||||||
backgroundColor: '#1976D2',
|
backgroundColor: '#1976D2',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet, TouchableOpacity, Modal, Alert } from 'react-native';
|
import { View, Text, StyleSheet, TouchableOpacity, Modal, Alert } from 'react-native';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
|
||||||
import * as DocumentPicker from 'expo-document-picker';
|
import * as DocumentPicker from 'expo-document-picker';
|
||||||
import { UploadFile } from '../services/uploadQueue';
|
import { UploadFile } from '../services/uploadQueue';
|
||||||
import { useUploadQueue } from '../hooks/useUploadQueue';
|
import { useUploadQueue } from '../hooks/useUploadQueue';
|
||||||
@@ -14,35 +13,6 @@ interface UploadModalProps {
|
|||||||
export function UploadModal({ visible, onClose }: UploadModalProps) {
|
export function UploadModal({ visible, onClose }: UploadModalProps) {
|
||||||
const { enqueue } = useUploadQueue();
|
const { enqueue } = useUploadQueue();
|
||||||
|
|
||||||
const pickImages = async () => {
|
|
||||||
try {
|
|
||||||
const { status: perm } = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
||||||
if (perm !== 'granted') {
|
|
||||||
Alert.alert('Permission requise', "L'accès à la galerie est nécessaire pour sélectionner des photos.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await ImagePicker.launchImageLibraryAsync({
|
|
||||||
mediaTypes: ['images'],
|
|
||||||
quality: 1,
|
|
||||||
allowsMultipleSelection: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.canceled || result.assets.length === 0) return;
|
|
||||||
|
|
||||||
const files = result.assets.map((asset) => ({
|
|
||||||
uri: asset.uri,
|
|
||||||
type: asset.mimeType || 'image/jpeg',
|
|
||||||
name: asset.fileName || 'photo.jpg',
|
|
||||||
}));
|
|
||||||
|
|
||||||
enqueue(files);
|
|
||||||
onClose();
|
|
||||||
} catch {
|
|
||||||
Alert.alert('Erreur', "Impossible d'accéder à la galerie");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const pickDocuments = async () => {
|
const pickDocuments = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await DocumentPicker.getDocumentAsync({
|
const result = await DocumentPicker.getDocumentAsync({
|
||||||
@@ -87,14 +57,6 @@ export function UploadModal({ visible, onClose }: UploadModalProps) {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.photoButton}
|
|
||||||
onPress={pickImages}
|
|
||||||
>
|
|
||||||
<MaterialIcons name="photo-library" size={20} color="#fff" />
|
|
||||||
<Text style={styles.buttonText}>Sélectionner des photos</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.docButton}
|
style={styles.docButton}
|
||||||
onPress={pickDocuments}
|
onPress={pickDocuments}
|
||||||
@@ -145,16 +107,6 @@ const styles = StyleSheet.create({
|
|||||||
closeBtn: {
|
closeBtn: {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
photoButton: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: '#1976D2',
|
|
||||||
padding: 16,
|
|
||||||
borderRadius: 8,
|
|
||||||
marginBottom: 12,
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
docButton: {
|
docButton: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
+12
-122
@@ -1,5 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import * as MediaLibrary from 'expo-media-library/legacy';
|
|
||||||
import * as FileSystem from 'expo-file-system/legacy';
|
import * as FileSystem from 'expo-file-system/legacy';
|
||||||
import { safDirectory, type StoredFolder } from '../services/safDirectory';
|
import { safDirectory, type StoredFolder } from '../services/safDirectory';
|
||||||
import { downloadRegistry } from '../services/downloadRegistry';
|
import { downloadRegistry } from '../services/downloadRegistry';
|
||||||
@@ -90,30 +89,6 @@ async function scanSafFolder(folder: StoredFolder): Promise<DeviceFile[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function scanMediaAlbum(folder: StoredFolder): Promise<DeviceFile[]> {
|
|
||||||
try {
|
|
||||||
if (!folder.albumId) return [];
|
|
||||||
const result = await MediaLibrary.getAssetsAsync({
|
|
||||||
album: folder.albumId,
|
|
||||||
first: 500,
|
|
||||||
sortBy: 'creationTime',
|
|
||||||
});
|
|
||||||
return result.assets.map((asset) => ({
|
|
||||||
id: `media_${folder.id}_${asset.id}`,
|
|
||||||
uri: asset.uri,
|
|
||||||
name: asset.filename,
|
|
||||||
mimeType: asset.mediaType === 'video' ? 'video/mp4' : 'image/jpeg',
|
|
||||||
size: 0,
|
|
||||||
createdAt: asset.creationTime
|
|
||||||
? new Date(asset.creationTime).toISOString()
|
|
||||||
: new Date().toISOString(),
|
|
||||||
folderId: folder.id,
|
|
||||||
}));
|
|
||||||
} catch (err) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function scanSubdirectories(
|
export async function scanSubdirectories(
|
||||||
baseUri: string,
|
baseUri: string,
|
||||||
depth: number = 0,
|
depth: number = 0,
|
||||||
@@ -143,80 +118,15 @@ export async function scanSubdirectories(
|
|||||||
export function useDeviceFiles() {
|
export function useDeviceFiles() {
|
||||||
const [files, setFiles] = useState<DeviceFile[]>([]);
|
const [files, setFiles] = useState<DeviceFile[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [hasPermission, setHasPermission] = useState(false);
|
|
||||||
const [folders, setFolders] = useState<StoredFolder[]>(() => safDirectory.getAll());
|
const [folders, setFolders] = useState<StoredFolder[]>(() => safDirectory.getAll());
|
||||||
const [discovered, setDiscovered] = useState(() => safDirectory.getDiscovered());
|
const [discovered, setDiscovered] = useState(() => safDirectory.getDiscovered());
|
||||||
const { newFiles, clearNewFiles } = useFileWatcher();
|
const { newFiles, clearNewFiles } = useFileWatcher();
|
||||||
|
|
||||||
const requestPermission = useCallback(async () => {
|
|
||||||
const req = await MediaLibrary.requestPermissionsAsync();
|
|
||||||
if (req.granted) {
|
|
||||||
setHasPermission(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const check = await MediaLibrary.getPermissionsAsync();
|
|
||||||
const granted = check.granted;
|
|
||||||
setHasPermission(granted);
|
|
||||||
return granted;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const loadAssets = useCallback(async () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
const result = await MediaLibrary.getAssetsAsync({
|
|
||||||
first: 500,
|
|
||||||
mediaType: ['photo', 'video'],
|
|
||||||
sortBy: 'creationTime',
|
|
||||||
});
|
|
||||||
|
|
||||||
const deviceFiles: DeviceFile[] = result.assets.map((asset) => ({
|
|
||||||
id: asset.id,
|
|
||||||
uri: asset.uri,
|
|
||||||
name: asset.filename,
|
|
||||||
mimeType: asset.mediaType === 'video' ? 'video/mp4' : 'image/jpeg',
|
|
||||||
size: 0,
|
|
||||||
createdAt: asset.creationTime
|
|
||||||
? new Date(asset.creationTime).toISOString()
|
|
||||||
: new Date().toISOString(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
setFiles(deviceFiles);
|
|
||||||
} catch (err) {
|
|
||||||
setFiles([]);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const discoverMediaAlbums = useCallback(async (): Promise<number> => {
|
|
||||||
try {
|
|
||||||
const albums = await MediaLibrary.getAlbumsAsync();
|
|
||||||
let added = 0;
|
|
||||||
for (const album of albums) {
|
|
||||||
if (album.title && album.assetCount > 0) {
|
|
||||||
safDirectory.addMediaFolder(album.id, album.title, '');
|
|
||||||
added++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (added > 0) {
|
|
||||||
setFolders(safDirectory.getAll());
|
|
||||||
}
|
|
||||||
return added;
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const scanVisibleFolders = useCallback(async () => {
|
const scanVisibleFolders = useCallback(async () => {
|
||||||
const visibleFolders = safDirectory.getVisibleFolders();
|
const visibleFolders = safDirectory.getVisibleFolders().filter((f) => f.source !== 'media-library');
|
||||||
if (visibleFolders.length === 0) return;
|
if (visibleFolders.length === 0) return;
|
||||||
|
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(visibleFolders.map((folder) => scanSafFolder(folder)));
|
||||||
visibleFolders.map((folder) => {
|
|
||||||
if (folder.source === 'media-library') return scanMediaAlbum(folder);
|
|
||||||
return scanSafFolder(folder);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const safFiles = results.flat();
|
const safFiles = results.flat();
|
||||||
|
|
||||||
setFiles((prev) => {
|
setFiles((prev) => {
|
||||||
@@ -284,33 +194,19 @@ export function useDeviceFiles() {
|
|||||||
}
|
}
|
||||||
}, [discovered, scanVisibleFolders]);
|
}, [discovered, scanVisibleFolders]);
|
||||||
|
|
||||||
const runInitialDiscovery = useCallback(async () => {
|
|
||||||
if (safDirectory.getDiscovered()) return;
|
|
||||||
|
|
||||||
const granted = hasPermission || await requestPermission();
|
|
||||||
if (granted) {
|
|
||||||
const mediaCount = await discoverMediaAlbums();
|
|
||||||
if (mediaCount > 0) {
|
|
||||||
await scanVisibleFolders();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
safDirectory.setDiscovered();
|
|
||||||
setDiscovered(true);
|
|
||||||
}, [hasPermission, requestPermission, discoverMediaAlbums, scanVisibleFolders]);
|
|
||||||
|
|
||||||
const refreshFolders = useCallback(() => {
|
const refreshFolders = useCallback(() => {
|
||||||
setFolders(safDirectory.getAll());
|
setFolders(safDirectory.getAll());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const rescan = useCallback(async () => {
|
const rescan = useCallback(async () => {
|
||||||
const granted = await requestPermission();
|
setIsLoading(true);
|
||||||
if (granted) {
|
try {
|
||||||
await loadAssets();
|
|
||||||
}
|
|
||||||
await scanVisibleFolders();
|
await scanVisibleFolders();
|
||||||
loadRegistryFiles();
|
loadRegistryFiles();
|
||||||
}, [requestPermission, loadAssets, scanVisibleFolders, loadRegistryFiles]);
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [scanVisibleFolders, loadRegistryFiles]);
|
||||||
|
|
||||||
// Handle new files detected by native module
|
// Handle new files detected by native module
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -333,22 +229,16 @@ export function useDeviceFiles() {
|
|||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestPermission().then((granted) => {
|
|
||||||
if (granted) {
|
|
||||||
loadAssets();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
scanVisibleFolders();
|
scanVisibleFolders();
|
||||||
loadRegistryFiles();
|
loadRegistryFiles();
|
||||||
runInitialDiscovery();
|
}, [scanVisibleFolders, loadRegistryFiles]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
files, isLoading, hasPermission,
|
files, isLoading,
|
||||||
requestPermission, rescan,
|
rescan,
|
||||||
pickAndScanRecursive,
|
pickAndScanRecursive,
|
||||||
folders, refreshFolders,
|
folders, refreshFolders,
|
||||||
discovered, discoverMediaAlbums,
|
discovered,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { generateLocalThumbnail } from '../services/thumbnail';
|
|||||||
import { UnifiedFileItem } from '../types';
|
import { UnifiedFileItem } from '../types';
|
||||||
|
|
||||||
export function useLocalFiles() {
|
export function useLocalFiles() {
|
||||||
const { files: deviceFiles, isLoading: deviceLoading, hasPermission, requestPermission, rescan, pickAndScanRecursive, folders, refreshFolders, discovered, discoverMediaAlbums } = useDeviceFiles();
|
const { files: deviceFiles, isLoading: deviceLoading, rescan, pickAndScanRecursive, folders, refreshFolders, discovered } = useDeviceFiles();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const lastDeviceCount = useRef(0);
|
const lastDeviceCount = useRef(0);
|
||||||
const thumbnailQueue = useRef<Set<string>>(new Set());
|
const thumbnailQueue = useRef<Set<string>>(new Set());
|
||||||
@@ -101,13 +101,10 @@ export function useLocalFiles() {
|
|||||||
return {
|
return {
|
||||||
localFiles,
|
localFiles,
|
||||||
isLoading: deviceLoading,
|
isLoading: deviceLoading,
|
||||||
hasPermission,
|
|
||||||
requestPermission,
|
|
||||||
rescan,
|
rescan,
|
||||||
pickAndScanRecursive,
|
pickAndScanRecursive,
|
||||||
folders,
|
folders,
|
||||||
refreshFolders,
|
refreshFolders,
|
||||||
discovered,
|
discovered,
|
||||||
discoverMediaAlbums,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-24
@@ -23,8 +23,6 @@
|
|||||||
"expo-file-system": "~57.0.1",
|
"expo-file-system": "~57.0.1",
|
||||||
"expo-image": "~57.0.1",
|
"expo-image": "~57.0.1",
|
||||||
"expo-image-manipulator": "~57.0.14",
|
"expo-image-manipulator": "~57.0.14",
|
||||||
"expo-image-picker": "~57.0.6",
|
|
||||||
"expo-media-library": "~57.0.3",
|
|
||||||
"expo-print": "~57.0.1",
|
"expo-print": "~57.0.1",
|
||||||
"expo-secure-store": "~57.0.1",
|
"expo-secure-store": "~57.0.1",
|
||||||
"expo-sqlite": "~57.0.1",
|
"expo-sqlite": "~57.0.1",
|
||||||
@@ -4398,28 +4396,6 @@
|
|||||||
"expo": "*"
|
"expo": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-image-picker": {
|
|
||||||
"version": "57.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-57.0.6.tgz",
|
|
||||||
"integrity": "sha512-6Of7SzyFVC+WFuFxhD4+nRTQ9joqldPhbiVWRsumg4ybttKmY8GxrnXAUSIDfa9DSk9PhgIAy2C1c5y1+CnU3g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"expo-image-loader": "~57.0.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"expo": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/expo-media-library": {
|
|
||||||
"version": "57.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/expo-media-library/-/expo-media-library-57.0.3.tgz",
|
|
||||||
"integrity": "sha512-VI2NEDaMWIfz9Gxgibzu8BxxxVvgwHRAeZDjV9gci9YH+JkXn5vdvNAnRdmojaPxpp3tbWMfMDB1ympFcrak9A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"expo": "*",
|
|
||||||
"react-native": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/expo-modules-autolinking": {
|
"node_modules/expo-modules-autolinking": {
|
||||||
"version": "57.0.9",
|
"version": "57.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.9.tgz",
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
"expo-file-system": "~57.0.1",
|
"expo-file-system": "~57.0.1",
|
||||||
"expo-image": "~57.0.1",
|
"expo-image": "~57.0.1",
|
||||||
"expo-image-manipulator": "~57.0.14",
|
"expo-image-manipulator": "~57.0.14",
|
||||||
"expo-image-picker": "~57.0.6",
|
|
||||||
"expo-media-library": "~57.0.3",
|
|
||||||
"expo-print": "~57.0.1",
|
"expo-print": "~57.0.1",
|
||||||
"expo-secure-store": "~57.0.1",
|
"expo-secure-store": "~57.0.1",
|
||||||
"expo-sqlite": "~57.0.1",
|
"expo-sqlite": "~57.0.1",
|
||||||
|
|||||||
@@ -22,32 +22,37 @@ export function initDB() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!existingVersion) {
|
if (!existingVersion) {
|
||||||
|
createSchema(_sqliteDb);
|
||||||
_sqliteDb.execSync(`CREATE TABLE schema_version (version INTEGER PRIMARY KEY);`);
|
_sqliteDb.execSync(`CREATE TABLE schema_version (version INTEGER PRIMARY KEY);`);
|
||||||
}
|
_sqliteDb.execSync(`INSERT INTO schema_version (version) VALUES (${SCHEMA_VERSION});`);
|
||||||
|
} else {
|
||||||
const versionRow = _sqliteDb.getFirstSync<{ version: number }>(
|
const versionRow = _sqliteDb.getFirstSync<{ version: number }>(
|
||||||
`SELECT version FROM schema_version ORDER BY version DESC LIMIT 1`
|
`SELECT version FROM schema_version ORDER BY version DESC LIMIT 1`
|
||||||
);
|
);
|
||||||
const currentVersion = versionRow?.version ?? 0;
|
const currentVersion = versionRow?.version ?? 0;
|
||||||
|
|
||||||
if (currentVersion < SCHEMA_VERSION) {
|
if (currentVersion < SCHEMA_VERSION) {
|
||||||
dropAllTables(_sqliteDb);
|
migrate(_sqliteDb, currentVersion, SCHEMA_VERSION);
|
||||||
createSchema(_sqliteDb);
|
_sqliteDb.execSync(`UPDATE schema_version SET version = ${SCHEMA_VERSION};`);
|
||||||
_sqliteDb.execSync(`INSERT INTO schema_version (version) VALUES (${SCHEMA_VERSION});`);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_db = drizzle(_sqliteDb);
|
_db = drizzle(_sqliteDb);
|
||||||
return _db;
|
return _db;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dropAllTables(db: SQLite.SQLiteDatabase) {
|
const MIGRATIONS: Array<(db: SQLite.SQLiteDatabase) => void> = [
|
||||||
db.execSync(`DROP TABLE IF EXISTS file_tags;`);
|
// v5: add thumbnail_local column
|
||||||
db.execSync(`DROP TABLE IF EXISTS files;`);
|
(db) => {
|
||||||
db.execSync(`DROP TABLE IF EXISTS deleted_files;`);
|
db.execSync(`ALTER TABLE files ADD COLUMN thumbnail_local TEXT;`);
|
||||||
db.execSync(`DROP TABLE IF EXISTS device_info;`);
|
},
|
||||||
db.execSync(`DROP TABLE IF EXISTS resources_fts;`);
|
];
|
||||||
db.execSync(`DROP TABLE IF EXISTS pending_actions;`);
|
|
||||||
db.execSync(`DROP TABLE IF EXISTS schema_version;`);
|
function migrate(db: SQLite.SQLiteDatabase, fromVersion: number, toVersion: number) {
|
||||||
|
for (let v = fromVersion; v < toVersion; v++) {
|
||||||
|
const migration = MIGRATIONS[v - 1];
|
||||||
|
if (migration) migration(db);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSchema(db: SQLite.SQLiteDatabase) {
|
function createSchema(db: SQLite.SQLiteDatabase) {
|
||||||
|
|||||||
Reference in New Issue
Block a user