From 21b669f96cfeee95c7484fc689a3541cbb9cea9f Mon Sep 17 00:00:00 2001 From: Jonathan Atta Date: Wed, 4 Mar 2026 11:08:40 +0100 Subject: [PATCH] feat: implement permission handling in ModelsScreen and update PermissionMessage component --- .../LocalModelsScreen/PermissionMessage.tsx | 57 +++++++++++++++---- app/src/screens/LocalModelsScreen/index.tsx | 4 -- app/src/screens/ModelsScreen.tsx | 34 ++++++++++- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/app/src/screens/LocalModelsScreen/PermissionMessage.tsx b/app/src/screens/LocalModelsScreen/PermissionMessage.tsx index a7f4b94..d28f927 100644 --- a/app/src/screens/LocalModelsScreen/PermissionMessage.tsx +++ b/app/src/screens/LocalModelsScreen/PermissionMessage.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { View, Text, TouchableOpacity } from 'react-native'; -import { createStyles } from './styles'; +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { useTheme } from '../../theme/ThemeProvider'; interface PermissionMessageProps { @@ -11,24 +10,62 @@ export default function PermissionMessage({ onRequestPermission, }: PermissionMessageProps) { const { colors } = useTheme(); - const styles = createStyles(colors); return ( - - - 📁 Permission requise + + 📁 + + Permission requise - - L'application a besoin d'accéder aux fichiers pour lire vos modèles GGUF + + L'application a besoin d'accéder aux fichiers pour lire vos modèles GGUF. - + Autoriser l'accès aux fichiers ); } + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingHorizontal: 40, + gap: 16, + }, + icon: { + fontSize: 56, + marginBottom: 8, + }, + title: { + fontSize: 22, + fontWeight: '700', + textAlign: 'center', + }, + subtitle: { + fontSize: 15, + textAlign: 'center', + opacity: 0.65, + lineHeight: 22, + marginBottom: 8, + }, + button: { + paddingHorizontal: 28, + paddingVertical: 14, + borderRadius: 12, + marginTop: 8, + }, + buttonText: { + fontSize: 16, + fontWeight: '600', + textAlign: 'center', + }, +}); diff --git a/app/src/screens/LocalModelsScreen/index.tsx b/app/src/screens/LocalModelsScreen/index.tsx index d2bda6b..242f8c9 100644 --- a/app/src/screens/LocalModelsScreen/index.tsx +++ b/app/src/screens/LocalModelsScreen/index.tsx @@ -28,7 +28,6 @@ import type { ModelConfig } from '../../store/types'; import ModelItem from './ModelItem'; import DirectoryPicker from './DirectoryPicker'; import DirectoryEditor from './DirectoryEditor'; -import PermissionMessage from './PermissionMessage'; import ModelsList from './ModelsList'; import { createStyles } from './styles'; import { useTheme } from '../../theme/ThemeProvider'; @@ -381,9 +380,6 @@ export default function LocalModelsScreen() { )} )} - {!hasPermission && ( - - )} {/* Models List */} diff --git a/app/src/screens/ModelsScreen.tsx b/app/src/screens/ModelsScreen.tsx index fb34825..0b683fe 100644 --- a/app/src/screens/ModelsScreen.tsx +++ b/app/src/screens/ModelsScreen.tsx @@ -1,8 +1,15 @@ -import React from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; +import { AppState } from 'react-native'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; +import { useFocusEffect } from '@react-navigation/native'; import { useTheme } from '../theme/ThemeProvider'; import LocalModelsScreen from './LocalModelsScreen'; import HuggingFaceModelsScreen from './HuggingFaceModelsScreen'; +import PermissionMessage from './LocalModelsScreen/PermissionMessage'; +import { + checkStoragePermission, + requestStoragePermission, +} from '../utils/permissions'; export type ModelsTabParamList = { LocalModels: undefined; @@ -14,6 +21,31 @@ const Tab = createMaterialTopTabNavigator(); export default function ModelsScreen() { const { colors, scheme } = useTheme(); const isDark = scheme === 'dark'; + const [hasPermission, setHasPermission] = useState(false); + + const checkPerm = useCallback(async () => { + const granted = await checkStoragePermission(); + setHasPermission(granted); + }, []); + + useEffect(() => { + checkPerm(); + const sub = AppState.addEventListener('change', (state) => { + if (state === 'active') checkPerm(); + }); + return () => sub.remove(); + }, [checkPerm]); + + useFocusEffect(useCallback(() => { checkPerm(); }, [checkPerm])); + + const handleRequestPermission = async () => { + await requestStoragePermission(); + setTimeout(checkPerm, 1000); + }; + + if (!hasPermission) { + return ; + } return (