@@ -1,13 +1,47 @@
|
||||
import { createRouter, Todo } from "@<@var(context.project.name)>/rpc"
|
||||
import { useEffect, useState } from "react"
|
||||
import { ScrollView, Button, Checkbox, Host, Column, TextInput, Text, useNativeState, Row, Icon } from "@expo/ui";
|
||||
import { Platform, PlatformColor, ScrollView, useColorScheme } from "react-native"
|
||||
import Animated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"
|
||||
import { SafeAreaView } from "react-native-safe-area-context"
|
||||
import { Button, Checkbox, Host, Column, TextInput, Text, useNativeState, Row, Icon } from "@expo/ui";
|
||||
import * as Crypto from "expo-crypto"
|
||||
import { colorInvert, controlSize } from "@expo/ui/swift-ui/modifiers";
|
||||
import { fillMaxWidth, padding, width } from "@expo/ui/jetpack-compose/modifiers";
|
||||
import { background, colorInvert, controlSize, padding as iosPadding, shapes } from "@expo/ui/swift-ui/modifiers";
|
||||
// No numeric width() on Android: @expo/ui 57 can't convert it natively and crashes.
|
||||
import { fillMaxWidth, padding, weight } from "@expo/ui/jetpack-compose/modifiers";
|
||||
// The Android emulator reaches the host via 10.0.2.2; the iOS simulator shares the host's network.
|
||||
const apiUrl = Platform.OS === "android" ? "http://10.0.2.2:8080" : "http://127.0.0.1:8080"
|
||||
|
||||
// SwiftUI clips a border to its corner radius, cutting off the corners, so on iOS the
|
||||
// border is an outer rounded fill showing 1pt around an inner one.
|
||||
const iosInputBorder = Platform.OS === "ios" ? [
|
||||
iosPadding({ all: 10 }),
|
||||
background(PlatformColor("secondarySystemGroupedBackground"), shapes.roundedRectangle({ cornerRadius: 9, roundedCornerStyle: "continuous" })),
|
||||
iosPadding({ all: 1 }),
|
||||
background(PlatformColor("separator"), shapes.roundedRectangle({ cornerRadius: 10, roundedCornerStyle: "continuous" })),
|
||||
] : []
|
||||
|
||||
// Animates its position when the list re-sorts. The Host's measured height goes through state:
|
||||
// on Android a layout transition ignores the size matchContents sets natively, collapsing the row.
|
||||
function AnimatedRow({ children }: { children: React.ReactNode }) {
|
||||
const [height, setHeight] = useState<number>()
|
||||
return (
|
||||
<Animated.View layout={LinearTransition.springify()} entering={FadeIn} exiting={FadeOut}>
|
||||
<Host
|
||||
matchContents={{ vertical: true }}
|
||||
style={height ? { height } : undefined}
|
||||
onLayoutContent={(e) => setHeight(e.nativeEvent.height)}
|
||||
>
|
||||
{children}
|
||||
</Host>
|
||||
</Animated.View>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const router = createRouter("http://10.0.2.2:8080")
|
||||
const router = createRouter(apiUrl)
|
||||
const [todos, setTodos] = useState<Array<Todo>>([])
|
||||
const todoToCreateTask = useNativeState<string>("")
|
||||
const colorScheme = useColorScheme()
|
||||
const fetchTodos = () => {
|
||||
router.todos.listTodos({}).then((r) => {
|
||||
setTodos(r.todos)
|
||||
@@ -15,19 +49,17 @@ export default function Index() {
|
||||
}
|
||||
useEffect(() => {
|
||||
fetchTodos();
|
||||
})
|
||||
}, [])
|
||||
|
||||
const setTodoDone = (id: string, task: string) => {
|
||||
return (done: boolean) => {
|
||||
router.todos.updateTodo({ todo: { id, task, done } })
|
||||
fetchTodos()
|
||||
router.todos.updateTodo({ todo: { id, task, done } }).then(fetchTodos)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteTodo = (id: string) => {
|
||||
return () => {
|
||||
router.todos.deleteTodo({ todo: { id } })
|
||||
fetchTodos()
|
||||
router.todos.deleteTodo({ todo: { id } }).then(fetchTodos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,29 +77,43 @@ export default function Index() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<Column alignment="center" modifiers={[fillMaxWidth()]}>
|
||||
<Text textStyle={{fontSize:30}}> Create Todo </Text>
|
||||
<TextInput value={todoToCreateTask} onChangeText={updateTodoToCreateTask} />
|
||||
<Button modifiers={[controlSize('regular')]} label="create Todo" onPress={createTodo} />
|
||||
<ScrollView modifiers={[fillMaxWidth()]}>
|
||||
{
|
||||
todos.map((todo) => (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Host matchContents={{ vertical: true }}>
|
||||
<Column alignment="center" modifiers={[fillMaxWidth()]}>
|
||||
<Text textStyle={{fontSize:30}}> Create Todo </Text>
|
||||
<TextInput
|
||||
value={todoToCreateTask}
|
||||
onChangeText={updateTodoToCreateTask}
|
||||
placeholder="What needs to be done?"
|
||||
style={Platform.OS === "ios"
|
||||
? { width: 280 }
|
||||
// Android draws the border as a fill behind the background, so the background must be opaque.
|
||||
: { padding: 10, borderWidth: 1, borderColor: "#8E8E93", borderRadius: 8, backgroundColor: colorScheme === "dark" ? "#1C1B1F" : "#FFFFFF" }}
|
||||
modifiers={Platform.OS === "ios" ? iosInputBorder : [fillMaxWidth(0.75)]}
|
||||
/>
|
||||
<Button modifiers={[controlSize('regular')]} label="create Todo" onPress={createTodo} />
|
||||
</Column>
|
||||
</Host>
|
||||
<ScrollView>
|
||||
{
|
||||
todos.map((todo) => (
|
||||
// Keyed by id so native toggles follow their todo.
|
||||
<AnimatedRow key={todo.id}>
|
||||
<Row alignment="center" modifiers={[fillMaxWidth(),padding(20,0,20,0)]}>
|
||||
<Column alignment="start" modifiers={[width(200)]}>
|
||||
<Text> {todo.task} </Text>
|
||||
<Column alignment="start" modifiers={[weight(1)]}>
|
||||
<Text>{todo.task}</Text>
|
||||
</Column>
|
||||
<Column alignment="center">
|
||||
<Checkbox value={todo.done || false} onValueChange={setTodoDone(todo.id || "", todo.task)} />
|
||||
</Column>
|
||||
<Column alignment="end" modifiers={[fillMaxWidth()]}>
|
||||
<Column alignment="end">
|
||||
<Button modifiers={[controlSize('regular')]} label="delete" onPress={deleteTodo(todo.id || "")}/>
|
||||
</Column>
|
||||
</Row>
|
||||
))
|
||||
}
|
||||
</ScrollView>
|
||||
</Column>
|
||||
</Host>
|
||||
</AnimatedRow>
|
||||
))
|
||||
}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user