diff options
author | 2022-05-21 18:42:30 +0100 | |
---|---|---|
committer | 2022-05-21 20:04:22 +0100 | |
commit | 774113edda1b9219001ef4adab3f4e19c46bcecc (patch) | |
tree | 6cbf37abb72e688e5a01f2ceebe56c97874d65d7 | |
parent | bbdf817bc139f5d647a6508802e90370267d2af7 (diff) | |
download | notes_mobile-774113edda1b9219001ef4adab3f4e19c46bcecc.tar.gz notes_mobile-774113edda1b9219001ef4adab3f4e19c46bcecc.tar.bz2 notes_mobile-774113edda1b9219001ef4adab3f4e19c46bcecc.zip |
refactor
-rw-r--r-- | App.js | 75 | ||||
-rw-r--r-- | api.js | 31 | ||||
-rw-r--r-- | components/Edit.jsx | 60 | ||||
-rw-r--r-- | components/List.jsx | 71 | ||||
-rw-r--r-- | components/Login.jsx | 19 | ||||
-rw-r--r-- | components/Main.jsx | 26 | ||||
-rw-r--r-- | components/Menu.jsx | 100 | ||||
-rw-r--r-- | components/RemoveNoteButton.jsx | 31 | ||||
-rw-r--r-- | components/index.js | 3 | ||||
-rw-r--r-- | helpers.jsx | 99 |
10 files changed, 276 insertions, 239 deletions
@@ -1,31 +1,13 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, SafeAreaView, Text } from 'react-native'; -import { useState, useEffect } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { Login, List, Edit } from './components' +import { StatusBar } from 'expo-status-bar' +import { StyleSheet, SafeAreaView, Text } from 'react-native' +import { useState, useEffect } from 'react' +import AsyncStorage from '@react-native-async-storage/async-storage' +import Main from './components/Main' +import Login from './components/Login' export default function App() { - const [session, setSession] = useState(); - const [error, setError] = useState(null); - const [edit, setEdit] = useState(null) - - const login = async (e, p) => { - try { - const response = await fetch('https://apps.pruss.it/api/login', { - method: 'POST', - headers: { 'Content-Type': 'plain/text; charset=utf-8' }, - body: JSON.stringify({ email: e, password: p }) - }) - const cookies = response.headers?.map?.['set-cookie'] - const data = await response.json() - if (data?.isLoggedIn) { - await AsyncStorage.setItem('session', JSON.stringify({...data, cookies})) - setSession({ ...data, cookies }) - } - } catch(e) { - showError('Error while logging in') - } - } + const [session, setSession] = useState() + const [error, setError] = useState(null) const showError = (e) => { setError(e) @@ -34,48 +16,29 @@ export default function App() { useEffect(() => { AsyncStorage.getItem('session') - .then(s => setSession(JSON.parse(s))) + .then(session => setSession(JSON.parse(session))) .catch(() => setSession(null)) - }, []); + }, []) - if (error) return ( + if (error || session === undefined) return ( <SafeAreaView style={styles.container}> - <Text style={styles.error}>{error}</Text> + {error + ? <Text style={styles.error}>{error}</Text> + : <Text>Loading...</Text>} </SafeAreaView> ) - if (session === undefined) return ( - <SafeAreaView style={styles.container}> - <Text>Loading...</Text> - </SafeAreaView> - ); - return ( <SafeAreaView style={styles.container}> { session === null - ? <Login login={login} /> - : edit - ? ( - <Edit - edit={edit} - setEdit={setEdit} - session={session} - setSession={setSession} - showError={showError} - /> - ) : ( - <List - session={session} - showError={showError} - setEdit={setEdit} - /> - ) + ? <Login setSession={setSession} showError={showError} /> + : <Main session={session} setSession={setSession} showError={showError} /> } <StatusBar style="auto" /> </SafeAreaView> - ); -}; + ) +} const styles = StyleSheet.create({ container: { @@ -85,4 +48,4 @@ const styles = StyleSheet.create({ error: { color: 'red', } -}); +}) @@ -0,0 +1,31 @@ +const API = 'https://apps.pruss.it/api' + +export const login = ({ email, password }) => fetch(`${API}/login`, { + method: 'POST', + headers: { 'Content-Type': 'plain/text; charset=utf-8' }, + body: JSON.stringify({ email, password }) +}) + +export const getList = ({ session }) => fetch(`${API}/notes`, { + 'Cookie': session.cookies +}) + +export const getNote = ({ note, session }) => fetch(`${API}/notes/${note.noteId}`, { + method: 'GET', headers: { 'Cookie': session.cookies }, +}) + +export const createNote = ({ title, content, session }) => fetch(`${API}/notes`, { + method: 'POST', + headers: { 'Content-Type': 'plain/text; charset=utf-8', 'Cookie': session.cookies }, + body: JSON.stringify({ title, content }) +}) + +export const editNote = ({ note, title, content, session }) => fetch(`${API}/notes/${note._id}`, { + method: 'PUT', + headers: { 'Content-Type': 'plain/text; charset=utf-8', 'Cookie': session.cookies }, + body: JSON.stringify({ title, noteId: note.noteId, content }) +}) + +export const removeNote = ({ note, session }) => fetch(`${API}/notes/${note._id}`, { + method: 'DELETE', headers: { 'Cookie': session.cookies }, +}) diff --git a/components/Edit.jsx b/components/Edit.jsx index 3ca7b32..7818637 100644 --- a/components/Edit.jsx +++ b/components/Edit.jsx @@ -1,62 +1,20 @@ import { StyleSheet, Text, TextInput , View } from 'react-native'; import { useState, useEffect } from 'react' +import { handleGetNote, handleCreateNote, handleEditNote } from '../helpers'; import Menu from './Menu' -const Edit = ({ edit, setEdit, session, setSession, showError }) => { +const Edit = ({ edit: note, setEdit, session, setSession, showError }) => { const [saving, setSaving] = useState() - const [title, setTitle] = useState(edit ? edit.title : '') + const [title, setTitle] = useState(note ? note.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) - } - } + const saveNote = () => note._id + ? handleEditNote({ note, title, content, setSaving, setEdit, session, showError }) + : handleCreateNote({ title, content, setSaving, setEdit, session, showError }) useEffect(() => { - if (edit?._id) { - fetchNote() + if (note?._id) { + handleGetNote({ note, setContent, setEdit, session, showError }) } else { setContent('') } @@ -69,7 +27,7 @@ const Edit = ({ edit, setEdit, session, setSession, showError }) => { setSession={setSession} showError={showError} setEdit={setEdit} - saveNote={edit._id ? saveNote : createNote} + saveNote={saveNote} /> <View style={styles.container}> <TextInput diff --git a/components/List.jsx b/components/List.jsx index b7bdc07..b280d4a 100644 --- a/components/List.jsx +++ b/components/List.jsx @@ -1,8 +1,11 @@ -import { StyleSheet, Text, View, Pressable, Alert } from 'react-native' +import { StyleSheet, Text } from 'react-native' import SwipeableFlatList from 'react-native-swipeable-list' import { useState, useEffect } from 'react' +import { getList } from '../api' import Note from './Note' import Menu from './Menu' +import RemoveNoteButton from './RemoveNoteButton' +import { handleRemove } from "../helpers"; const List = ({ session, setSession, showError, setEdit }) => { const [list, setList] = useState() @@ -11,7 +14,7 @@ const List = ({ session, setSession, showError, setEdit }) => { const fetchNotes = async () => { setLoading(true) try { - const response = await fetch('https://apps.pruss.it/api/notes', { 'Cookie': session.cookies } ) + const response = await getList({ session }) const data = await response.json() setList(data) } catch(e) { @@ -25,51 +28,6 @@ const List = ({ session, setSession, showError, setEdit }) => { fetchNotes() }, []) - const deleteNote = async (note) => { - setLoading(true) - try { - await fetch(`https://apps.pruss.it/api/notes/${note._id}`, { - method: 'DELETE', headers: { 'Cookie': session.cookies }, - }) - fetchNotes() - } catch(e) { - showError('Error while removing note') - } finally { - setLoading(false) - } - } - - const deleteItem = note => { - Alert.alert( - 'Are you sure?', - `Note "${note.title}" will be permanently removed`, - [ - { - text: 'Remove', - onPress: () => deleteNote(note), - style: 'destructive', - }, - { - text: 'Cancel', - onPress: () => {}, - style: 'cancel', - }, - ], - ); - }; - - const removeBtn = (note) => { - return ( - <View style={styles.buttonsContainer}> - <View style={styles.button}> - <Pressable onPress={() => deleteItem(note)}> - <Text style={styles.buttonText}>Delete</Text> - </Pressable> - </View> - </View> - ); - }; - return ( <> <Menu @@ -90,8 +48,10 @@ const List = ({ session, setSession, showError, setEdit }) => { onRefresh={fetchNotes} refreshing={loading} maxSwipeDistance={80} - renderQuickActions={({item}) => removeBtn(item)} bounceFirstRowOnMount={false} + renderQuickActions={({item}) => RemoveNoteButton({ + removeNote: () => handleRemove({ note: item, session, fetchNotes, setLoading, showError }) + })} /> ) } @@ -104,21 +64,6 @@ const styles = StyleSheet.create({ color: 'white', padding: 20, }, - button: { - width: 80, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'red', - }, - buttonText: { - fontWeight: 'bold', - color: 'white', - }, - buttonsContainer: { - flex: 1, - flexDirection: 'row', - justifyContent: 'flex-end', - }, }); export default List; diff --git a/components/Login.jsx b/components/Login.jsx index 4c6ffb0..8b42913 100644 --- a/components/Login.jsx +++ b/components/Login.jsx @@ -1,9 +1,10 @@ -import { StyleSheet, Text, TextInput, Pressable, View } from 'react-native'; -import { useState } from 'react'; +import { StyleSheet, Text, TextInput, Pressable, View } from 'react-native' +import { useState } from 'react' +import { handleLogin } from '../helpers' -const Login = ({ login }) => { +const Login = ({ setSession, showError }) => { const [email, setEmail] = useState(''); - const [pass, setPass] = useState(''); + const [password, setPassword] = useState(''); const disabled = (e, p) => !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e) || p.length < 6; return ( @@ -31,13 +32,13 @@ const Login = ({ login }) => { autoComplete="password-new" secureTextEntry={true} textContentType="newPassword" - value={pass} - onChange={e => setPass(e.nativeEvent.text)} + value={password} + onChange={e => setPassword(e.nativeEvent.text)} /> <Pressable - onPress={() => login(email, pass)} - style={{...styles.button, opacity: disabled(email, pass) ? .3 : 1}} - disabled={disabled(email, pass)} + onPress={() => handleLogin({ email, password, setSession, showError })} + style={{...styles.button, opacity: disabled(email, password) ? .3 : 1}} + disabled={disabled(email, password)} > <Text style={styles.buttonText}>Login</Text> </Pressable> diff --git a/components/Main.jsx b/components/Main.jsx new file mode 100644 index 0000000..3f48c3e --- /dev/null +++ b/components/Main.jsx @@ -0,0 +1,26 @@ +import { List, Edit } from '.' +import { useState } from 'react' + +const Main = ({ session, setSession, showError }) => { + const [edit, setEdit] = useState(null) + + return edit + ? ( + <Edit + edit={edit} + setEdit={setEdit} + session={session} + setSession={setSession} + showError={showError} + /> + ) : ( + <List + session={session} + setSession={setSession} + showError={showError} + setEdit={setEdit} + /> + ) +} + +export default Main diff --git a/components/Menu.jsx b/components/Menu.jsx index fb82f34..519fe94 100644 --- a/components/Menu.jsx +++ b/components/Menu.jsx @@ -1,61 +1,49 @@ -import { StyleSheet, Text, View, Alert } from 'react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { confirmLogout } from '../helpers' +import { StyleSheet, Text, View } from 'react-native'; +import { handleLogout } from '../helpers' -const Menu = ({ session, setSession, setEdit, showError, saveNote }) => { - const logout = async () => { - try { - await AsyncStorage.clear(); - setSession(null) - } catch(e) { - showError('Error while logging out') +const Menu = ({ session, setSession, setEdit, showError, saveNote }) => ( + <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} + > + Sort + </Text> + <Text + style={styles.menuText} + onPress={() => handleLogout({ session, setSession, showError })} + > + {session.email} + </Text> + </> + ) } - } - - 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} - > - Sort - </Text> - <Text - style={styles.menuText} - onPress={() => confirmLogout({ logout, email: session.email })} - > - {session.email} - </Text> - </> - ) - } - </View> - ) -} + </View> +) const styles = StyleSheet.create({ menu: { diff --git a/components/RemoveNoteButton.jsx b/components/RemoveNoteButton.jsx new file mode 100644 index 0000000..d88d6b0 --- /dev/null +++ b/components/RemoveNoteButton.jsx @@ -0,0 +1,31 @@ +import { StyleSheet, Text, View, Pressable } from 'react-native' + +const RemoveNoteButton = ({ removeNote }) => ( + <View style={styles.buttonsContainer}> + <View style={styles.button}> + <Pressable onPress={removeNote}> + <Text style={styles.buttonText}>Delete</Text> + </Pressable> + </View> + </View> +); + +const styles = StyleSheet.create({ + button: { + width: 80, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'red', + }, + buttonText: { + fontWeight: 'bold', + color: 'white', + }, + buttonsContainer: { + flex: 1, + flexDirection: 'row', + justifyContent: 'flex-end', + }, +}); + +export default RemoveNoteButton; diff --git a/components/index.js b/components/index.js index 0f227df..7323846 100644 --- a/components/index.js +++ b/components/index.js @@ -1,7 +1,6 @@ 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 } +export { Edit, List, Menu, Note } diff --git a/helpers.jsx b/helpers.jsx index cf95ac2..427158d 100644 --- a/helpers.jsx +++ b/helpers.jsx @@ -1,9 +1,34 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' import { Alert } from 'react-native'; +import { login, getNote, editNote, createNote, removeNote } from './api' + +export const handleLogin = async ({ email, password, setSession, showError }) => { + try { + const response = await login({ email, password }) + const cookies = response.headers?.map?.['set-cookie'] + const data = await response.json() + if (data?.isLoggedIn) { + await AsyncStorage.setItem('session', JSON.stringify({ ...data, cookies })) + setSession({ ...data, cookies }) + } + } catch(e) { + showError('Error while logging in') + } +} + +export const handleLogout = ({ session, setSession, showError }) => { + const logout = async () => { + try { + await AsyncStorage.clear(); + setSession(null) + } catch(e) { + showError('Error while logging out') + } + } -export const confirmLogout = ({ logout, email }) => { Alert.alert( 'Are you sure?', - `Do you want to log out user ${email}?`, + `Do you want to log out user ${session.email}?`, [ { text: 'Logout', @@ -18,3 +43,73 @@ export const confirmLogout = ({ logout, email }) => { ], ); } + +export const handleGetNote = async ({ note, setContent, setEdit, session, showError }) => { + try { + const response = await getNote({ note, session }) + const { content } = await response.json() + setContent(content) + } catch(e) { + showError('Error while fetching note') + setEdit(null) + } +} + +export const handleEditNote = async ({ note, title, content, setSaving, setEdit, session, showError }) => { + try { + setSaving(true) + await editNote({ note, title, content, session }) + setSaving(false) + setEdit(null) + } catch(e) { + showError('Error while saving note') + setSaving(false) + setEdit(null) + } +} + +export const handleCreateNote = async ({ title, content, setSaving, setEdit, session, showError }) => { + try { + setSaving(true) + await createNote({ title, content, session }) + setSaving(false) + setEdit(null) + } catch(e) { + showError('Error while saving note') + setSaving(false) + setEdit(null) + } +} + +export const handleRemove = ({ note, session, fetchNotes, setLoading, showError }) => { + const deleteNote = async () => { + setLoading(true) + try { + await removeNote({ note, session }) + fetchNotes() + } catch(e) { + console.log(e) + showError('Error while removing note') + setLoading(false) + } + } + + Alert.alert( + 'Are you sure?', + `Note "${note.title}" will be permanently removed`, + [ + { + text: 'Remove', + onPress: deleteNote, + style: 'destructive', + }, + { + text: 'Cancel', + onPress: () => {}, + style: 'cancel', + }, + ], + ); +}; + + |