Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "simple-notepad",
"slug": "simple-notepad",
"version": "1.1.1",
"version": "1.2.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "simple-notepad",
Expand All @@ -25,7 +25,7 @@
"backgroundColor": "#ffffff"
},
"package": "com.pgarr.simplenotepad",
"versionCode": 13
"versionCode": 14
},
"web": {
"bundler": "metro",
Expand All @@ -44,7 +44,7 @@
"projectId": "9e3820b7-558b-4bd2-a1b2-e49561e741e6"
}
},
"runtimeVersion": "1.1.1",
"runtimeVersion": "1.2.0",
"updates": {
"url": "https://u.expo.dev/9e3820b7-558b-4bd2-a1b2-e49561e741e6"
}
Expand Down
69 changes: 9 additions & 60 deletions app/add-list.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
import { HeaderBackButton } from '@/components/navigation/HeaderBackButton';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { useHardwareBackHandler } from '@/hooks/useHardwareBackHandler';
import { addList, type ListItem } from '@/lib/dataStorage';
import { useSQLiteContext } from 'expo-sqlite';
import { Stack, useRouter } from 'expo-router';
import { useCallback, useState } from 'react';
import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
import { useCallback } from 'react';
import { ListForm } from '@/components/ListForm';

export default function AddListScreen() {
const db = useSQLiteContext();
const router = useRouter();
const [title, setTitle] = useState('');
const [items, setItems] = useState<ListItem[]>([]);
const [saving, setSaving] = useState(false);

const handleAddRow = useCallback(() => {
setItems((current) => [...current, { checked: false, text: '' }]);
}, []);

const handleUpdateRow = useCallback((index: number, text: string) => {
setItems((current) =>
current.map((item, currentIndex) => (currentIndex === index ? { ...item, text } : item))
);
}, []);

const handleSave = useCallback(async () => {
const trimmedTitle = title.trim();
if (!trimmedTitle || saving) return;
const sanitizedItems = items
.map((item) => ({ ...item, text: item.text.trim() }))
.filter((item) => item.text.length > 0);
setSaving(true);
try {
await addList(db, { title: trimmedTitle, items: sanitizedItems });
const handleSave = useCallback(
async (title: string, items: ListItem[]) => {
await addList(db, { title, items });
router.replace('/');
} finally {
setSaving(false);
}
}, [db, items, router, saving, title]);
},
[db, router]
);

useHardwareBackHandler(() => {
router.replace('/');
Expand All @@ -59,35 +36,7 @@ export default function AddListScreen() {
),
}}
/>
<KeyboardAvoidingView
className="flex-1"
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<ScrollView className="flex-1" contentContainerClassName="gap-3 p-4 pb-8">
<Input
className="w-full"
placeholder="Title"
value={title}
onChangeText={setTitle}
editable={!saving}
/>
{items.map((item, index) => (
<Input
key={`row-${index}`}
className="w-full"
placeholder={`Item ${index + 1}`}
value={item.text}
onChangeText={(text) => handleUpdateRow(index, text)}
editable={!saving}
/>
))}
<Button variant="outline" onPress={handleAddRow} disabled={saving}>
<Text>Add</Text>
</Button>
<Button onPress={handleSave} disabled={saving}>
<Text>Save</Text>
</Button>
</ScrollView>
</KeyboardAvoidingView>
<ListForm onSave={handleSave} />
</>
);
}
92 changes: 24 additions & 68 deletions app/edit-list/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { HeaderBackButton } from '@/components/navigation/HeaderBackButton';
import { ScreenLoadingState } from '@/components/state/ScreenLoadingState';
import { ScreenNotFoundState } from '@/components/state/ScreenNotFoundState';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { useHardwareBackHandler } from '@/hooks/useHardwareBackHandler';
import { useParsedNumericRouteParam } from '@/hooks/useParsedNumericRouteParam';
import {
Expand All @@ -16,7 +13,7 @@ import {
import { useSQLiteContext } from 'expo-sqlite';
import { Stack, useRouter } from 'expo-router';
import { useCallback, useEffect, useState } from 'react';
import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
import { ListForm } from '@/components/ListForm';

type ListEditState =
| 'loading'
Expand All @@ -31,8 +28,7 @@ export default function EditListScreen() {
const router = useRouter();
const { rawValue: id, value: listId, isValid: isValidId } = useParsedNumericRouteParam('id');
const [list, setList] = useState<ListEditState>('loading');
const [title, setTitle] = useState('');
const [items, setItems] = useState<ListItem[]>([]);

const [saving, setSaving] = useState(false);

const backTarget = isValidId ? `/list/${id}` : '/';
Expand All @@ -50,8 +46,6 @@ export default function EditListScreen() {
}

const loadedItems = (await getListItemsById(db, listId)) ?? [];
setTitle(content.title);
setItems(loadedItems);
setList({ title: content.title, items: loadedItems });
}, [db, isValidId, listId]);

Expand All @@ -63,33 +57,26 @@ export default function EditListScreen() {
router.replace(backTarget as never);
}, [backTarget, router]);

