diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/Edit.jsx | 127 | ||||
-rw-r--r-- | components/List.jsx | 72 | ||||
-rw-r--r-- | components/Login.jsx | 90 | ||||
-rw-r--r-- | components/Menu.jsx | 71 | ||||
-rw-r--r-- | components/Note.jsx | 38 | ||||
-rw-r--r-- | components/index.js | 7 |
6 files changed, 405 insertions, 0 deletions
diff --git a/components/Edit.jsx b/components/Edit.jsx new file mode 100644 index 0000000..d2ec6b3 --- /dev/null +++ b/components/Edit.jsx @@ -0,0 +1,127 @@ +import { StyleSheet, Text, TextInput , View } from 'react-native'; +import { useState, useEffect } from 'react' +import Menu from './Menu' + +const Edit = ({ edit, setEdit, session, setSession, showError }) => { + const [saving, setSaving] = useState() + const [title, setTitle] = useState(edit ? edit.title : '') + const [content, setContent] = useState() + + const fetchNote = async () => { + try { + const response = await fetch(`https://apps.pruss.it/api/notes/${edit.noteId}`, { + method: 'GET', headers: { 'Cookie': session.cookies }, + }) + const { content } = await response.json() + setContent(content) + } catch(e) { + showError('Error while fetching note') + setEdit(null) + } + } + + const saveNote = async () => { + try { + setSaving(true) + await fetch(`https://apps.pruss.it/api/notes/${edit._id}`, { + method: 'PUT', + headers: { 'Content-Type': 'plain/text; charset=utf-8', 'Cookie': session.cookies }, + body: JSON.stringify({ title, noteId: edit.noteId, content }) + }) + setSaving(false) + setEdit(null) + } catch(e) { + showError('Error while saving note') + setSaving(false) + setEdit(null) + } + } + + createNote = async () => { + try { + setSaving(true) + await fetch(`https://apps.pruss.it/api/notes`, { + method: 'POST', + headers: { 'Content-Type': 'plain/text; charset=utf-8', 'Cookie': session.cookies }, + body: JSON.stringify({ title, content }) + }) + setSaving(false) + setEdit(null) + } catch(e) { + showError('Error while saving note') + setSaving(false) + setEdit(null) + } + } + + useEffect(() => { + if (edit?._id) { + fetchNote() + } else { + setContent('') + } + }, []) + + return ( + <> + <Menu + session={session} + setSession={setSession} + showError={showError} + setEdit={setEdit} + saveNote={edit._id ? saveNote : createNote} + /> + { + content === undefined || saving + ? <Text style={styles.text}>{saving ? 'Saving...' : 'Loading...'}</Text> + : ( + <View style={styles.container}> + <TextInput + placeholder="Title" + placeholderTextColor="#BBB" + style={styles.title} + value={title} + onChange={e => setTitle(e.nativeEvent.text)} + /> + <TextInput + placeholder="Content" + placeholderTextColor="#BBB" + multiline={true} + textAlignVertical="top" + style={styles.content} + value={content} + onChange={e => setContent(e.nativeEvent.text)} + /> + </View> + ) + } + </> + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 15, + marginBottom: 50, + }, + title: { + color: 'white', + borderBottomColor: 'white', + borderBottomWidth: 1, + paddingBottom: 15, + marginBottom: 10, + fontWeight: 'bold', + }, + content: { + color: 'white', + flexGrow: 1, + paddingVertical: 10, + }, + text: { + padding: 15, + color: 'white', + } +}); + +export default Edit diff --git a/components/List.jsx b/components/List.jsx new file mode 100644 index 0000000..ae578d9 --- /dev/null +++ b/components/List.jsx @@ -0,0 +1,72 @@ +import { StyleSheet, FlatList, Text } from 'react-native' +import { useState, useEffect } from 'react' +import Note from './Note' +import Menu from './Menu' + +const List = ({ session, setSession, showError, setEdit }) => { + const [list, setList] = useState() + const [loading, setLoading] = useState(true) + + const fetchNotes = async () => { + setLoading(true) + try { + const response = await fetch('https://apps.pruss.it/api/notes', { 'Cookie': session.cookies } ) + const data = await response.json() + setList(data) + } catch(e) { + showError('Error while fetching notes') + } + setLoading(false) + } + + useEffect(() => { + setLoading(true) + fetchNotes() + }, []) + + return ( + <> + <Menu + session={session} + setSession={setSession} + showError={showError} + setEdit={setEdit} + /> + { + loading || list === undefined + ? <Text style={styles.text}>Loading notes...</Text> + : ( + <FlatList + data={list.sort((a,b) => new Date(b.updated_at) - new Date(a.updated_at))} + renderItem={({item}) => <Note note={item} setEdit={setEdit} />} + ListEmptyComponent={<Text style={styles.text}>You don't have any notes</Text>} + onRefresh={fetchNotes} + refreshing={loading} + /> + ) + } + </> + ) +} + +const styles = StyleSheet.create({ + text: { + color: 'white', + padding: 20, + }, + menu: { + backgroundColor: 'lightgrey', + height: 55, + width: '100%', + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'space-between', + padding: 10, + }, + menuText: { + fontWeight: 'bold', + paddingHorizontal: 10, + }, +}); + +export default List; diff --git a/components/Login.jsx b/components/Login.jsx new file mode 100644 index 0000000..4c6ffb0 --- /dev/null +++ b/components/Login.jsx @@ -0,0 +1,90 @@ +import { StyleSheet, Text, TextInput, Pressable, View } from 'react-native'; +import { useState } from 'react'; + +const Login = ({ login }) => { + const [email, setEmail] = useState(''); + const [pass, setPass] = useState(''); + const disabled = (e, p) => !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e) || p.length < 6; + + return ( + <View style={styles.container}> + <Text style={styles.title}>Login to Notes</Text> + <Text style={styles.text}>Account can be created through apps.pruss.it website</Text> + <TextInput + style={styles.input} + autoComplete="email" + placeholder="Email" + placeholderTextColor="#BBB" + keyboardType="email-address" + returnKeyType="next" + autoComplete="email" + textContentType="emailAddress" + value={email} + onChange={e => setEmail(e.nativeEvent.text.toLowerCase())} + /> + <TextInput + style={styles.input} + autoComplete="password" + placeholder="Password" + placeholderTextColor="#BBB" + returnKeyType="next" + autoComplete="password-new" + secureTextEntry={true} + textContentType="newPassword" + value={pass} + onChange={e => setPass(e.nativeEvent.text)} + /> + <Pressable + onPress={() => login(email, pass)} + style={{...styles.button, opacity: disabled(email, pass) ? .3 : 1}} + disabled={disabled(email, pass)} + > + <Text style={styles.buttonText}>Login</Text> + </Pressable> + </View> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: '100%' + }, + title: { + color: 'white', + fontSize: 26, + marginBottom: 40, + }, + text: { + color: 'white', + marginBottom: 50, + }, + input: { + height: 50, + width: 280, + padding: 10, + marginBottom: 20, + color: 'white', + backgroundColor: '#444', + borderColor: '#aaa', + borderWidth: 1, + borderStyle: 'solid', + borderRadius: 10, + }, + button: { + width: 100, + height: 40, + backgroundColor: '#666', + borderRadius: 5, + marginTop: 40, + alignItems: 'center', + justifyContent: 'center', + }, + buttonText: { + color: 'white', + } +}); + +export default Login; diff --git a/components/Menu.jsx b/components/Menu.jsx new file mode 100644 index 0000000..bc883e4 --- /dev/null +++ b/components/Menu.jsx @@ -0,0 +1,71 @@ +import { StyleSheet, Text, View } from 'react-native'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +const Menu = ({ session, setSession, setEdit, showError, saveNote }) => { + const logout = async () => { + try { + await AsyncStorage.clear(); + setSession(null) + } catch(e) { + showError('Error while logging out') + } + } + + return ( + <View style={styles.menu}> + { + saveNote + ? ( + <> + <Text + style={styles.menuText} + onPress={() => setEdit(null)} + > + Back + </Text> + <Text + style={styles.menuText} + onPress={saveNote} + > + Save + </Text> + </> + ) : ( + <> + <Text + style={styles.menuText} + onPress={() => setEdit({})} + > + New Note + </Text> + <Text style={styles.menuText}>{session.email}</Text> + <Text + style={styles.menuText} + onPress={logout} + > + Logout + </Text> + </> + ) + } + </View> + ) +} + +const styles = StyleSheet.create({ + menu: { + backgroundColor: 'lightgrey', + height: 55, + width: '100%', + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'space-between', + padding: 10, + }, + menuText: { + fontWeight: 'bold', + paddingHorizontal: 10, + }, +}); + +export default Menu diff --git a/components/Note.jsx b/components/Note.jsx new file mode 100644 index 0000000..d13e786 --- /dev/null +++ b/components/Note.jsx @@ -0,0 +1,38 @@ +import { Pressable, StyleSheet, Text } from 'react-native' + +const Note = ({ note, setEdit }) => { + const formatDate = d => d.replace('T',' ').replace(/\..*Z/, '') + + return ( + <Pressable + style={styles.note} + onPress={() => setEdit(note)} + > + <Text style={styles.title}>{note.title}</Text> + <Text style={styles.dates}>{`Created at: ${formatDate(note.created_at)}`}</Text> + <Text style={styles.dates}>{`Updated at: ${formatDate(note.updated_at)}`}</Text> + </Pressable> + ) +} + +const styles = StyleSheet.create({ + note: { + textAlign: 'left', + padding: 15, + width: '100%', + borderColor: '#222', + borderStyle: 'solid', + borderTopWidth: 1, + borderBottomWidth: 1, + }, + title: { + color: 'white', + fontSize: 18, + }, + dates: { + color: '#bbb', + fontSize: 10, + }, +}); + +export default Note diff --git a/components/index.js b/components/index.js new file mode 100644 index 0000000..0f227df --- /dev/null +++ b/components/index.js @@ -0,0 +1,7 @@ +import Edit from './Edit' +import List from './List' +import Login from './Login' +import Menu from './Menu' +import Note from './Note' + +export { Edit, List, Login, Menu, Note } |