learning_ai_notes/mobile/src/app/(tabs)/index.tsx
saravanakumardb1 83f4953870 fix(mobile): add missing accessibilityLabels across all screens
- settings.tsx: labels on sign-out, feedback type chips, submit button
- capture.tsx: labels on workspace selection chips
- index.tsx: labels on workspace filter chips, note card pressables
- search.tsx: labels on search input, result row pressables
- note/[id].tsx: labels on edit, save, cancel buttons
2026-03-31 00:55:05 -07:00

192 lines
6.0 KiB
TypeScript

import { useMemo } from 'react';
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import { router } from 'expo-router';
import type { MobileNote } from '../../api/notes';
import { useNotesStore, type NotesState } from '../../store/notes-store';
import { useWorkspaceStore, type WorkspaceState } from '../../store/workspace-store';
import type { MobileWorkspace } from '../../api/workspaces';
import { colors } from '../../theme';
export default function HomeScreen() {
const notes = useNotesStore((state: NotesState) => state.notes);
const isLoading = useNotesStore((state: NotesState) => state.isLoading);
const workspaces = useWorkspaceStore((state: WorkspaceState) => state.workspaces);
const activeWorkspaceId = useWorkspaceStore((state: WorkspaceState) => state.activeWorkspaceId);
const setActiveWorkspace = useWorkspaceStore((state: WorkspaceState) => state.setActiveWorkspace);
const activeWorkspaceName =
workspaces.find((workspace: MobileWorkspace) => workspace.id === activeWorkspaceId)?.name ?? null;
const recentNotes = useMemo(
() =>
notes
.filter((note: MobileNote) => (activeWorkspaceName ? note.workspaceName === activeWorkspaceName : true))
.map((note: MobileNote) => ({
id: note.id,
title: note.title,
workspace: note.workspaceName,
preview: note.body,
})),
[activeWorkspaceName, notes]
);
const visibleCount = recentNotes.length;
return (
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.eyebrow}>Recent notes</Text>
<Text style={styles.title}>Capture and retrieve fast</Text>
<Text style={styles.subtitle}>Mobile MVP focuses on recent notes, quick capture, search, and lightweight review.</Text>
<View style={styles.summaryCard}>
<Text style={styles.summaryLabel}>Workspace</Text>
<Text style={styles.summaryValue}>{activeWorkspaceName ?? 'All workspaces'}</Text>
<Text style={styles.summaryMeta}>{visibleCount} visible notes ready for quick retrieval</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.workspaceRow}>
<Pressable
accessibilityLabel="Show all workspaces"
onPress={() => setActiveWorkspace(null)}
style={[styles.workspaceChip, activeWorkspaceId === null ? styles.workspaceChipActive : null]}
>
<Text style={[styles.workspaceChipText, activeWorkspaceId === null ? styles.workspaceChipTextActive : null]}>
All
</Text>
</Pressable>
{workspaces.map((workspace: MobileWorkspace) => {
const isActive = workspace.id === activeWorkspaceId;
return (
<Pressable
accessibilityLabel={`Filter by ${workspace.name}`}
key={workspace.id}
onPress={() => setActiveWorkspace(workspace.id)}
style={[styles.workspaceChip, isActive ? styles.workspaceChipActive : null]}
>
<Text style={[styles.workspaceChipText, isActive ? styles.workspaceChipTextActive : null]}>
{workspace.name}
</Text>
</Pressable>
);
})}
</ScrollView>
{isLoading ? <Text style={styles.empty}>Loading notes</Text> : null}
{recentNotes.map((note: (typeof recentNotes)[number]) => (
<Pressable accessibilityLabel={`Open note ${note.title}`} key={note.id} onPress={() => router.push(`/note/${note.id}`)} style={styles.card}>
<View style={styles.badgeRow}>
<Text style={styles.badge}>{note.workspace}</Text>
</View>
<Text style={styles.cardTitle}>{note.title}</Text>
<Text style={styles.cardPreview}>{note.preview}</Text>
</Pressable>
))}
{!isLoading && recentNotes.length === 0 ? (
<Text style={styles.empty}>No recent notes yet for this workspace.</Text>
) : null}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
gap: 14,
backgroundColor: colors.bgCanvas,
minHeight: '100%',
},
eyebrow: {
color: colors.textSecondary,
fontSize: 13,
textTransform: 'uppercase',
letterSpacing: 1,
},
title: {
color: colors.textPrimary,
fontSize: 30,
fontWeight: '700',
},
subtitle: {
color: colors.textSecondary,
fontSize: 16,
lineHeight: 22,
marginBottom: 8,
},
workspaceRow: {
gap: 10,
paddingBottom: 6,
},
workspaceChip: {
borderWidth: 1,
borderColor: colors.borderDefault,
borderRadius: 999,
paddingHorizontal: 12,
paddingVertical: 8,
backgroundColor: colors.surfaceCard,
},
workspaceChipActive: {
backgroundColor: colors.accentPrimary,
borderColor: colors.accentPrimary,
},
workspaceChipText: {
color: colors.textSecondary,
fontWeight: '600',
},
workspaceChipTextActive: {
color: colors.textPrimary,
},
summaryCard: {
backgroundColor: colors.surfaceCard,
borderWidth: 1,
borderColor: colors.borderDefault,
borderRadius: 16,
padding: 16,
gap: 6,
},
summaryLabel: {
color: colors.textSecondary,
fontSize: 13,
textTransform: 'uppercase',
letterSpacing: 1,
},
summaryValue: {
color: colors.textPrimary,
fontSize: 20,
fontWeight: '700',
},
summaryMeta: {
color: colors.textSecondary,
fontSize: 14,
},
card: {
backgroundColor: colors.surfaceCard,
borderWidth: 1,
borderColor: colors.borderDefault,
borderRadius: 16,
padding: 16,
gap: 10,
},
badgeRow: {
flexDirection: 'row',
},
badge: {
color: colors.accentPrimary,
backgroundColor: colors.bgElevated,
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 999,
fontWeight: '600',
overflow: 'hidden',
},
cardTitle: {
color: colors.textPrimary,
fontSize: 18,
fontWeight: '700',
},
cardPreview: {
color: colors.textSecondary,
fontSize: 15,
lineHeight: 21,
},
empty: {
color: colors.textSecondary,
fontSize: 15,
},
});