From a353b62b372924cc4ad2794b1b89e215abee6c56 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 11 Jul 2026 16:45:15 +0200 Subject: [PATCH] feat(app): Projekte verstecken auch in der App (Auge + Toggle) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spiegelt das Diagnostic-Feature auf App-Seite: - Project-Interface + updateProject um hidden erweitert; setProjectHidden(). - ProjectsBrowser: versteckte Projekte standardmaessig ausgeblendet (mama sieht sie nicht). Auge pro Zeile (πŸ™ˆ verstecken / πŸ‘ sichtbar), Toggle "πŸ‘ Versteckte anzeigen (N)" blendet sie temporaer gedimmt + Badge ein zum Ansehen/Auswaehlen/Wieder-Sichtbarmachen. Eigener Touch am Auge, damit der Tap nicht das Projekt wechselt. Co-Authored-By: Claude Opus 4.8 --- android/src/components/ProjectsBrowser.tsx | 67 ++++++++++++++++++++-- android/src/services/brainApi.ts | 13 ++++- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/android/src/components/ProjectsBrowser.tsx b/android/src/components/ProjectsBrowser.tsx index 007baaf..bc8c23b 100644 --- a/android/src/components/ProjectsBrowser.tsx +++ b/android/src/components/ProjectsBrowser.tsx @@ -75,6 +75,9 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc const [editing, setEditing] = useState(null); const [editName, setEditName] = useState(''); const [editDesc, setEditDesc] = useState(''); + // Versteckte Projekte standardmaessig ausblenden; Toggle blendet sie + // temporaer (gedimmt) ein β€” zum Ansehen/Auswaehlen oder Wieder-Sichtbarmachen. + const [showHidden, setShowHidden] = useState(false); // Refs damit useCallback NICHT bei jeder Re-Render des Parents neu erzeugt // wird (parent uebergibt oft inline-arrow-Callbacks, neue Identity jedes @@ -158,6 +161,12 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc ]); }, [load]); + const toggleHidden = useCallback((p: Project) => { + brainApi.setProjectHidden(p.id, !p.hidden) + .then(() => load()) + .catch(e => Alert.alert('Fehler', String(e?.message || e))); + }, [load]); + const archiveProject = useCallback((p: Project) => { Alert.alert(`"${p.name}" archivieren?`, 'Verschwindet aus der Standardliste. Über "archivierte zeigen" erreichbar.', @@ -176,11 +185,12 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc const renderItem = ({ item }: { item: Project }) => { const isActive = item.id === activeId; const dot = _statusDot(item.id); + const hidden = !!item.hidden; return ( switchTo(item.id)} onLongPress={() => openEdit(item)} - style={[s.row, isActive && s.rowActive]} + style={[s.row, isActive && s.rowActive, hidden && s.rowHidden]} > @@ -188,6 +198,7 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc )} {item.name} + {hidden && versteckt} {item.status === 'ended' && beendet} {isActive && βœ“ FOCUS} @@ -199,10 +210,22 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc {dot.label ? ` Β· ${dot.label}` : ''} + {/* Auge: verstecken (πŸ™ˆ) / wieder sichtbar (πŸ‘). Eigener Touch, damit + der Tap NICHT das Projekt wechselt. */} + toggleHidden(item)} + hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} + style={s.eyeBtn} + > + {hidden ? 'πŸ‘' : 'πŸ™ˆ'} + ); }; + const hiddenCount = projects.filter(p => p.hidden).length; + const visibleProjects = showHidden ? projects : projects.filter(p => !p.hidden); + const body = ( {/* Header */} @@ -243,6 +266,17 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc ); })()} + {/* Versteckte-Toggle β€” nur wenn es welche gibt (oder gerade eingeblendet) */} + {(hiddenCount > 0 || showHidden) && ( + setShowHidden(v => !v)} style={s.hiddenToggle}> + + {showHidden + ? `πŸ™ˆ Versteckte ausblenden${hiddenCount ? ` (${hiddenCount})` : ''}` + : `πŸ‘ Versteckte anzeigen${hiddenCount ? ` (${hiddenCount})` : ''}`} + + + )} + {loading ? ( @@ -251,14 +285,21 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc ⚠ {err} ) : ( p.id} renderItem={renderItem} ListEmptyComponent={ - - Noch keine Projekte. Tipp + Neu oder sag zu ARIA:{'\n'} - β€žLass uns ein Projekt 'XY' anlegen". - + projects.length > 0 ? ( + + Alle {hiddenCount} Projekte sind versteckt.{'\n'} + Tipp β€žπŸ‘ Versteckte anzeigen". + + ) : ( + + Noch keine Projekte. Tipp + Neu oder sag zu ARIA:{'\n'} + β€žLass uns ein Projekt 'XY' anlegen". + + ) } /> )} @@ -365,6 +406,8 @@ const s = StyleSheet.create({ headerBtnText: { color: '#0096FF', fontSize: 18, fontWeight: '600' }, headerTitle: { flex: 1, textAlign: 'center', color: '#E0E0F0', fontSize: 18, fontWeight: '700' }, row: { + flexDirection: 'row', + alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, @@ -375,6 +418,18 @@ const s = StyleSheet.create({ borderLeftWidth: 3, borderLeftColor: '#34C759', }, + rowHidden: { opacity: 0.55 }, + eyeBtn: { paddingHorizontal: 8, paddingVertical: 6, marginLeft: 6 }, + eyeIcon: { fontSize: 18 }, + hiddenBadge: { color: '#B392F0', fontSize: 10, fontWeight: '700', + backgroundColor: 'rgba(179,146,240,0.15)', paddingHorizontal: 6, + paddingVertical: 2, borderRadius: 4 }, + hiddenToggle: { + paddingHorizontal: 16, paddingVertical: 10, + borderBottomWidth: 1, borderColor: '#1E1E2E', + backgroundColor: '#0D0D18', + }, + hiddenToggleText: { color: '#B392F0', fontSize: 12, fontWeight: '600' }, rowName: { color: '#E0E0F0', fontSize: 16, fontWeight: '600' }, rowDesc: { color: '#8888AA', fontSize: 13, marginTop: 4 }, rowMeta: { color: '#555570', fontSize: 11, marginTop: 4 }, diff --git a/android/src/services/brainApi.ts b/android/src/services/brainApi.ts index 50a669a..a7445df 100644 --- a/android/src/services/brainApi.ts +++ b/android/src/services/brainApi.ts @@ -157,6 +157,7 @@ export interface Project { name: string; description: string; status: 'active' | 'ended' | 'archived'; + hidden?: boolean; // aus Listen ausgeblendet (bleibt nutzbar) created_at: number; updated_at: number; last_activity_at: number; @@ -593,14 +594,22 @@ export const brainApi = { }); }, - /** Projekt-Metadaten patchen (name / description). */ - updateProject(projectId: string, patch: Partial>): Promise { + /** Projekt-Metadaten patchen (name / description / hidden). */ + updateProject(projectId: string, patch: Partial>): Promise { return _send(`/projects/${encodeURIComponent(projectId)}`, { method: 'PATCH', body: patch, }); }, + /** Projekt verstecken / wieder sichtbar machen (bleibt voll nutzbar). */ + setProjectHidden(projectId: string, hidden: boolean): Promise { + return _send(`/projects/${encodeURIComponent(projectId)}`, { + method: 'PATCH', + body: { hidden }, + }); + }, + /** Queue-Status: pro Kontext (project_id oder __main__ fuer Hauptchat) * ob gerade ein Request in Verarbeitung ist + wieviele in der Queue warten. * Wird fuer Status-Dots im Drawer periodisch gepollt. */