const handleAddRow = useCallback(() => {
setItems((current) => [...current, { checked: false, text: '' }]);
}, []);

const handleUpdateRow = useCallback((index: number, text: string) => {
setItems((current) =>
current.map((item, currentIndex) => (currentIndex === index ? { ...item, text } : item))
);
}, []);

const handleSave = useCallback(async () => {
if (!isValidId || saving) return;
const trimmedTitle = title.trim();
if (!trimmedTitle) return;

const sanitizedItems = items
.map((item) => ({ ...item, text: item.text.trim() }))
.filter((item) => item.text.length > 0);

setSaving(true);
try {
await updateList(db, listId, { title: trimmedTitle, items: sanitizedItems });
router.replace(`/list/${id}`);
} finally {
setSaving(false);
}
}, [db, id, isValidId, items, listId, router, saving, title]);
const handleSave = useCallback(
async (title: string, items: ListItem[]) => {
if (!isValidId || saving) return;
const trimmedTitle = title.trim();
if (!trimmedTitle) return;

const sanitizedItems = items
.map((item) => ({ ...item, text: item.text.trim() }))
.filter((item) => item.text.length > 0);

setSaving(true);
try {
await updateList(db, listId, { title: trimmedTitle, items: sanitizedItems });
router.replace(`/list/${id}`);
} finally {
setSaving(false);
}
},
[db, id, isValidId, listId, router, saving]
);

useHardwareBackHandler(handleBackToPreviousScreen);

Expand All @@ -108,42 +95,11 @@ export default function EditListScreen() {
title: 'Edit list',
headerBackVisible: false,
headerLeft: () => (
<HeaderBackButton
onPress={handleBackToPreviousScreen}
accessibilityLabel="Back"
/>
<HeaderBackButton onPress={handleBackToPreviousScreen} accessibilityLabel="Back" />
),
}}
/>
<KeyboardAvoidingView
className="flex-1"
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<ScrollView className="flex-1" contentContainerClassName="gap-3 p-4 pb-8">
<Input
className="w-full"
placeholder="Title"
value={title}
onChangeText={setTitle}
editable={!saving}
/>
{items.map((item, index) => (
<Input
key={`row-${index}`}
className="w-full"
placeholder={`Item ${index + 1}`}
value={item.text}
onChangeText={(text) => handleUpdateRow(index, text)}
editable={!saving}
/>
))}
<Button variant="outline" onPress={handleAddRow} disabled={saving}>
<Text>Add</Text>
</Button>
<Button onPress={handleSave} disabled={saving}>
<Text>Save</Text>
</Button>
</ScrollView>
</KeyboardAvoidingView>
<ListForm initialTitle={list.title} initialItems={list.items} onSave={handleSave} />
</>
);
}
18 changes: 5 additions & 13 deletions app/edit-note/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import { ScreenLoadingState } from '@/components/state/ScreenLoadingState';
import { ScreenNotFoundState } from '@/components/state/ScreenNotFoundState';
import { useHardwareBackHandler } from '@/hooks/useHardwareBackHandler';
import { useParsedNumericRouteParam } from '@/hooks/useParsedNumericRouteParam';
import { LIST_TYPE, getNoteById, updateNote, type Note } from '@/lib/dataStorage';
import { NOTE_TYPE, getNoteById, updateNote, type Note } from '@/lib/dataStorage';
import { useSQLiteContext } from 'expo-sqlite';
import { Stack, useRouter } from 'expo-router';
import { useCallback, useEffect, useState } from 'react';

