fix: Persistent storage for todo + font fix
Signed-off-by: SindreKjelsrud <sindre@kjelsrud.dev>
This commit is contained in:
parent
2764f555de
commit
43aa945fc9
2 changed files with 143 additions and 84 deletions
|
@ -221,6 +221,7 @@ export const styles = StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
color: "#333",
|
color: "#333",
|
||||||
|
fontFamily: "Minecraft",
|
||||||
},
|
},
|
||||||
removeTodoButton: {
|
removeTodoButton: {
|
||||||
backgroundColor: "#FF6347",
|
backgroundColor: "#FF6347",
|
||||||
|
|
226
app/todo.tsx
226
app/todo.tsx
|
@ -1,14 +1,16 @@
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
ImageBackground,
|
Alert,
|
||||||
KeyboardAvoidingView,
|
ImageBackground,
|
||||||
Platform,
|
KeyboardAvoidingView,
|
||||||
ScrollView,
|
Platform,
|
||||||
Text,
|
ScrollView,
|
||||||
TextInput,
|
Text,
|
||||||
TouchableOpacity,
|
TextInput,
|
||||||
View
|
TouchableOpacity,
|
||||||
|
View
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
import { styles } from "./styles";
|
import { styles } from "./styles";
|
||||||
|
@ -16,86 +18,142 @@ import { styles } from "./styles";
|
||||||
const converterBgImage = require("../assets/images/todo.png");
|
const converterBgImage = require("../assets/images/todo.png");
|
||||||
|
|
||||||
export default function CoordinateConverter() {
|
export default function CoordinateConverter() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [todos, setTodos] = useState<string[]>([]);
|
const [todos, setTodos] = useState<string[]>([]);
|
||||||
const [newTodo, setNewTodo] = useState<string>("");
|
const [newTodo, setNewTodo] = useState<string>("");
|
||||||
|
|
||||||
const addTodo = () => {
|
// Define a unique key for AsyncStorage
|
||||||
if (newTodo.trim().length > 0) {
|
const STORAGE_KEY = "@my_todo_list";
|
||||||
setTodos([...todos, newTodo.trim()]);
|
|
||||||
setNewTodo("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTodo = (index: number) => {
|
// --- Persistence Logic ---
|
||||||
const updatedTodos = todos.filter((_, i) => i !== index);
|
useEffect(() => {
|
||||||
setTodos(updatedTodos);
|
loadTodos();
|
||||||
};
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
saveTodos();
|
||||||
|
}, [todos]);
|
||||||
|
|
||||||
return (
|
// Async function to load todos from AsyncStorage
|
||||||
<View style={styles.converterScreenRoot}>
|
const loadTodos = async () => {
|
||||||
<ImageBackground
|
try {
|
||||||
source={converterBgImage}
|
const jsonValue = await AsyncStorage.getItem(STORAGE_KEY);
|
||||||
style={styles.converterBackgroundImage}
|
if (jsonValue != null) {
|
||||||
resizeMode="cover"
|
const parsedTodos = JSON.parse(jsonValue);
|
||||||
>
|
if (Array.isArray(parsedTodos)) {
|
||||||
<View style={styles.converterBackgroundOverlay} />
|
setTodos(parsedTodos);
|
||||||
</ImageBackground>
|
} else {
|
||||||
|
console.warn("Loaded data is not an array, initializing empty todo list.");
|
||||||
|
setTodos([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to load todos:", e);
|
||||||
|
Alert.alert("Error", "Failed to load your todo list.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
<SafeAreaView style={styles.converterContentWrapper}>
|
// Async function to save todos to AsyncStorage
|
||||||
<KeyboardAvoidingView
|
const saveTodos = async () => {
|
||||||
style={styles.converterContainer}
|
try {
|
||||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
const jsonValue = JSON.stringify(todos);
|
||||||
>
|
await AsyncStorage.setItem(STORAGE_KEY, jsonValue);
|
||||||
<ScrollView contentContainerStyle={styles.converterScrollContent}>
|
} catch (e) {
|
||||||
{/* Custom Back Button */}
|
console.error("Failed to save todos:", e);
|
||||||
<TouchableOpacity
|
Alert.alert("Error", "Failed to save your todo list.");
|
||||||
onPress={() => router.back()}
|
}
|
||||||
style={styles.backButton}
|
};
|
||||||
|
|
||||||
|
const addTodo = () => {
|
||||||
|
if (newTodo.trim().length > 0) {
|
||||||
|
setTodos((prevTodos) => [...prevTodos, newTodo.trim()]);
|
||||||
|
setNewTodo("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTodo = (index: number) => {
|
||||||
|
const updatedTodos = todos.filter((_, i) => i !== index);
|
||||||
|
setTodos(updatedTodos);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.converterScreenRoot}>
|
||||||
|
<ImageBackground
|
||||||
|
source={converterBgImage}
|
||||||
|
style={styles.converterBackgroundImage}
|
||||||
|
resizeMode="cover"
|
||||||
>
|
>
|
||||||
<Text style={styles.backButtonText}>← Go Back</Text>
|
<View style={styles.converterBackgroundOverlay} />
|
||||||
</TouchableOpacity>
|
</ImageBackground>
|
||||||
|
|
||||||
<Text style={styles.converterTitle}>Todo List</Text>
|
<SafeAreaView style={styles.converterContentWrapper}>
|
||||||
|
<KeyboardAvoidingView
|
||||||
{/* Todo Input Section */}
|
style={styles.converterContainer}
|
||||||
<View style={styles.todoInputContainer}>
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||||
<TextInput
|
>
|
||||||
style={styles.todoInputField}
|
<ScrollView
|
||||||
placeholder="Add a new todo..."
|
contentContainerStyle={styles.converterScrollContent}
|
||||||
placeholderTextColor="#666"
|
|
||||||
value={newTodo}
|
|
||||||
onChangeText={setNewTodo}
|
|
||||||
onSubmitEditing={addTodo}
|
|
||||||
/>
|
|
||||||
<TouchableOpacity onPress={addTodo} style={styles.addTodoButton}>
|
|
||||||
<Text style={styles.addTodoButtonText}>Add</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Todo List Display */}
|
|
||||||
<View style={styles.todoListContainer}>
|
|
||||||
{todos.length === 0 ? (
|
|
||||||
<Text style={styles.placeholderText}>
|
|
||||||
Your todo list is empty. Add some tasks!
|
|
||||||
</Text>
|
|
||||||
) : (
|
|
||||||
todos.map((todo, index) => (
|
|
||||||
<View key={index} style={styles.todoItem}>
|
|
||||||
<Text style={styles.todoText}>{todo}</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => removeTodo(index)}
|
|
||||||
style={styles.removeTodoButton}
|
|
||||||
>
|
>
|
||||||
<Text style={styles.removeTodoButtonText}>X</Text>
|
{/* Custom Back Button */}
|
||||||
</TouchableOpacity>
|
<TouchableOpacity
|
||||||
</View>
|
onPress={() => router.back()}
|
||||||
))
|
style={styles.backButton}
|
||||||
)}
|
>
|
||||||
</View>
|
<Text style={styles.backButtonText}>
|
||||||
</ScrollView>
|
← Go Back
|
||||||
</KeyboardAvoidingView>
|
</Text>
|
||||||
</SafeAreaView>
|
</TouchableOpacity>
|
||||||
</View>
|
|
||||||
);
|
<Text style={styles.converterTitle}>Todo List</Text>
|
||||||
|
|
||||||
|
{/* Todo Input Section */}
|
||||||
|
<View style={styles.todoInputContainer}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.todoInputField}
|
||||||
|
placeholder="Add a new todo..."
|
||||||
|
placeholderTextColor="#666"
|
||||||
|
value={newTodo}
|
||||||
|
onChangeText={setNewTodo}
|
||||||
|
onSubmitEditing={addTodo}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={addTodo}
|
||||||
|
style={styles.addTodoButton}
|
||||||
|
>
|
||||||
|
<Text style={styles.addTodoButtonText}>
|
||||||
|
Add
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Todo List Display */}
|
||||||
|
<View style={styles.todoListContainer}>
|
||||||
|
{todos.length === 0 ? (
|
||||||
|
<Text style={styles.placeholderText}>
|
||||||
|
Your todo list is empty. Add some tasks!
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
todos.map((todo, index) => (
|
||||||
|
<View key={index} style={styles.todoItem}>
|
||||||
|
<Text style={styles.todoText}>
|
||||||
|
{todo}
|
||||||
|
</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => removeTodo(index)}
|
||||||
|
style={styles.removeTodoButton}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={styles.removeTodoButtonText}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</SafeAreaView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue