Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 160 additions & 10 deletions App.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,171 @@
import { SafeAreaView, Text } from 'react-native';
import tw, { useDeviceContext } from 'twrnc';
import MasonryList from '@react-native-seoul/masonry-list';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Image, SafeAreaView, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import 'react-native-reanimated';
import { Provider } from 'react-redux';
import tw, { useDeviceContext } from 'twrnc';
import { useAddNoteMutation, useDeleteNoteMutation, useFetchNotesQuery, useSearchNotesQuery, useUpdateNoteMutation } from './db';
import { store } from './store';
import 'react-native-reanimated';

//Daniel Flemming

const Stack = createNativeStackNavigator();

//This is the note object. Can be added dynamically
const Note = ({item, nav}) => {

titleCalculator = () => {
if(item.title.length >= 40){
return item.title.substring(0, 70) + "..."
}
return item.title
}

contentCalculator = () => {
if(item.content.length >=40){
return item.content.substring(0, 256) + "..."
}
return item.content
}
return (
<TouchableOpacity onPress={() => {nav.navigate('Details', {note : item});}} style = {[tw`m-1 p-5 rounded-lg bg-[#2F0082]`]}>
<View>
<Text style={tw`text-white text-4 font-bold pb-1`}>{titleCalculator()}</Text>
<Text style={tw`text-white`}>{contentCalculator()}</Text>
</View>
</TouchableOpacity>
);
}

// HomeScreen component handles the main functionality of adding, searching, and displaying notes
function HomeScreen({route, navigation}){
const [addNote, {data : addNoteData}] = useAddNoteMutation();
const {data, error, isLoading} = useFetchNotesQuery();
const [text, setText] = useState("");
const {data : filteredData, isLoading : searchNotesLoading} = useSearchNotesQuery(""+text);

useEffect(() => {
if(addNoteData != undefined){
navigation.navigate('Details', {note : addNoteData});
console.log(addNoteData);
}
}, [addNoteData])

if(isLoading || searchNotesLoading){
return(
<View style={tw`flex-1 justify-center items-center`}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}

if(error){
return(
<View style={tw`flex-1 justify-center items-center`}>
<Text style={tw`text-white`}>Failed to load notes</Text>
</View>
);
}

return(
<View style={tw`w-full h-screen flex-1 bg-[#1c004f]`}>
<View style={tw`items-center pb-5`}>
<Text style={tw`justify-center text-white font-bold pb-3 pt-5`}>Search/Quick add</Text>
<TextInput style={tw`w-4/5 h-6 bg-[#2F0082] text-white`} placeholder='Type here...' onChangeText={(text) => {setText(text)}}></TextInput>
</View>
<MasonryList
style={tw`w-full`}
data = {filteredData}
keyExtractor = {(item) => item.id}
renderItem = {({item}) => <Note item={item} nav={navigation}/>}
numColumns = {2}
contentContainerStyle={tw`p-4`}
/>
<TouchableOpacity style={tw`items-center justify-center pb-3`} onPress={async () => {await addNote({title : " ", content: ""+text});}}>
<Image
source = {require('./assets/add.png')}
style={tw`w-15 h-15`}
/>
</TouchableOpacity>
</View>
);
}

// DetailsScreen component handles the display, editing, and deletion of a single note
function DetailsScreen({route, navigation}){
const [updateNote] = useUpdateNoteMutation();
const {note} = route.params;
const [title, setTitle] = useState(note.title);
const [text, setText] = useState(note.content);
const [deleteNote] = useDeleteNoteMutation();
noteDelete = false;
const deleteThisNote = () => {
noteDelete = true;
deleteNote(note);
navigation.popToTop();
}
React.useEffect(() => {
navigation.setOptions({
headerRight: () =>
<TouchableOpacity style={tw`items-center justify-center pb-3`} onPress={deleteThisNote}>
<Image
source = {require('./assets/delete.png')}
style={tw`w-10 h-10`}
/>
</TouchableOpacity>
});
const unsubscribe = navigation.addListener('beforeRemove', (e) => {
// Prevent default behavior of leaving the screen
e.preventDefault();
// Save the note before leaving
if(!noteDelete)
{saveNote();}
// Manually trigger the default behavior
navigation.dispatch(e.data.action);
});

return unsubscribe;
}, [navigation, title, text]);

saveNote = () => {
updateNote({id : note.id, content : text, title : title});
//navigation.popToTop();
}

return(
<ScrollView style={tw`flex-1 bg-[#2F0082] p-2 h-screen`} automaticallyAdjustKeyboardInsets={true}>
<TextInput style={tw`text-white bg-[#3d00a9] w-full text-6 mb-5`} multiline={true} onChangeText={text => setTitle(text)} placeholder='Type here'>{note.title}</TextInput>
<TextInput style={tw`text-white bg-[#3d00a9] w-full`} multiline={true} onChangeText={text => setText(text)} placeholder='Type here'>{note.content}</TextInput>
<TouchableOpacity style={tw`items-center pt-5`} onPress={navigation.popToTop}>
<Text style={tw`font-bold text-white`}>DONE</Text>
</TouchableOpacity>
</ScrollView>
);
}

function App() {
useDeviceContext(tw);

return (
<Provider store={store}>
<SafeAreaView>
<Text style={tw`w-screen mt-16 text-center text-xl`}>
Your app code goes here.
</Text>
<SafeAreaView style={tw`w-full h-screen`}>
<NavigationContainer>
<Stack.Navigator screenOptions={{headerStyle: {backgroundColor : '#120033'}, headerTitleStyle: {color: 'white'} }} initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={{headerShadowVisible:false}}/>
<Stack.Screen name="Details" component={DetailsScreen} options={{ headerRight: () =>
<TouchableOpacity style={tw`items-center justify-center pb-3`}>
<Image
source = {require('./assets/delete.png')}
style={tw`w-10 h-10`}
/>
</TouchableOpacity>, headerShadowVisible:false, headerTintColor: '#ffffff'}}/>
</Stack.Navigator>
</NavigationContainer>
</SafeAreaView>
</Provider>
)
);
}

export default App;
export default App;
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# SKNotesStarter
# Notes Management App

This is a simple Notes Management app built with React Native. It allows users to add, search, update, and delete notes. The app uses Redux Toolkit Query for data fetching and state management, and AsyncStorage for local data persistence.

## Features

- Add new notes
- Search notes by content
- Update existing notes
- Delete notes
- Delete all notes

## Technologies Used

- React Native
- Redux Toolkit Query
- AsyncStorage
- UUID

## Installation

1. Clone the repository:
```bash
git clone https://github.com/PerfectOctogon/NotesJS.git

View the [ShiftKey Labs Notion](https://shiftkeylabs.notion.site/Project-Install-Instructions-f937641104bc42e098fcfefcf7349608) for detailed installation instructions.
8 changes: 7 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"package": "com.perfectoctogon.SKNotesStarter"
},
"web": {
"favicon": "./assets/favicon.png"
},
"extra": {
"eas": {
"projectId": "8572402e-b966-48a4-a7d8-7c79fd4323f2"
}
}
}
}
Binary file added assets/add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"cli": {
"version": ">= 9.1.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {}
}
}