export default function EditNoteScreen() {
const db = useSQLiteContext();
const router = useRouter();
const { rawValue: id, value: noteId, isValid: isValidId } =
useParsedNumericRouteParam('id');
const { rawValue: id, value: noteId, isValid: isValidId } = useParsedNumericRouteParam('id');
const [note, setNote] = useState<Note | null | 'loading'>('loading');

const backTarget = isValidId ? `/note/${id}` : '/';
Expand All @@ -24,7 +23,7 @@ export default function EditNoteScreen() {
return;
}
const found = await getNoteById(db, noteId);
if (found?.type === LIST_TYPE) {
if (found?.type !== NOTE_TYPE) {
setNote(null);
return;
}
Expand Down Expand Up @@ -65,18 +64,11 @@ export default function EditNoteScreen() {
title: 'Edit note',
headerBackVisible: false,
headerLeft: () => (
<HeaderBackButton
onPress={handleBackToPreviousScreen}
accessibilityLabel="Back"
/>
<HeaderBackButton onPress={handleBackToPreviousScreen} accessibilityLabel="Back" />
),
}}
/>
<NoteForm
initialTitle={note.title}
initialContent={note.note}
onSave={handleSave}
/>
<NoteForm initialTitle={note.title} initialContent={note.note} onSave={handleSave} />
</>
);
}
33 changes: 27 additions & 6 deletions app/list/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import {
getNoteById,
type ListItem,
updateListItems,
deletePosition,
} from '@/lib/dataStorage';
import { useSQLiteContext } from 'expo-sqlite';
import { Stack, useRouter } from 'expo-router';
import { CheckSquare2, PencilIcon, Square } from 'lucide-react-native';
import { CheckSquare2, PencilIcon, Square, Trash2Icon } from 'lucide-react-native';
import { useCallback, useEffect, useState } from 'react';
import { Pressable, ScrollView, View } from 'react-native';
import { Alert, Pressable, ScrollView, View } from 'react-native';

type ListViewState =
| 'loading'
Expand Down Expand Up @@ -70,6 +71,21 @@ export default function ListViewScreen() {
[db, listView]
);

const handleDeletePress = useCallback(() => {
if (listView === null || listView === 'loading') return;
Alert.alert('Delete list', 'Are you sure you want to delete this list?', [
{ text: 'No', style: 'cancel' },
{
text: 'Yes',
style: 'destructive',
onPress: async () => {
await deletePosition(db, listView.id);
router.replace('/');
},
},
]);
}, [db, listView, router]);

if (listView === 'loading') {
return <ScreenLoadingState />;
}
Expand All @@ -96,10 +112,16 @@ export default function ListViewScreen() {
variant="ghost"
size="icon"
onPress={() => router.push(`/edit-list/${listView.id}` as never)}
accessibilityLabel="Edit list"
>
accessibilityLabel="Edit list">
<Icon as={PencilIcon} className="size-5" />
</Button>
<Button
variant="ghost"
size="icon"
onPress={handleDeletePress}
accessibilityLabel="Delete list">
<Icon as={Trash2Icon} className="size-5" />
</Button>
</View>
),
}}
Expand All @@ -111,8 +133,7 @@ export default function ListViewScreen() {
className="flex-row items-center gap-3 rounded-md border border-border px-3 py-2"
onPress={() => void handleToggleItem(index)}
accessibilityRole="checkbox"
accessibilityState={{ checked: item.checked }}
>
accessibilityState={{ checked: item.checked }}>
<Icon as={item.checked ? CheckSquare2 : Square} className="size-5 text-foreground" />
<Text className={item.checked ? 'text-muted-foreground line-through' : ''}>
{item.text}
Expand Down
Loading
Loading