diff --git a/app/app/(tabs)/(home)/_layout.tsx b/app/app/(tabs)/(home)/_layout.tsx
new file mode 100644
index 0000000..cb69062
--- /dev/null
+++ b/app/app/(tabs)/(home)/_layout.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { Stack } from "expo-router";
+import TaskNavigationBar from "@/components/navigation/TaskNavigationBar";
+import ListNavigationBar from "@/components/navigation/ListNavigationBar";
+
+export default function HomeLayout() {
+ return (
+
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+
+ );
+}
diff --git a/app/app/(tabs)/(home)/index.tsx b/app/app/(tabs)/(home)/index.tsx
new file mode 100644
index 0000000..01dca8e
--- /dev/null
+++ b/app/app/(tabs)/(home)/index.tsx
@@ -0,0 +1,3 @@
+import ListContent from "./lists/[listId]";
+
+export default ListContent;
diff --git a/app/app/(tabs)/(home)/lists/[listId].tsx b/app/app/(tabs)/(home)/lists/[listId].tsx
new file mode 100644
index 0000000..689878f
--- /dev/null
+++ b/app/app/(tabs)/(home)/lists/[listId].tsx
@@ -0,0 +1,157 @@
+import { StyleSheet, ScrollView, View } from "react-native";
+import { Text, Avatar, Card, List, useTheme } from "react-native-paper";
+import useSWR, { preload } from "swr";
+import { ListData, listFetcher, taskFetcher } from "@/api/fetcher";
+import { router, useLocalSearchParams, useNavigation } from "expo-router";
+import { useEffect } from "react";
+
+function ListItem(props: { data: ListData["lists"][0]; onPress(): void }) {
+ const { name, listCount, taskCount } = props.data;
+ let subtitle = "";
+ if (listCount) {
+ subtitle += `${listCount} lists`;
+ }
+ if (listCount && taskCount) {
+ subtitle += " • ";
+ }
+ if (taskCount) {
+ subtitle += `${taskCount} tasks`;
+ }
+ if (!listCount && !taskCount) {
+ subtitle += "Empty";
+ }
+
+ return (
+
+ }
+ /*right={(props) => (
+ {}} />
+ )}*/
+ />
+
+ );
+}
+
+function TaskItem(props: { data: ListData["tasks"][0]; onPress(): void }) {
+ const { id, name, schedule } = props.data;
+ const date = new Date(schedule);
+ const now = new Date();
+ const ready = date <= now;
+
+ return (
+ }
+ onPress={props.onPress}
+ onPressIn={() => preload(id, taskFetcher)}
+ />
+ );
+}
+
+export default function ListContent() {
+ const theme = useTheme();
+ const { listId, listName } = useLocalSearchParams<{
+ listId: string;
+ listName?: string;
+ }>();
+
+ const navigation = useNavigation();
+ const { isLoading, data, error } = useSWR(listId || "all", listFetcher);
+ useEffect(() => {
+ let title = "";
+ if (isLoading) {
+ title = listName || "Loading...";
+ } else if (data) {
+ title = data.name;
+ } else if (error) {
+ title = "Error";
+ }
+ navigation.setOptions({ title });
+ }, [isLoading, data?.name, error, navigation]);
+
+ if (isLoading) {
+ return (
+
+ Loading...
+
+ );
+ }
+ if (error || !data) {
+ return (
+
+ Something went wrong: {error.toString()}
+
+ );
+ }
+
+ const { name, parentId, lists, tasks } = data;
+
+ return (
+
+ {lists.map((l) => (
+
+ router.push({
+ pathname: "/lists/[listId]",
+ params: { listId: l.id, listName: l.name },
+ })
+ }
+ />
+ ))}
+ {tasks.length > 0 && (
+
+ Tasks
+ {tasks.map((t) => (
+
+ router.push({
+ pathname: "/tasks/[taskId]",
+ params: { taskId: t.id, taskName: t.name },
+ })
+ }
+ />
+ ))}
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ paddingVertical: 8,
+ //backgroundColor: "#fff",
+ /*alignItems: "center",
+ justifyContent: "center",*/
+ },
+ padded: {
+ padding: 16,
+ flex: 1,
+ },
+ centered: {
+ alignItems: "center",
+ },
+ image: {
+ width: 300,
+ height: 300,
+ borderRadius: 15,
+ },
+});
diff --git a/app/app/(tabs)/(home)/tasks/[taskId].tsx b/app/app/(tabs)/(home)/tasks/[taskId].tsx
new file mode 100644
index 0000000..7ee25da
--- /dev/null
+++ b/app/app/(tabs)/(home)/tasks/[taskId].tsx
@@ -0,0 +1,99 @@
+import { taskFetcher } from "@/api/fetcher";
+import React, { useEffect } from "react";
+import { useWindowDimensions, View, Image, StyleSheet } from "react-native";
+import { useTheme, Text, Button } from "react-native-paper";
+import useSWR from "swr";
+import RenderHtml from "react-native-render-html";
+import { useLocalSearchParams, useNavigation } from "expo-router";
+
+export default function TaskContent() {
+ const dimensions = useWindowDimensions();
+ const theme = useTheme();
+ const { taskId, taskName } = useLocalSearchParams<{
+ taskId: string;
+ taskName?: string;
+ }>();
+
+ const { isLoading, data, error } = useSWR(taskId, taskFetcher);
+ const navigation = useNavigation();
+ useEffect(() => {
+ let title = "";
+ if (isLoading) {
+ title = taskName || "Loading...";
+ } else if (data) {
+ title = data.name;
+ } else if (error) {
+ title = "Error";
+ }
+ navigation.setOptions({ title });
+ }, [isLoading, data?.name, error, navigation]);
+
+ if (isLoading) {
+ return (
+
+ Loading...
+
+ );
+ }
+ if (error || !data) {
+ return (
+
+ Something went wrong: {error?.toString()}
+
+ );
+ }
+
+ const { name, description, schedule, imageSource } = data;
+ const date = new Date(schedule);
+ const now = new Date();
+ const ready = date <= now;
+
+ return (
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ paddingVertical: 8,
+ //backgroundColor: "#fff",
+ /*alignItems: "center",
+ justifyContent: "center",*/
+ },
+ padded: {
+ padding: 16,
+ flex: 1,
+ },
+ centered: {
+ alignItems: "center",
+ },
+ image: {
+ width: 300,
+ height: 300,
+ borderRadius: 15,
+ },
+});
diff --git a/app/app/(tabs)/_layout.tsx b/app/app/(tabs)/_layout.tsx
index 5317e13..7b5cb90 100644
--- a/app/app/(tabs)/_layout.tsx
+++ b/app/app/(tabs)/_layout.tsx
@@ -19,7 +19,7 @@ export default function TabLayout() {
}}
>
,
diff --git a/app/app/(tabs)/index.tsx b/app/app/(tabs)/index.tsx
deleted file mode 100644
index a982910..0000000
--- a/app/app/(tabs)/index.tsx
+++ /dev/null
@@ -1,295 +0,0 @@
-import {
- StyleSheet,
- ScrollView,
- View,
- useWindowDimensions,
- Image,
-} from "react-native";
-import { Appbar, Text, Avatar, Card, List, useTheme, Button } from "react-native-paper";
-import {
- StackHeaderProps,
- createStackNavigator,
-} from "@react-navigation/stack";
-import RenderHtml from "react-native-render-html";
-import { HomeScreenNavigationProp } from "./types";
-import useSWR, { preload } from "swr";
-import { getHeaderTitle } from "@react-navigation/elements";
-import { useEffect } from "react";
-import { ListData, listFetcher, randomTask, taskFetcher } from "@/api/fetcher";
-
-function ListItem(props: { data: ListData["lists"][0]; onPress(): void }) {
- const { name, listCount, taskCount } = props.data;
- let subtitle = "";
- if (listCount) {
- subtitle += `${listCount} lists`;
- }
- if (listCount && taskCount) {
- subtitle += " • "
- }
- if (taskCount) {
- subtitle += `${taskCount} tasks`;
- }
- if (!listCount && !taskCount) {
- subtitle += "Empty";
- }
-
- return (
-
- }
- /*right={(props) => (
- {}} />
- )}*/
- />
-
- );
-}
-
-function TaskItem(props: { data: ListData["tasks"][0]; onPress(): void }) {
- const { id, name, schedule } = props.data;
- const date = new Date(schedule);
- const now = new Date();
- const ready = date <= now;
-
- return (
- }
- onPress={props.onPress}
- onPressIn={() => preload(id, taskFetcher)}
- />
- );
-}
-
-function ListContent({ route, navigation }: HomeScreenNavigationProp<"List">) {
- const theme = useTheme();
- const { listId, listName } = route.params ?? {};
-
- const { isLoading, data, error } = useSWR(listId || "all", listFetcher);
- useEffect(() => {
- let title = "";
- if (isLoading) {
- title = listName || "Loading...";
- } else if (data) {
- title = data.name;
- } else if (error) {
- title = "Error";
- }
- navigation.setOptions({ title });
- }, [isLoading, data?.name, error, navigation]);
- if (isLoading) {
- return (
-
- Loading...
-
- );
- }
- if (error || !data) {
- return (
-
- Something went wrong: {error.toString()}
-
- );
- }
-
- const { name, parentId, lists, tasks } = data;
-
- return (
-
- {lists.map((l) => (
-
- navigation.push("List", { listId: l.id, listName: l.name })
- }
- />
- ))}
- {tasks.length > 0 && (
-
- Tasks
- {tasks.map((t) => (
-
- navigation.push("Task", { taskId: t.id, taskName: t.name })
- }
- />
- ))}
-
- )}
-
- );
-}
-
-function TaskContent({ route, navigation }: HomeScreenNavigationProp<"Task">) {
- const dimensions = useWindowDimensions();
- const theme = useTheme();
- const { taskId, taskName } = route.params;
-
- const { isLoading, data, error } = useSWR(taskId, taskFetcher);
- useEffect(() => {
- let title = "";
- if (isLoading) {
- title = taskName || "Loading...";
- } else if (data) {
- title = data.name;
- } else if (error) {
- title = "Error";
- }
- navigation.setOptions({ title });
- }, [isLoading, data?.name, error, navigation]);
- if (isLoading) {
- return (
-
- Loading...
-
- );
- }
- if (error || !data) {
- return (
-
- Something went wrong: {error?.toString()}
-
- );
- }
-
- const { name, description, schedule, imageSource } = data;
- const date = new Date(schedule);
- const now = new Date();
- const ready = date <= now;
-
- return (
-
-
-
-
-
- );
-}
-
-function ListNavigationBar({
- navigation,
- route,
- options,
- back,
-}: StackHeaderProps) {
- const params = route.params as any | undefined;
- const title = getHeaderTitle(options, params?.listName || route.name);
-
- return (
-
- {(back || params?.listId) ? (back ? navigation.pop() : navigation.replace("List"))}
- /> : null}
-
-
- randomTask(params?.listId || null).then((data) =>
- navigation.push("Task", { taskId: data.id, taskName: data.name }),
- )
- }
- />
- {/* {}} />*/}
-
- );
-}
-
-function TaskNavigationBar({
- navigation,
- route,
- options,
- back,
-}: StackHeaderProps) {
- const title = getHeaderTitle(
- options,
- (route.params as any)?.taskName || route.name,
- );
-
- return (
-
- (back ? navigation.pop() : navigation.replace("List"))}
- />
-
- {/* {}} />*/}
- {/* {}} />*/}
-
- );
-}
-
-const Stack = createStackNavigator();
-
-export default function ScreenList() {
- return (
-
- ,
- }}
- />
- ,
- }}
- />
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- paddingVertical: 8,
- //backgroundColor: "#fff",
- /*alignItems: "center",
- justifyContent: "center",*/
- },
- padded: {
- padding: 16,
- flex: 1,
- },
- centered: {
- alignItems: "center",
- },
- image: {
- width: 300,
- height: 300,
- borderRadius: 15,
- },
-});
diff --git a/app/app/(tabs)/types.ts b/app/app/(tabs)/types.ts
deleted file mode 100644
index bea9af6..0000000
--- a/app/app/(tabs)/types.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import type {
- NavigatorScreenParams,
- ParamListBase,
-} from "@react-navigation/native";
-import type { StackScreenProps } from "@react-navigation/stack";
-
-export interface RootBottomParamList extends ParamListBase {
- Home: NavigatorScreenParams;
- History: undefined;
- Profile: undefined;
-}
-
-interface HomeStackParamList extends ParamListBase {
- List: { listId: string | null, listName?: string };
- Task: { taskId: string | null, taskName?: string };
-}
-
-export type HomeScreenNavigationProp<
- RouteName extends keyof HomeStackParamList = keyof HomeStackParamList,
-> = StackScreenProps;
-
-declare global {
- namespace ReactNavigation {
- interface RootParamList extends RootBottomParamList {}
- }
-}
diff --git a/app/app/_layout.tsx b/app/app/_layout.tsx
index 458b464..e0804d3 100644
--- a/app/app/_layout.tsx
+++ b/app/app/_layout.tsx
@@ -1,10 +1,9 @@
import {
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
- DefaultTheme,
ThemeProvider,
} from "@react-navigation/native";
-import { PaperProvider, Text, adaptNavigationTheme } from "react-native-paper";
+import { PaperProvider, adaptNavigationTheme } from "react-native-paper";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
diff --git a/app/components/navigation/ListNavigationBar.tsx b/app/components/navigation/ListNavigationBar.tsx
new file mode 100644
index 0000000..244d38a
--- /dev/null
+++ b/app/components/navigation/ListNavigationBar.tsx
@@ -0,0 +1,40 @@
+import { randomTask } from "@/api/fetcher";
+import { getHeaderTitle } from "@react-navigation/elements";
+import type { NativeStackHeaderProps } from "@react-navigation/native-stack";
+import { router, useGlobalSearchParams } from "expo-router";
+import { Appbar } from "react-native-paper";
+
+export default function ListNavigationBar({
+ options,
+ route,
+}: NativeStackHeaderProps) {
+ const { listId, listName } = useGlobalSearchParams<{
+ listId: string;
+ listName?: string;
+ }>();
+ const title = getHeaderTitle(options, listName || route.name);
+ const back = router.canGoBack();
+
+ return (
+
+ {back || (route.path !== "/") ? (
+ (back ? router.back() : router.replace("/"))}
+ />
+ ) : null}
+
+
+ randomTask(listId || null).then((data) =>
+ router.push({
+ pathname: "/tasks/[taskId]",
+ params: { taskId: data.id, taskName: data.name },
+ })
+ )
+ }
+ />
+ {/* {}} />*/}
+
+ );
+}
diff --git a/app/components/navigation/TaskNavigationBar.tsx b/app/components/navigation/TaskNavigationBar.tsx
new file mode 100644
index 0000000..ac09920
--- /dev/null
+++ b/app/components/navigation/TaskNavigationBar.tsx
@@ -0,0 +1,28 @@
+import { router, useGlobalSearchParams } from "expo-router";
+import type { NativeStackHeaderProps } from "@react-navigation/native-stack";
+import { Appbar } from "react-native-paper";
+import { getHeaderTitle } from "@react-navigation/elements";
+
+export default function TaskNavigationBar({
+ options,
+ route,
+}: NativeStackHeaderProps) {
+ const { taskId, taskName } = useGlobalSearchParams<{
+ taskId: string;
+ taskName?: string;
+ }>();
+ const title = getHeaderTitle(options, taskName || route.name);
+
+ return (
+
+
+ router.canGoBack() ? router.back() : router.replace("/")
+ }
+ />
+
+ {/* {}} />*/}
+ {/* {}} />*/}
+
+ );
+}