fixed, long chats not loading to end, saved attachments in local folder on android., if file missing redownload over shared folde via rvs server, andord app added settingss for local storage path, updated readme
This commit is contained in:
@@ -16,10 +16,15 @@ import {
|
||||
Alert,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import RNFS from 'react-native-fs';
|
||||
import rvs, { ConnectionState, RVSMessage, ConnectionConfig, ConnectionLogEntry } from '../services/rvs';
|
||||
import ModeSelector from '../components/ModeSelector';
|
||||
import QRScanner from '../components/QRScanner';
|
||||
|
||||
const STORAGE_PATH_KEY = 'aria_attachment_storage_path';
|
||||
const DEFAULT_STORAGE_PATH = `${RNFS.DocumentDirectoryPath}/chat_attachments`;
|
||||
|
||||
// --- Typen ---
|
||||
|
||||
interface LogEntry {
|
||||
@@ -62,6 +67,10 @@ const SettingsScreen: React.FC = () => {
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [events, setEvents] = useState<EventEntry[]>([]);
|
||||
const [connLog, setConnLog] = useState<ConnectionLogEntry[]>(rvs.getConnectionLog());
|
||||
const [storagePath, setStoragePath] = useState(DEFAULT_STORAGE_PATH);
|
||||
const [storageSize, setStorageSize] = useState('...');
|
||||
const [editingPath, setEditingPath] = useState(false);
|
||||
const [tempPath, setTempPath] = useState('');
|
||||
|
||||
let logIdCounter = 0;
|
||||
|
||||
@@ -73,8 +82,64 @@ const SettingsScreen: React.FC = () => {
|
||||
setManualPort(String(config.port));
|
||||
setManualToken(config.token);
|
||||
}
|
||||
// Speicherpfad laden
|
||||
AsyncStorage.getItem(STORAGE_PATH_KEY).then(saved => {
|
||||
if (saved) setStoragePath(saved);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Speichergroesse berechnen
|
||||
useEffect(() => {
|
||||
const calcSize = async () => {
|
||||
try {
|
||||
const exists = await RNFS.exists(storagePath);
|
||||
if (!exists) { setStorageSize('0 KB'); return; }
|
||||
const items = await RNFS.readDir(storagePath);
|
||||
const totalBytes = items.reduce((sum, f) => sum + (f.size || 0), 0);
|
||||
if (totalBytes > 1024 * 1024) {
|
||||
setStorageSize(`${(totalBytes / 1024 / 1024).toFixed(1)} MB (${items.length} Dateien)`);
|
||||
} else {
|
||||
setStorageSize(`${Math.round(totalBytes / 1024)} KB (${items.length} Dateien)`);
|
||||
}
|
||||
} catch { setStorageSize('nicht verfuegbar'); }
|
||||
};
|
||||
calcSize();
|
||||
}, [storagePath]);
|
||||
|
||||
const saveStoragePath = useCallback(async (newPath: string) => {
|
||||
const clean = newPath.trim();
|
||||
if (!clean) return;
|
||||
await AsyncStorage.setItem(STORAGE_PATH_KEY, clean);
|
||||
setStoragePath(clean);
|
||||
setEditingPath(false);
|
||||
Alert.alert('Gespeichert', `Neuer Speicherort:\n${clean}\n\nWird ab der naechsten Nachricht verwendet.`);
|
||||
}, []);
|
||||
|
||||
const clearStorageCache = useCallback(async () => {
|
||||
Alert.alert(
|
||||
'Cache loeschen',
|
||||
`Alle lokalen Anhaenge in\n${storagePath}\nloeschen?\n\nDateien koennen ueber RVS erneut heruntergeladen werden.`,
|
||||
[
|
||||
{ text: 'Abbrechen', style: 'cancel' },
|
||||
{
|
||||
text: 'Loeschen',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
try {
|
||||
const exists = await RNFS.exists(storagePath);
|
||||
if (exists) await RNFS.unlink(storagePath);
|
||||
await RNFS.mkdir(storagePath);
|
||||
setStorageSize('0 KB (0 Dateien)');
|
||||
Alert.alert('Erledigt', 'Cache geleert. Anhaenge werden bei Bedarf neu geladen.');
|
||||
} catch (e: any) {
|
||||
Alert.alert('Fehler', e.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}, [storagePath]);
|
||||
|
||||
// RVS-Nachrichten und Verbindungslog abonnieren
|
||||
useEffect(() => {
|
||||
const unsubState = rvs.onStateChange(setConnectionState);
|
||||
@@ -332,6 +397,62 @@ const SettingsScreen: React.FC = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* === Speicher === */}
|
||||
<Text style={styles.sectionTitle}>Anhang-Speicher</Text>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.toggleLabel}>Lokaler Speicherort</Text>
|
||||
<Text style={styles.toggleHint}>
|
||||
Hier werden Bilder und Dateien aus dem Chat gespeichert.
|
||||
Bei geloeschtem Cache werden Anhaenge automatisch ueber RVS neu geladen.
|
||||
</Text>
|
||||
|
||||
{editingPath ? (
|
||||
<View style={{marginTop: 10}}>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={tempPath}
|
||||
onChangeText={setTempPath}
|
||||
placeholder="z.B. /storage/emulated/0/ARIA/attachments"
|
||||
placeholderTextColor="#555570"
|
||||
autoCapitalize="none"
|
||||
/>
|
||||
<View style={{flexDirection: 'row', gap: 8}}>
|
||||
<TouchableOpacity
|
||||
style={[styles.connectButton, {flex: 1}]}
|
||||
onPress={() => saveStoragePath(tempPath)}
|
||||
>
|
||||
<Text style={styles.connectButtonText}>Speichern</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {flex: 1, marginTop: 0}]}
|
||||
onPress={() => setEditingPath(false)}
|
||||
>
|
||||
<Text style={styles.clearButtonText}>Abbrechen</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View style={{marginTop: 10}}>
|
||||
<Text style={styles.storagePathText} numberOfLines={2}>{storagePath}</Text>
|
||||
<Text style={styles.storageSizeText}>{storageSize}</Text>
|
||||
<View style={{flexDirection: 'row', gap: 8, marginTop: 8}}>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {flex: 1, marginTop: 0}]}
|
||||
onPress={() => { setTempPath(storagePath); setEditingPath(true); }}
|
||||
>
|
||||
<Text style={styles.clearButtonText}>Pfad aendern</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {flex: 1, marginTop: 0, backgroundColor: 'rgba(255,59,48,0.15)'}]}
|
||||
onPress={clearStorageCache}
|
||||
>
|
||||
<Text style={[styles.clearButtonText, {color: '#FF3B30'}]}>Cache leeren</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* === Logs === */}
|
||||
<Text style={styles.sectionTitle}>Protokoll</Text>
|
||||
<View style={styles.card}>
|
||||
@@ -559,6 +680,18 @@ const styles = StyleSheet.create({
|
||||
marginTop: 2,
|
||||
},
|
||||
|
||||
// Speicher
|
||||
storagePathText: {
|
||||
color: '#0096FF',
|
||||
fontSize: 12,
|
||||
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
||||
},
|
||||
storageSizeText: {
|
||||
color: '#8888AA',
|
||||
fontSize: 12,
|
||||
marginTop: 4,
|
||||
},
|
||||
|
||||
// Logs
|
||||
tabRow: {
|
||||
flexDirection: 'row',
|
||||
|
||||
Reference in New Issue
Block a user