|
|
|
@@ -37,9 +37,11 @@ interface Props {
|
|
|
|
|
title?: string;
|
|
|
|
|
/** Style-Erweiterung fuer den Container. */
|
|
|
|
|
flatStyle?: boolean;
|
|
|
|
|
/** Wenn gesetzt: kein eigenes DetailModal mounten — Parent kuemmert sich. */
|
|
|
|
|
onOpenMemory?: (id: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const MemoryBrowser: React.FC<Props> = ({ restrictToIds, title, flatStyle }) => {
|
|
|
|
|
export const MemoryBrowser: React.FC<Props> = ({ restrictToIds, title, flatStyle, onOpenMemory }) => {
|
|
|
|
|
const [items, setItems] = useState<Memory[]>([]);
|
|
|
|
|
const [filtered, setFiltered] = useState<Memory[]>([]);
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
@@ -82,38 +84,35 @@ export const MemoryBrowser: React.FC<Props> = ({ restrictToIds, title, flatStyle
|
|
|
|
|
setFiltered(out);
|
|
|
|
|
}, [items, q, typeFilter, pinnedFilter, restrictToIds]);
|
|
|
|
|
|
|
|
|
|
const [showNewMemoryDialog, setShowNewMemoryDialog] = useState(false);
|
|
|
|
|
const [newMemoryTitle, setNewMemoryTitle] = useState('');
|
|
|
|
|
|
|
|
|
|
const onAddNew = () => {
|
|
|
|
|
Alert.prompt(
|
|
|
|
|
'Neue Memory',
|
|
|
|
|
'Titel:',
|
|
|
|
|
[
|
|
|
|
|
{ text: 'Abbrechen', style: 'cancel' },
|
|
|
|
|
{
|
|
|
|
|
text: 'Anlegen',
|
|
|
|
|
onPress: async (title?: string) => {
|
|
|
|
|
const t = (title || '').trim();
|
|
|
|
|
if (!t) return;
|
|
|
|
|
try {
|
|
|
|
|
const m = await brainApi.saveMemory({
|
|
|
|
|
type: 'fact', title: t,
|
|
|
|
|
content: '(noch leer — bitte editieren)',
|
|
|
|
|
});
|
|
|
|
|
load();
|
|
|
|
|
setOpenId(m.id);
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
Alert.alert('Fehler', String(e?.message || e));
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
'plain-text',
|
|
|
|
|
);
|
|
|
|
|
setNewMemoryTitle('');
|
|
|
|
|
setShowNewMemoryDialog(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const confirmAddNew = async () => {
|
|
|
|
|
const t = newMemoryTitle.trim();
|
|
|
|
|
if (!t) { setShowNewMemoryDialog(false); return; }
|
|
|
|
|
setShowNewMemoryDialog(false);
|
|
|
|
|
try {
|
|
|
|
|
const m = await brainApi.saveMemory({
|
|
|
|
|
type: 'fact', title: t,
|
|
|
|
|
content: '(noch leer — bitte editieren)',
|
|
|
|
|
});
|
|
|
|
|
load();
|
|
|
|
|
if (onOpenMemory) onOpenMemory(m.id);
|
|
|
|
|
else setOpenId(m.id);
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
Alert.alert('Fehler', String(e?.message || e));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const renderItem = ({ item }: { item: Memory }) => {
|
|
|
|
|
const attCount = (item.attachments || []).length;
|
|
|
|
|
return (
|
|
|
|
|
<TouchableOpacity style={s.row} onPress={() => setOpenId(item.id)}>
|
|
|
|
|
<TouchableOpacity style={s.row} onPress={() => onOpenMemory ? onOpenMemory(item.id) : setOpenId(item.id)}>
|
|
|
|
|
<View style={{flex:1}}>
|
|
|
|
|
<Text style={s.rowTitle} numberOfLines={1}>
|
|
|
|
|
{item.pinned ? '📌 ' : ''}{item.title || '(ohne Titel)'}
|
|
|
|
@@ -202,12 +201,42 @@ export const MemoryBrowser: React.FC<Props> = ({ restrictToIds, title, flatStyle
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
<MemoryDetailModal
|
|
|
|
|
memoryId={openId}
|
|
|
|
|
visible={!!openId}
|
|
|
|
|
onClose={() => { setOpenId(null); load(); }}
|
|
|
|
|
onDeleted={() => { setOpenId(null); load(); }}
|
|
|
|
|
/>
|
|
|
|
|
{/* Eigenes DetailModal nur wenn der Parent kein Callback uebergibt
|
|
|
|
|
(vermeidet Modal-in-Modal-Stacking auf Android). */}
|
|
|
|
|
{!onOpenMemory && (
|
|
|
|
|
<MemoryDetailModal
|
|
|
|
|
memoryId={openId}
|
|
|
|
|
visible={!!openId}
|
|
|
|
|
onClose={() => { setOpenId(null); load(); }}
|
|
|
|
|
onDeleted={() => { setOpenId(null); load(); }}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* "Neue Memory"-Dialog (Alert.prompt ist iOS-only, daher eigenes Modal) */}
|
|
|
|
|
<Modal visible={showNewMemoryDialog} transparent animationType="fade" onRequestClose={() => setShowNewMemoryDialog(false)}>
|
|
|
|
|
<View style={s.menuBack}>
|
|
|
|
|
<View style={[s.menuBox, {padding:16, minWidth:280}]}>
|
|
|
|
|
<Text style={{color:'#FFD60A', fontWeight:'bold', fontSize:14, marginBottom:10}}>Neue Memory anlegen</Text>
|
|
|
|
|
<Text style={{color:'#8888AA', fontSize:11, marginBottom:6}}>Titel:</Text>
|
|
|
|
|
<TextInput
|
|
|
|
|
value={newMemoryTitle}
|
|
|
|
|
onChangeText={setNewMemoryTitle}
|
|
|
|
|
autoFocus
|
|
|
|
|
placeholder="z.B. Stefans Auto"
|
|
|
|
|
placeholderTextColor="#555570"
|
|
|
|
|
style={{backgroundColor:'#1E1E2E', color:'#E0E0F0', padding:8, borderRadius:4, fontSize:13, marginBottom:12}}
|
|
|
|
|
/>
|
|
|
|
|
<View style={{flexDirection:'row', gap:8, justifyContent:'flex-end'}}>
|
|
|
|
|
<TouchableOpacity onPress={() => setShowNewMemoryDialog(false)} style={{padding:8}}>
|
|
|
|
|
<Text style={{color:'#8888AA'}}>Abbrechen</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
<TouchableOpacity onPress={confirmAddNew} style={{backgroundColor:'#0096FF', paddingHorizontal:14, paddingVertical:8, borderRadius:4}}>
|
|
|
|
|
<Text style={{color:'#fff', fontWeight:'600'}}>Anlegen</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
</Modal>
|
|
|
|
|
</View>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|