Path based routing for index
This commit is contained in:
parent
581d28a6df
commit
9921da7265
10 changed files with 358 additions and 324 deletions
29
app/app/(tabs)/(home)/_layout.tsx
Normal file
29
app/app/(tabs)/(home)/_layout.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen
|
||||||
|
name="index"
|
||||||
|
options={{
|
||||||
|
header: (props) => <ListNavigationBar {...props} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="lists/[listId]"
|
||||||
|
options={{
|
||||||
|
header: (props) => <ListNavigationBar {...props} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="tasks/[taskId]"
|
||||||
|
options={{
|
||||||
|
header: (props) => <TaskNavigationBar {...props} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
app/app/(tabs)/(home)/index.tsx
Normal file
3
app/app/(tabs)/(home)/index.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import ListContent from "./lists/[listId]";
|
||||||
|
|
||||||
|
export default ListContent;
|
||||||
157
app/app/(tabs)/(home)/lists/[listId].tsx
Normal file
157
app/app/(tabs)/(home)/lists/[listId].tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Card
|
||||||
|
style={{ marginHorizontal: 8, marginBottom: 8 }}
|
||||||
|
onPress={props.onPress}
|
||||||
|
>
|
||||||
|
<Card.Title
|
||||||
|
title={name}
|
||||||
|
subtitle={subtitle}
|
||||||
|
left={(props) => <Avatar.Icon {...props} icon="clipboard-list" />}
|
||||||
|
/*right={(props) => (
|
||||||
|
<IconButton {...props} icon="dots-vertical" onPress={() => {}} />
|
||||||
|
)}*/
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<List.Item
|
||||||
|
title={name}
|
||||||
|
description={ready ? "Ready" : "Not ready"}
|
||||||
|
left={(props) => <List.Icon {...props} icon="clipboard-check-outline" />}
|
||||||
|
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 (
|
||||||
|
<View
|
||||||
|
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
||||||
|
>
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (error || !data) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
||||||
|
>
|
||||||
|
<Text>Something went wrong: {error.toString()}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, parentId, lists, tasks } = data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView
|
||||||
|
style={[styles.container, { backgroundColor: theme.colors.background }]}
|
||||||
|
>
|
||||||
|
{lists.map((l) => (
|
||||||
|
<ListItem
|
||||||
|
key={l.id}
|
||||||
|
data={l}
|
||||||
|
onPress={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/lists/[listId]",
|
||||||
|
params: { listId: l.id, listName: l.name },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{tasks.length > 0 && (
|
||||||
|
<List.Section>
|
||||||
|
<List.Subheader>Tasks</List.Subheader>
|
||||||
|
{tasks.map((t) => (
|
||||||
|
<TaskItem
|
||||||
|
key={t.id}
|
||||||
|
data={t}
|
||||||
|
onPress={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/tasks/[taskId]",
|
||||||
|
params: { taskId: t.id, taskName: t.name },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</List.Section>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
99
app/app/(tabs)/(home)/tasks/[taskId].tsx
Normal file
99
app/app/(tabs)/(home)/tasks/[taskId].tsx
Normal file
|
|
@ -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 (
|
||||||
|
<View
|
||||||
|
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
||||||
|
>
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (error || !data) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
||||||
|
>
|
||||||
|
<Text>Something went wrong: {error?.toString()}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, description, schedule, imageSource } = data;
|
||||||
|
const date = new Date(schedule);
|
||||||
|
const now = new Date();
|
||||||
|
const ready = date <= now;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.padded,
|
||||||
|
styles.centered,
|
||||||
|
{ backgroundColor: theme.colors.background },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Image style={styles.image} source={{ uri: imageSource }} />
|
||||||
|
<RenderHtml
|
||||||
|
contentWidth={dimensions.width}
|
||||||
|
source={{ html: description }}
|
||||||
|
baseStyle={{
|
||||||
|
color: theme.colors.onBackground,
|
||||||
|
fontSize: "1.1em",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button disabled={!ready} mode="contained">
|
||||||
|
{ready ? "Start" : "Not ready"}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -19,7 +19,7 @@ export default function TabLayout() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="(home)"
|
||||||
options={{
|
options={{
|
||||||
title: "Home",
|
title: "Home",
|
||||||
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
|
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
|
||||||
<Card
|
|
||||||
style={{ marginHorizontal: 8, marginBottom: 8 }}
|
|
||||||
onPress={props.onPress}
|
|
||||||
>
|
|
||||||
<Card.Title
|
|
||||||
title={name}
|
|
||||||
subtitle={subtitle}
|
|
||||||
left={(props) => <Avatar.Icon {...props} icon="clipboard-list" />}
|
|
||||||
/*right={(props) => (
|
|
||||||
<IconButton {...props} icon="dots-vertical" onPress={() => {}} />
|
|
||||||
)}*/
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<List.Item
|
|
||||||
title={name}
|
|
||||||
description={ready ? "Ready" : "Not ready"}
|
|
||||||
left={(props) => <List.Icon {...props} icon="clipboard-check-outline" />}
|
|
||||||
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 (
|
|
||||||
<View
|
|
||||||
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
|
||||||
>
|
|
||||||
<Text>Loading...</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (error || !data) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
|
||||||
>
|
|
||||||
<Text>Something went wrong: {error.toString()}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, parentId, lists, tasks } = data;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ScrollView
|
|
||||||
style={[styles.container, { backgroundColor: theme.colors.background }]}
|
|
||||||
>
|
|
||||||
{lists.map((l) => (
|
|
||||||
<ListItem
|
|
||||||
key={l.id}
|
|
||||||
data={l}
|
|
||||||
onPress={() =>
|
|
||||||
navigation.push("List", { listId: l.id, listName: l.name })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{tasks.length > 0 && (
|
|
||||||
<List.Section>
|
|
||||||
<List.Subheader>Tasks</List.Subheader>
|
|
||||||
{tasks.map((t) => (
|
|
||||||
<TaskItem
|
|
||||||
key={t.id}
|
|
||||||
data={t}
|
|
||||||
onPress={() =>
|
|
||||||
navigation.push("Task", { taskId: t.id, taskName: t.name })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</List.Section>
|
|
||||||
)}
|
|
||||||
</ScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<View
|
|
||||||
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
|
||||||
>
|
|
||||||
<Text>Loading...</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (error || !data) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.padded, { backgroundColor: theme.colors.background }]}
|
|
||||||
>
|
|
||||||
<Text>Something went wrong: {error?.toString()}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, description, schedule, imageSource } = data;
|
|
||||||
const date = new Date(schedule);
|
|
||||||
const now = new Date();
|
|
||||||
const ready = date <= now;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.padded,
|
|
||||||
styles.centered,
|
|
||||||
{ backgroundColor: theme.colors.background },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Image style={styles.image} source={{ uri: imageSource }} />
|
|
||||||
<RenderHtml
|
|
||||||
contentWidth={dimensions.width}
|
|
||||||
source={{ html: description }}
|
|
||||||
baseStyle={{
|
|
||||||
color: theme.colors.onBackground,
|
|
||||||
fontSize: "1.1em",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button disabled={!ready} mode="contained">{ready ? "Start" : "Not ready"}</Button>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ListNavigationBar({
|
|
||||||
navigation,
|
|
||||||
route,
|
|
||||||
options,
|
|
||||||
back,
|
|
||||||
}: StackHeaderProps) {
|
|
||||||
const params = route.params as any | undefined;
|
|
||||||
const title = getHeaderTitle(options, params?.listName || route.name);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Appbar.Header elevated>
|
|
||||||
{(back || params?.listId) ? <Appbar.BackAction
|
|
||||||
onPress={() => (back ? navigation.pop() : navigation.replace("List"))}
|
|
||||||
/> : null}
|
|
||||||
<Appbar.Content title={title} />
|
|
||||||
<Appbar.Action
|
|
||||||
icon="dice-multiple-outline"
|
|
||||||
onPress={() =>
|
|
||||||
randomTask(params?.listId || null).then((data) =>
|
|
||||||
navigation.push("Task", { taskId: data.id, taskName: data.name }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/*<Appbar.Action icon="magnify" onPress={() => {}} />*/}
|
|
||||||
</Appbar.Header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function TaskNavigationBar({
|
|
||||||
navigation,
|
|
||||||
route,
|
|
||||||
options,
|
|
||||||
back,
|
|
||||||
}: StackHeaderProps) {
|
|
||||||
const title = getHeaderTitle(
|
|
||||||
options,
|
|
||||||
(route.params as any)?.taskName || route.name,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Appbar.Header elevated>
|
|
||||||
<Appbar.BackAction
|
|
||||||
onPress={() => (back ? navigation.pop() : navigation.replace("List"))}
|
|
||||||
/>
|
|
||||||
<Appbar.Content title={title} />
|
|
||||||
{/*<Appbar.Action icon="run-fast" onPress={() => {}} />*/}
|
|
||||||
{/*<Appbar.Action icon="square-edit-outline" onPress={() => {}} />*/}
|
|
||||||
</Appbar.Header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Stack = createStackNavigator();
|
|
||||||
|
|
||||||
export default function ScreenList() {
|
|
||||||
return (
|
|
||||||
<Stack.Navigator initialRouteName="List">
|
|
||||||
<Stack.Screen
|
|
||||||
name="List"
|
|
||||||
component={ListContent as any}
|
|
||||||
options={{
|
|
||||||
header: (props) => <ListNavigationBar {...props} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="Task"
|
|
||||||
component={TaskContent as any}
|
|
||||||
options={{
|
|
||||||
header: (props) => <TaskNavigationBar {...props} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack.Navigator>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -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<HomeStackParamList>;
|
|
||||||
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<HomeStackParamList, RouteName>;
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace ReactNavigation {
|
|
||||||
interface RootParamList extends RootBottomParamList {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import {
|
import {
|
||||||
DarkTheme as NavigationDarkTheme,
|
DarkTheme as NavigationDarkTheme,
|
||||||
DefaultTheme as NavigationDefaultTheme,
|
DefaultTheme as NavigationDefaultTheme,
|
||||||
DefaultTheme,
|
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
} from "@react-navigation/native";
|
} 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 { useFonts } from "expo-font";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
import * as SplashScreen from "expo-splash-screen";
|
import * as SplashScreen from "expo-splash-screen";
|
||||||
|
|
|
||||||
40
app/components/navigation/ListNavigationBar.tsx
Normal file
40
app/components/navigation/ListNavigationBar.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Appbar.Header elevated>
|
||||||
|
{back || (route.path !== "/") ? (
|
||||||
|
<Appbar.BackAction
|
||||||
|
onPress={() => (back ? router.back() : router.replace("/"))}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Appbar.Content title={title} />
|
||||||
|
<Appbar.Action
|
||||||
|
icon="dice-multiple-outline"
|
||||||
|
onPress={() =>
|
||||||
|
randomTask(listId || null).then((data) =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/tasks/[taskId]",
|
||||||
|
params: { taskId: data.id, taskName: data.name },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{/*<Appbar.Action icon="magnify" onPress={() => {}} />*/}
|
||||||
|
</Appbar.Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
28
app/components/navigation/TaskNavigationBar.tsx
Normal file
28
app/components/navigation/TaskNavigationBar.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Appbar.Header elevated>
|
||||||
|
<Appbar.BackAction
|
||||||
|
onPress={() =>
|
||||||
|
router.canGoBack() ? router.back() : router.replace("/")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Appbar.Content title={title} />
|
||||||
|
{/*<Appbar.Action icon="run-fast" onPress={() => {}} />*/}
|
||||||
|
{/*<Appbar.Action icon="square-edit-outline" onPress={() => {}} />*/}
|
||||||
|
</Appbar.Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue