added shared volume to diagnostic, added folder picker to android app, fixed bridge for attachment uploading, fixed hopefully chat history in android app
This commit is contained in:
@@ -165,21 +165,14 @@ const ChatScreen: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// STT-Ergebnis: Spracheingabe-Placeholder mit transkribiertem Text ersetzen
|
||||
// STT-Ergebnis: Transkribierten Text unter den Placeholder schreiben
|
||||
if (message.type === 'stt_result') {
|
||||
const sttText = (message.payload.text as string) || '';
|
||||
if (sttText) {
|
||||
setMessages(prev => prev.map(m =>
|
||||
m.sender === 'user' && m.text.includes('Spracheingabe wird verarbeitet')
|
||||
? { ...m, text: sttText }
|
||||
: m
|
||||
));
|
||||
} else {
|
||||
// Keine Sprache erkannt — Placeholder entfernen
|
||||
setMessages(prev => prev.filter(m =>
|
||||
!(m.sender === 'user' && m.text.includes('Spracheingabe wird verarbeitet'))
|
||||
));
|
||||
}
|
||||
setMessages(prev => prev.map(m =>
|
||||
m.sender === 'user' && m.text.includes('Spracheingabe wird verarbeitet')
|
||||
? { ...m, text: sttText ? `\uD83C\uDFA4 ${sttText}` : '\uD83C\uDFA4 (nicht erkannt)' }
|
||||
: m
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -282,20 +275,34 @@ const ChatScreen: React.FC = () => {
|
||||
}
|
||||
}, [wakeWordActive]);
|
||||
|
||||
// Chat-Verlauf in AsyncStorage speichern (letzte N Nachrichten)
|
||||
// Chat-Verlauf in AsyncStorage speichern (debounced, nur nach initialem Laden)
|
||||
const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
useEffect(() => {
|
||||
if (messages.length === 0 || isInitialLoad.current) return;
|
||||
// Nur file:// URIs speichern, data: URIs rausfiltern (zu gross)
|
||||
const toStore = messages.slice(-MAX_STORED_MESSAGES).map(msg => ({
|
||||
...msg,
|
||||
attachments: msg.attachments?.map(att => ({
|
||||
...att,
|
||||
uri: att.uri?.startsWith('file://') ? att.uri : undefined,
|
||||
})),
|
||||
}));
|
||||
AsyncStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(toStore)).catch(err =>
|
||||
console.error('[Chat] Fehler beim Speichern:', err),
|
||||
);
|
||||
// Debounce: 1s warten damit persistAttachment fertig werden kann
|
||||
if (saveTimer.current) clearTimeout(saveTimer.current);
|
||||
saveTimer.current = setTimeout(() => {
|
||||
const toStore = messages.slice(-MAX_STORED_MESSAGES).map(msg => ({
|
||||
...msg,
|
||||
attachments: msg.attachments?.map(att => ({
|
||||
...att,
|
||||
// Nur file:// URIs speichern, data: URIs rausfiltern (zu gross fuer AsyncStorage)
|
||||
uri: att.uri?.startsWith('file://') ? att.uri : undefined,
|
||||
})),
|
||||
}));
|
||||
const json = JSON.stringify(toStore);
|
||||
// Sicherheitscheck: nicht speichern wenn >4MB (AsyncStorage Limit)
|
||||
if (json.length > 4 * 1024 * 1024) {
|
||||
console.warn('[Chat] Speicher zu gross, kuerze auf 100 Nachrichten');
|
||||
const shortened = JSON.stringify(toStore.slice(-100));
|
||||
AsyncStorage.setItem(CHAT_STORAGE_KEY, shortened).catch(() => {});
|
||||
} else {
|
||||
AsyncStorage.setItem(CHAT_STORAGE_KEY, json).catch(err =>
|
||||
console.error('[Chat] Speichern fehlgeschlagen:', err),
|
||||
);
|
||||
}
|
||||
}, 1000);
|
||||
return () => { if (saveTimer.current) clearTimeout(saveTimer.current); };
|
||||
}, [messages]);
|
||||
|
||||
// Auto-Scroll wird ueber onContentSizeChange der FlatList gesteuert
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import RNFS from 'react-native-fs';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import rvs, { ConnectionState, RVSMessage, ConnectionConfig, ConnectionLogEntry } from '../services/rvs';
|
||||
import ModeSelector from '../components/ModeSelector';
|
||||
import QRScanner from '../components/QRScanner';
|
||||
@@ -115,28 +116,39 @@ const SettingsScreen: React.FC = () => {
|
||||
Alert.alert('Gespeichert', `Neuer Speicherort:\n${clean}\n\nWird ab der naechsten Nachricht verwendet.`);
|
||||
}, []);
|
||||
|
||||
const storagePaths = [
|
||||
{ label: 'App-intern (Standard)', path: DEFAULT_STORAGE_PATH },
|
||||
{ label: 'Externer Speicher', path: '/storage/emulated/0/ARIA/attachments' },
|
||||
{ label: 'SD-Karte', path: '/storage/sdcard1/ARIA/attachments' },
|
||||
{ label: 'Downloads', path: '/storage/emulated/0/Download/ARIA' },
|
||||
];
|
||||
|
||||
const showPathPicker = useCallback(() => {
|
||||
const options = storagePaths.map(p => p.label);
|
||||
options.push('Eigenen Pfad eingeben');
|
||||
options.push('Abbrechen');
|
||||
|
||||
Alert.alert(
|
||||
'Speicherort waehlen',
|
||||
'Wo sollen Anhaenge gespeichert werden?',
|
||||
[
|
||||
...storagePaths.map(p => ({
|
||||
text: p.label,
|
||||
onPress: () => saveStoragePath(p.path),
|
||||
})),
|
||||
{
|
||||
text: 'Eigenen Pfad eingeben',
|
||||
text: 'Ordner auswaehlen...',
|
||||
onPress: async () => {
|
||||
try {
|
||||
const result = await DocumentPicker.pickDirectory();
|
||||
if (result?.uri) {
|
||||
// SAF URI decodieren (content://com.android.externalstorage...)
|
||||
const decoded = decodeURIComponent(result.uri);
|
||||
// Versuche einen lesbaren Pfad zu extrahieren
|
||||
const match = decoded.match(/primary[:%]3A(.+)/);
|
||||
const readablePath = match
|
||||
? `/storage/emulated/0/${match[1].replace(/%2F|%3A/g, '/')}`
|
||||
: decoded;
|
||||
saveStoragePath(readablePath);
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (!DocumentPicker.isCancel(e)) {
|
||||
Alert.alert('Fehler', 'Ordnerauswahl fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'App-intern (Standard)',
|
||||
onPress: () => saveStoragePath(DEFAULT_STORAGE_PATH),
|
||||
},
|
||||
{
|
||||
text: 'Pfad manuell eingeben',
|
||||
onPress: () => { setTempPath(storagePath); setEditingPath(true); },
|
||||
},
|
||||
{ text: 'Abbrechen', style: 'cancel' as const },
|
||||
|
||||
Reference in New Issue
Block a user