Skip to content
Snippets Groups Projects
Commit e847427a authored by Joshua Müller's avatar Joshua Müller
Browse files

App

parent 3b944ad9
No related branches found
No related tags found
No related merge requests found
Showing with 13409 additions and 0 deletions
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
# dependencies
node_modules/
# Expo
.expo/
dist/
web-build/
expo-env.d.ts
# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
# Metro
.metro-health-check*
# debug
npm-debug.*
yarn-debug.*
yarn-error.*
# macOS
.DS_Store
*.pem
# local env files
.env*.local
# typescript
*.tsbuildinfo
app-example
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit",
"source.sortMembers": "explicit"
}
}
app.json 0 → 100644
{
"expo": {
"name": "To-Do",
"slug": "To-Do",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "todo",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"edgeToEdgeEnabled": true
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router",
[
"expo-splash-screen",
{
"image": "./assets/images/splash-icon.png",
"imageWidth": 200,
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
]
],
"experiments": {
"typedRoutes": true
}
}
}
import { Stack } from "expo-router";
export default function RootLayout() {
return <Stack screenOptions={{headerShown: false}}/>;
}
import { Ionicons } from '@expo/vector-icons';
import { Checkbox } from 'expo-checkbox';
import { collection, deleteDoc, doc, getDocs, setDoc, updateDoc } from "firebase/firestore";
import { useEffect, useState } from 'react';
import { FlatList, Image, KeyboardAvoidingView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../lib/firebaseConfig";
type ToDoType = {
id: string;
title: string;
isDone: boolean;
};
export default function Index() {
const [todos, setTodos] = useState<ToDoType[]>([]);
const [todoText, setTodoText] = useState<string>('');
const [editingId, setEditingId] = useState<string | null>(null);
const [editingText, setEditingText] = useState<string>('');
useEffect(() => {
const loadTodos = async () => {
try {
const querySnapshot = await getDocs(collection(db, "todosammlung"));
const loadedTodos = querySnapshot.docs.map((doc) => ({
id: String(doc.id),
...doc.data(),
})) as ToDoType[];
setTodos(loadedTodos);
} catch (err) {
console.error("Fehler beim Laden der ToDos:", err);
}
};
loadTodos();
}, []);
const addTodo = async () => {
if (!todoText.trim()) return;
try {
const newId = Date.now().toString();
const newTodo = {
id: newId,
title: todoText,
isDone: false,
};
await setDoc(doc(db, "todosammlung", newId), newTodo);
setTodos((prev) => [...prev, newTodo]);
setTodoText('');
} catch (error) {
console.error("Fehler beim Hinzufügen:", error);
}
};
const toggleTodo = async (id: string, currentValue: boolean) => {
try {
await updateDoc(doc(db, "todosammlung", id), {
isDone: !currentValue,
});
setTodos((prev) =>
prev.map((todo) =>
todo.id === id ? { ...todo, isDone: !currentValue } : todo
)
);
} catch (error) {
console.error("Fehler beim Aktualisieren des Status:", error);
}
};
const deleteTodo = async (id: string) => {
try {
await deleteDoc(doc(db, "todosammlung", id));
setTodos((prev) => prev.filter((todo) => todo.id !== id));
} catch (error) {
console.error("Fehler beim Löschen des ToDos:", error);
}
};
const startEditing = (id: string, title: string) => {
setEditingId(id);
setEditingText(title);
};
const confirmEdit = async () => {
if (!editingId || !editingText.trim()) return;
try {
await updateDoc(doc(db, "todosammlung", editingId), {
title: editingText,
});
setTodos((prev) =>
prev.map((todo) =>
todo.id === editingId ? { ...todo, title: editingText } : todo
)
);
setEditingId(null);
setEditingText('');
} catch (error) {
console.error("Fehler beim Bearbeiten:", error);
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => { alert('Clicked') }}>
<Ionicons name="menu" size={24} color={'#333'} />
</TouchableOpacity>
<TouchableOpacity onPress={() => { }}>
<Image
source={{ uri: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?fm=jpg&q=60&w=3000' }}
style={{ width: 40, height: 40, borderRadius: 20 }}
/>
</TouchableOpacity>
</View>
<View style={styles.searchbar}>
<Ionicons name="search" size={24} color={'#333'} />
<TextInput placeholderTextColor="#999" placeholder="Search" style={styles.searchInput} clearButtonMode='always' />
</View>
<FlatList
data={todos}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<ToDoItem
todo={item}
onDelete={deleteTodo}
onToggle={toggleTodo}
onEdit={startEditing}
/>
)}
/>
{editingId && (
<KeyboardAvoidingView style={styles.footer} behavior='padding' keyboardVerticalOffset={10}>
<TextInput
value={editingText}
placeholder="Edit Task"
placeholderTextColor="#999"
onChangeText={(text) => setEditingText(text)}
style={styles.newTodoInput}
/>
<TouchableOpacity style={styles.addButton} onPress={confirmEdit}>
<Ionicons name="checkmark" size={34} color={'#fff'} />
</TouchableOpacity>
</KeyboardAvoidingView>
)}
{!editingId && (
<KeyboardAvoidingView style={styles.footer} behavior='padding' keyboardVerticalOffset={10}>
<TextInput
value={todoText}
placeholder="Insert Task"
placeholderTextColor="#999"
onChangeText={(text) => setTodoText(text)}
style={styles.newTodoInput}
/>
<TouchableOpacity style={styles.addButton} onPress={addTodo} >
<Ionicons name='add' size={34} color={'#fff'} />
</TouchableOpacity>
</KeyboardAvoidingView>
)}
</SafeAreaView>
);
}
const ToDoItem = ({
todo,
onDelete,
onToggle,
onEdit,
}: {
todo: ToDoType;
onDelete: (id: string) => void;
onToggle: (id: string, current: boolean) => void;
onEdit: (id: string, title: string) => void;
}) => (
<View style={styles.todoContainer}>
<View style={styles.todoInfoContainer}>
<Checkbox
value={todo.isDone}
onValueChange={() => onToggle(todo.id, todo.isDone)}
color={todo.isDone ? '#4630EB' : undefined}
/>
<Text style={[styles.todoText, todo.isDone && { textDecorationLine: 'line-through' }]}>{todo.title}</Text>
</View>
<View style={{ flexDirection: 'row', gap: 10 }}>
<TouchableOpacity onPress={() => onEdit(todo.id, todo.title)}>
<Ionicons name="pencil" size={24} color={'#333'} />
</TouchableOpacity>
<TouchableOpacity onPress={() => onDelete(todo.id)}>
<Ionicons name="trash" size={24} color={'red'} />
</TouchableOpacity>
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 20,
backgroundColor: '#f5f5f5'
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
searchbar: {
backgroundColor: '#fff',
flexDirection: 'row',
alignItems: 'center',
padding: 16,
borderRadius: 10,
gap: 10,
marginBottom: 20,
},
searchInput: {
flex: 1,
height: 25,
fontSize: 18,
color: '#000',
},
todoContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#fff',
padding: 16,
borderRadius: 10,
marginBottom: 20,
},
todoInfoContainer: {
flexDirection: 'row',
gap: 10,
alignItems: 'center',
},
todoText: {
fontSize: 16,
color: '#333',
},
footer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
},
newTodoInput: {
flex: 1,
backgroundColor: '#fff',
padding: 16,
borderRadius: 10,
fontSize: 18,
color: '#333',
},
addButton: {
backgroundColor: '#4630EB',
padding: 8,
borderRadius: 10,
marginLeft: 20,
}
});
File added
assets/images/adaptive-icon.png

