Adding notes app in React Native
This commit is contained in:
31
FastNotes/app/_layout.tsx
Normal file
31
FastNotes/app/_layout.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
||||
import { Stack } from 'expo-router';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import 'react-native-reanimated';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { NotesProvider } from "@/src/notes/NotesContext"
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||
;
|
||||
|
||||
export default function RootLayout() {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
/*TODO
|
||||
Sort ThemeProvider to work with dark theme on iOS
|
||||
*/
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<NotesProvider>
|
||||
<ThemeProvider value={colorScheme === 'dark' ? DefaultTheme : DefaultTheme}>
|
||||
<Stack>
|
||||
<Stack.Screen name="index" options={{ title: 'FastNotes' }} />
|
||||
<Stack.Screen name="newNote" options={{ title: 'New Note'}}/>
|
||||
<Stack.Screen name="detail" options={{ title: 'Detail'}}/>
|
||||
</Stack>
|
||||
<StatusBar style="auto" />
|
||||
</ThemeProvider>
|
||||
</NotesProvider>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
26
FastNotes/app/detail.tsx
Normal file
26
FastNotes/app/detail.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function DetailScreen()
|
||||
{
|
||||
const { title, content } = useLocalSearchParams<
|
||||
{
|
||||
title?: string;
|
||||
content?: string;
|
||||
}>();
|
||||
|
||||
return(
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>{title ?? "(No title)"}</Text>
|
||||
<Text style={styles.content}>{content ?? ""}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const styles = StyleSheet.create(
|
||||
{
|
||||
container: { flex: 1, padding: 16, gap: 12 },
|
||||
title: { fontSize: 22, fontWeight:"700" },
|
||||
content: { fontSize: 16 }
|
||||
});
|
||||
55
FastNotes/app/index.tsx
Normal file
55
FastNotes/app/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import { router } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useNotes } from "@/src/notes/NotesContext";
|
||||
|
||||
export default function HomeScreen()
|
||||
{
|
||||
const { notes } = useNotes();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<FlatList
|
||||
data={notes}
|
||||
keyExtractor={(n) => n.id}
|
||||
contentContainerStyle={[styles.list, { paddingBottom: 120 }]}
|
||||
renderItem={({ item }) => (
|
||||
<Pressable
|
||||
style={styles.noteItem}
|
||||
onPress={() =>
|
||||
router.push({
|
||||
pathname: "/detail",
|
||||
params: { title: item.title, content: item.content },
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text style={styles.noteTitle}>{item.title}</Text>
|
||||
</Pressable>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Pressable
|
||||
style={[styles.fab, { bottom: insets.bottom + 24, right: insets.right + 40 }]}
|
||||
onPress={() => router.push("/newNote")}
|
||||
>
|
||||
<Text style={styles.fabText}>+</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
list: { padding: 16, gap: 12, paddingTop: 20 },
|
||||
noteItem: { padding: 16, borderWidth: 1, borderRadius:12 },
|
||||
noteTitle: { fontSize: 16, fontWeight: "600" },
|
||||
fab:
|
||||
{
|
||||
position: "absolute",
|
||||
width: 56, height: 56, borderRadius: 28,
|
||||
alignItems: "center", justifyContent: "center",
|
||||
backgroundColor: "grey"
|
||||
},
|
||||
fabText: { fontSize: 28, lineHeight: 28, fontWeight: "700"}
|
||||
});
|
||||
106
FastNotes/app/newNote.tsx
Normal file
106
FastNotes/app/newNote.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import
|
||||
{
|
||||
StyleSheet, Text, View, KeyboardAvoidingView,
|
||||
Platform, Pressable, TextInput, ScrollView
|
||||
} from "react-native";
|
||||
import { router, } from "expo-router";
|
||||
import { useNotes } from "@/src/notes/NotesContext";
|
||||
import { useState, useRef } from "react";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useHeaderHeight } from "@react-navigation/elements";
|
||||
|
||||
|
||||
|
||||
export default function NewNoteScreen()
|
||||
{
|
||||
const { addNote } = useNotes();
|
||||
const[title, setTitle] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
const insets = useSafeAreaInsets();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const [contentHeight, setContentHeight] = useState(160);
|
||||
const scrollRef = useRef<ScrollView>(null);
|
||||
|
||||
const onSave = () =>
|
||||
{
|
||||
if(!title.trim() && !content.trim()) return;
|
||||
addNote(title, content);
|
||||
router.back();
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView style={{ flex: 1}} behavior={Platform.OS === "ios" ? "padding" : undefined}
|
||||
keyboardVerticalOffset={headerHeight}>
|
||||
<View style={styles.container}>
|
||||
<ScrollView ref={scrollRef} contentContainerStyle={{ padding: 16, paddingBottom: 120, gap: 10}}
|
||||
keyboardShouldPersistTaps="handled">
|
||||
<Text style={styles.label}>Title</Text>
|
||||
<TextInput value={title} onChangeText={setTitle}
|
||||
placeholder="Give it a title..." style={styles.input}
|
||||
returnKeyType="next"/>
|
||||
|
||||
<Text style={styles.label}>Content</Text>
|
||||
<TextInput value={content} onChangeText={setContent} placeholder="Write your note..."
|
||||
style={[styles.input, { height: Math.max(160, contentHeight) }]} multiline
|
||||
textAlignVertical="top"
|
||||
onContentSizeChange={(e) => {setContentHeight(e.nativeEvent.contentSize.height);
|
||||
scrollRef.current?.scrollToEnd({ animated: true });
|
||||
}}/>
|
||||
|
||||
</ScrollView>
|
||||
<View>
|
||||
<Pressable onPress={onSave}
|
||||
style={[styles.saveFloating, { bottom: insets.bottom + 16 }]}>
|
||||
<Text style={styles.saveFloatingText}>Save</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create(
|
||||
{
|
||||
container: { flex: 1, gap: 10 },
|
||||
label: { fontSize: 14, fontWeight: "600" },
|
||||
input:
|
||||
{
|
||||
borderWidth: 1, borderRadius: 7,
|
||||
padding: 12, fontSize: 16
|
||||
},
|
||||
saveBtn:
|
||||
{
|
||||
borderRadius: 12,
|
||||
paddingVertical: 14, alignItems: "center",
|
||||
backgroundColor: "grey"
|
||||
},
|
||||
saveText: { color: "white", fontSize: 16, fontWeight: "700"},
|
||||
saveBar:
|
||||
{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
paddingTop: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: "white",
|
||||
borderTopWidth: 1
|
||||
},
|
||||
saveFloating:
|
||||
{
|
||||
position: "absolute",
|
||||
left: 16,
|
||||
right: 16,
|
||||
borderRadius: 12,
|
||||
paddingVertical: 14,
|
||||
alignItems: "center",
|
||||
backgroundColor: "#111"
|
||||
},
|
||||
saveFloatingText:
|
||||
{
|
||||
color: "white",
|
||||
fontSize: 16,
|
||||
fontWeight: "700"
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user