Set and persist a data directory

This commit is contained in:
2025-06-18 18:35:59 -07:00
parent b72f80cd34
commit 4583093f35
8 changed files with 601 additions and 60 deletions
+4 -35
View File
@@ -5,6 +5,7 @@ import { HelloWave } from '@/components/HelloWave';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { Text } from 'react-native-paper';
export default function HomeScreen() {
return (
@@ -16,41 +17,9 @@ export default function HomeScreen() {
style={styles.headerImage}
/>
}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome bruh.</ThemedText>
<HelloWave />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
<ThemedText>
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
Press{' '}
<ThemedText type="defaultSemiBold">
{Platform.select({
ios: 'cmd + d',
android: 'cmd + m',
web: 'F12',
})}
</ThemedText>{' '}
to open developer tools.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
<ThemedText>
{`Tap the Explore tab to learn more about what's included in this starter app.`}
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
{`When you're ready, run `}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
<Text variant="headlineLarge" style={styles.titleContainer}>
My Journey
</Text>
</ParallaxScrollView>
);
}
+84 -19
View File
@@ -1,27 +1,92 @@
import { useState } from 'react';
import { Button, Text, View } from 'react-native';
import { useEffect, useState } from 'react';
import { Button, View } from 'react-native';
import { pickDirectory } from '@react-native-documents/picker'
import { List, Text } from 'react-native-paper';
import { Directory, Paths } from 'expo-file-system/next';
import { StorageAccessFramework } from 'expo-file-system';
import AsyncStorage from '@react-native-async-storage/async-storage';
const DATA_DIRECTORY_URI_KEY = 'dataDirectoryUri';
const prettyName = (uri: string | null) => {
if (!uri) return null;
const decodedUri = decodeURIComponent(uri);
const match = decodedUri.match(/.*\/primary:(.+)/);
if (match) {
return match[1];
}
// Fallback to your original logic
return uri.split('%3A').pop() || "Unknown";
}
export default function SettingsScreen() {
const [directoryUri, setDirectoryUri] = useState<string | null>(null);
// Load saved directory URI on component mount
useEffect(() => {
const loadDirectoryUri = async () => {
try {
const savedUri = await AsyncStorage.getItem(DATA_DIRECTORY_URI_KEY);
if (savedUri) {
setDirectoryUri(savedUri);
}
} catch (error) {
console.error('Error loading directory URI:', error);
}
};
loadDirectoryUri();
}, []);
// Save directory URI whenever it changes
const saveDirectoryUri = async (uri: string | null) => {
try {
if (uri) {
await AsyncStorage.setItem(DATA_DIRECTORY_URI_KEY, uri);
} else {
await AsyncStorage.removeItem(DATA_DIRECTORY_URI_KEY);
}
setDirectoryUri(uri);
} catch (error) {
console.error('Error saving directory URI:', error);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', gap: 20 }}>
<Text>{directoryUri ?? "No directory set."}</Text>
<Button
title="open directory"
onPress={async () => {
try {
const { uri } = await pickDirectory({
requestLongTermAccess: false,
})
console.log(uri)
setDirectoryUri(uri)
} catch (err) {
// see error handling section
console.error(err)
}
}}
/>
<View style={{ flex: 1, padding: 16, backgroundColor: 'white' }}>
<List.Section title="Data" style={{ backgroundColor: 'white', }} titleStyle={{ fontSize: 24, fontWeight: 'bold' }}>
<List.Item
left={props => <List.Icon {...props} icon="folder" />}
title={prettyName(directoryUri) ?? "No directory selected. Tap to select."}
right={props => <List.Icon {...props} icon="chevron-right" />}
onPress={async () => {
try {
const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permissions.granted) {
await saveDirectoryUri(permissions.directoryUri);
}
} catch (err) {
console.error(err);
}
}}
/>
</List.Section>
<Button title="Debug: List Files" onPress={async () => {
if (!directoryUri) {
console.warn("No directory set.");
return;
}
try {
const dir = await StorageAccessFramework.readDirectoryAsync(directoryUri);
console.log("Directory contents:", dir.map((file, i) => prettyName(file)));
} catch (error) {
console.error("Error reading directory:", error);
}
}
} />
</View>
)
+16 -4
View File
@@ -1,11 +1,11 @@
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { DarkTheme, ThemeProvider } from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import 'react-native-reanimated';
import { MD3LightTheme as DefaultTheme, PaperProvider } from 'react-native-paper';
import { useColorScheme } from '@/hooks/useColorScheme';
import SettingsScreen from './SettingsScreen';
import { IconSymbol } from '@/components/ui/IconSymbol';
@@ -70,6 +70,18 @@ function DrawerNavigator() {
);
}
const theme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.light.tint,
background: Colors.light.background,
text: Colors.light.text,
placeholder: Colors.light.tabIconDefault,
icon: Colors.light.icon,
},
};
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
@@ -81,7 +93,7 @@ export default function RootLayout() {
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<PaperProvider theme={theme}>
<RootStack.Navigator>
<RootStack.Screen
name="Main"
@@ -112,6 +124,6 @@ export default function RootLayout() {
/>
</RootStack.Navigator>
<StatusBar style="auto" />
</ThemeProvider>
</PaperProvider>
);
}