17.1 KiB

assets/images/favicon.png

1.43 KiB

assets/images/icon.png

21.9 KiB

assets/images/partial-react-logo.png

4.96 KiB

assets/images/react-logo.png

6.19 KiB

assets/images/react-logo@2x.png

13.9 KiB

assets/images/react-logo@3x.png

20.8 KiB

assets/images/splash-icon.png

17.1 KiB

// https://docs.expo.dev/guides/using-eslint/
const { defineConfig } = require('eslint/config');
const expoConfig = require('eslint-config-expo/flat');
module.exports = defineConfig([
expoConfig,
{
ignores: ['dist/*'],
},
]);
// firebaseConfig.ts
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "AIzaSyDHG4oUX6JmDBVwWdcqnk7nwN9EI0JiQ1U",
authDomain: "mocotodoapp.firebaseapp.com",
projectId: "mocotodoapp",
storageBucket: "mocotodoapp.firebasestorage.app",
messagingSenderId: "428714435259",
appId: "1:428714435259:web:06d67636a622b52e87ff9e"
};
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "to-do",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"lint": "expo lint"
},
"dependencies": {
"@expo/vector-icons": "^14.1.0",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6",
"expo": "~53.0.7",
"expo-blur": "~14.1.4",
"expo-checkbox": "~4.1.4",
"expo-constants": "~17.1.5",
"expo-font": "~13.3.1",
"expo-haptics": "~14.1.4",
"expo-image": "~2.1.6",
"expo-linking": "~7.1.4",
"expo-router": "~5.0.5",
"expo-splash-screen": "~0.30.8",
"expo-status-bar": "~2.2.3",
"expo-symbols": "~0.4.4",
"expo-system-ui": "~5.0.7",
"expo-web-browser": "~14.1.6",
"firebase": "^11.7.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-gesture-handler": "~2.24.0",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.10.0",
"react-native-web": "~0.20.0",
"react-native-webview": "13.13.5"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.10",
"eslint": "^9.25.0",
"eslint-config-expo": "~9.2.0",
"typescript": "~5.8.3"
},
"private": true
}
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment