diff --git a/.gitignore b/.gitignore index c131846..e881539 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ ios/.xcode.env.local # Android/IntelliJ # +android/ build/ .idea .gradle diff --git a/App.tsx b/App.tsx index a6c4a21..c315dbf 100644 --- a/App.tsx +++ b/App.tsx @@ -1,11 +1,14 @@ import 'react-native-gesture-handler'; // ^ le bouge pas ca casse tout ^ +import { AppRegistry, Platform } from 'react-native' + + import { useState } from "react"; import { NavigationContainer, DefaultTheme } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { Provider } from 'react-redux'; -import { store } from './src/redux/store'; -import { MD3DarkTheme, MD3LightTheme, PaperProvider, Portal } from 'react-native-paper'; +import { store,persistor } from './src/redux/store'; +import { PaperProvider } from 'react-native-paper'; import { ToastProvider } from 'react-native-toast-notifications' import AuthPage from './src/pages/Auth'; import ClipPage from './src/pages/Clips'; @@ -15,11 +18,11 @@ import { Material3Dracula, ReactNavigationDracula } from './src/themes'; import { PixelRatio, useWindowDimensions } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; const Stack = createNativeStackNavigator(); - +import { PersistGate } from 'redux-persist/integration/react'; function App(){ - const [token,setToken] = useState(""); + const [token,setToken] = useState(store.getState().user.token); const [username,setUsername] = useState(""); const {height,width} = useWindowDimensions(); @@ -30,11 +33,12 @@ function App(){ let newToken = store.getState().user.token; setToken(newToken); }) - + return ( + @@ -54,6 +58,7 @@ function App(){ + diff --git a/app.json b/app.json index c10b0dd..0fd01a3 100644 --- a/app.json +++ b/app.json @@ -21,7 +21,8 @@ "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" - } + }, + "package": "fr.simailadjalim.ClipSync" }, "web": { "favicon": "./assets/favicon.png" diff --git a/bun.lockb b/bun.lockb index c94b7ab..37b0f50 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 1d6bcc4..10798e9 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", + "android": "expo run:android", + "ios": "expo run:ios", "web": "expo start --web" }, "dependencies": { @@ -22,9 +22,11 @@ "expo-status-bar": "~1.11.1", "react": "18.2.0", "react-native": "0.73.6", + "react-native-android-notification-listener": "^5.0.1", "react-native-async-storage": "^0.0.1", "react-native-drawer-layout": "^3.3.0", "react-native-gesture-handler": "~2.14.0", + "react-native-pager-view": "^6.3.0", "react-native-paper": "^5.12.3", "react-native-reanimated": "~3.6.2", "react-native-safe-area-context": "^4.9.0", @@ -38,6 +40,7 @@ "react-redux": "^9.1.0", "redux": "^5.0.1", "redux-persist": "^6.0.0", + "redux-persist-expo-filesystem": "^2.0.1", "redux-thunk": "^3.1.0" }, "devDependencies": { diff --git a/src/components/clip/ClipElementLocal.tsx b/src/components/clip/ClipElementLocal.tsx index a9fe410..b16af74 100644 --- a/src/components/clip/ClipElementLocal.tsx +++ b/src/components/clip/ClipElementLocal.tsx @@ -8,7 +8,7 @@ import { store } from '../../redux/store'; import { Button } from 'react-native-paper'; export default function ClipElementLocal({content}:{content: string}){ - + const toast = useToast(); function onCopy() { Clipboard.setStringAsync(content) diff --git a/src/components/clip/ClipList.tsx b/src/components/clip/ClipList.tsx index 0af4df6..a687630 100644 --- a/src/components/clip/ClipList.tsx +++ b/src/components/clip/ClipList.tsx @@ -1,46 +1,31 @@ import React from 'react'; -import {ScrollView} from 'react-native'; +import {FlatList, ScrollView} from 'react-native'; import ClipElementLocal from './ClipElementLocal'; import ClipElementRemote from './ClipElementRemote'; +import { nanoid } from '@reduxjs/toolkit'; export default class ClipList extends React.Component { constructor(props: any) { super(props); } - createClipElementLocal(content: string): JSX.Element { - return ; - } - - createClipElementRemote( - content: string, - deviceName: string, - timestamp: number, - ): JSX.Element { - return ( - - ); - } - render(): JSX.Element { - let clips; if (this.props.type === 'local') { - clips = this.props.clips.map((entry: any) => - this.createClipElementLocal(entry.content), - ); + return } + keyExtractor={item => nanoid()} + inverted={true} + /> } else { - clips = this.props.clips.map((entry: any) => - this.createClipElementRemote( - entry.content, - entry.device_name, - entry.timestamp, - ), - ); + return } + keyExtractor={item => nanoid()} + inverted={true} + /> + ; } - return {clips}; + } } diff --git a/src/components/clip/ClipView.tsx b/src/components/clip/ClipView.tsx index 4074683..66768bc 100644 --- a/src/components/clip/ClipView.tsx +++ b/src/components/clip/ClipView.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { useWindowDimensions, ScrollView } from "react-native"; -import { Searchbar } from "react-native-paper" +import { useWindowDimensions, ScrollView, View } from "react-native"; +import { Searchbar, Button } from "react-native-paper" import ClipViewLocal from "./ClipViewLocal"; import ClipViewRemote from "./ClipViewRemote"; import { ps } from "../../utils"; @@ -18,12 +18,19 @@ export default function ClipView(){ else if (width < 1200 ) layout = "medium"; else layout = "expanded"; return ( <> - - + {layout == "compact" ? + - } - async function addToLocal(dispatch) { Clipboard.getStringAsync() .then(value =>{ let newClip = {"content":value}; console.log(value); - dispatch(localAddToList(newClip)); + dispatch(localClipAddToList(newClip)); setClips([...clips,newClip]); toast.show('copied "'+value+'" into history'); }) diff --git a/src/components/clip/ClipViewRemote.tsx b/src/components/clip/ClipViewRemote.tsx index 809b9d0..61357fb 100644 --- a/src/components/clip/ClipViewRemote.tsx +++ b/src/components/clip/ClipViewRemote.tsx @@ -6,18 +6,22 @@ import { store } from '../../redux/store'; import { useToast } from 'react-native-toast-notifications'; import { Button } from 'react-native-paper'; import { ps } from '../../utils'; +import { useDispatch } from 'react-redux'; +import { remoteClipAddToList } from '../../redux/reducers'; export default function ClipViewRemote(){ - const [clips,setClips] = React.useState([]); + const [clips,setClips] = React.useState(store.getState().remoteClip.remoteClip); const toast = useToast(); - + const dispatch = useDispatch(); + const {height,width} = useWindowDimensions(); - function getClips() { + async function getClips(dispatch) { axios.get("http://notifysync.simailadjalim.fr/clipboard?token="+store.getState().user.token) .then((response,status) => { - console.log(response); - setClips(Object.values(response["data"]['clipboard'])); + let remoteclips = Object.values(response["data"]['clipboard']); + setClips(remoteclips); + dispatch(remoteClipAddToList(remoteclips)) toast.show("fetched latest clips from remote"); }) .catch(response =>{ @@ -37,7 +41,7 @@ export default function ClipViewRemote(){ flex:1, margin: width > 600 ? ps(10) : 0 }}> - diff --git a/src/pages/Clips.tsx b/src/pages/Clips.tsx index 3a89ace..ba4b441 100644 --- a/src/pages/Clips.tsx +++ b/src/pages/Clips.tsx @@ -1,15 +1,14 @@ import React from "react"; import { Drawer as DrawerLayout } from 'react-native-drawer-layout'; -import { Button, Drawer, Searchbar } from 'react-native-paper'; -import { Platform, View,StyleSheet, useWindowDimensions, ScrollView } from "react-native"; -import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; -import { Appbar, Modal, Text, Portal } from 'react-native-paper'; +import { Button, Drawer} from 'react-native-paper'; +import { Platform, View,StyleSheet, useWindowDimensions } from "react-native"; +import { Modal, Text, Portal } from 'react-native-paper'; import { useDispatch } from 'react-redux'; -import { disconnect } from "../redux/reducers"; -import { ps } from "../utils"; +import { disconnect, localClipClear, localNotifClear, remoteClipClear, remoteNotifClear } from "../redux/reducers"; import { ReactNavigationDracula as t } from "../themes"; import ClipView from "../components/clip/ClipView"; import Settings from "./Settings"; +import NotifView from "../components/notif/NotifView"; const styles = StyleSheet.create({ modal: { @@ -31,6 +30,15 @@ export default function ClipPage(){ const containerStyle = {backgroundColor: 'white', padding: 20}; const {height, width} = useWindowDimensions(); + + function signout(){ + dispatch(localClipClear()) + dispatch(remoteClipClear()) + dispatch(localNotifClear()) + dispatch(remoteNotifClear()) + dispatch(disconnect()) + } + let layout = "" if (width < 600) layout = "compact"; @@ -56,7 +64,7 @@ export default function ClipPage(){ let page; if (active == "clips") page = ; - if (active == "notif") page = ; + if (active == "notif") page = ; if (active == "settings") page = ; return ( no - @@ -99,6 +107,15 @@ export default function ClipPage(){ setOpen(false) }} /> + { + setActive('notif'); + setOpen(false) + }} + /> }else{ return - + + { + setActive('notif'); + setOpen(false) + }} + /> { + localClipAddToList: (state,action) => { state.localClip = [...state.localClip,action.payload] }, - localRemoveFromList: (state,action) => { + localClipRemoveFromList: (state,action) => { state.localClip = state.localClip.filter(e => e !== action.payload) + }, + localClipClear : (state,action) =>{ + state.localClip = [] } } }) -export const {localAddToList, localRemoveFromList} = localClipsSlice.actions; +export const {localClipAddToList, localClipRemoveFromList, localClipClear} = localClipsSlice.actions; // Remote clip structure // [ @@ -56,17 +59,59 @@ export const remoteClipsSlice = createSlice({ remoteClip:[], }, reducers: { - remoteAddToList: (state,) => { + remoteClipAddToList: (state,action) => { + console.log(action.payload); + state.remoteClip = action.payload + }, + remoteClipRemoveFromList: (state,action) => { + state.remoteClip = state.remoteClip.filter(e => e !== action.payload); + }, + remoteClipClear : (state,action) =>{ state.remoteClip = [] }, - remoteRemoveFromList: state => { - state.remoteClip = [] + + } +}) + +export const {remoteClipAddToList, remoteClipRemoveFromList, remoteClipClear} = remoteClipsSlice.actions; + + +export const localNotifsSlice = createSlice({ + name: 'localNotifs', + initialState: { + localNotif:[], + }, + reducers: { + localNotifAddToList: (state,action) => { + state.localNotif = [...state.localNotif,action.payload] + }, + localNotifRemoveFromList: (state,action) => { + state.localNotif = state.localNotif.filter(e => e !== action.payload) + }, + localNotifClear : (state,action) =>{ + state.localNotif = [] } } }) -export const {remoteAddToList, remoteRemoveFromList} = remoteClipsSlice.actions; - - +export const {localNotifAddToList, localNotifRemoveFromList, localNotifClear} = localNotifsSlice.actions; +export const remoteNotifsSlice = createSlice({ + name: 'remoteNotifs', + initialState: { + remoteNotif:[], + }, + reducers: { + remoteNotifAddToList: (state,action) => { + state.remoteNotif = [...action.payload] + }, + remoteNotifRemoveFromList: (state,action) => { + state.remoteNotif = state.remoteNotif.filter(e => e !== action.payload); + }, + remoteNotifClear : (state,action) =>{ + state.remoteNotif = [] + } + } +}) +export const {remoteNotifAddToList, remoteNotifRemoveFromList, remoteNotifClear} = remoteNotifsSlice.actions; diff --git a/src/redux/store.tsx b/src/redux/store.tsx index 7fd1eee..2a95445 100644 --- a/src/redux/store.tsx +++ b/src/redux/store.tsx @@ -1,17 +1,29 @@ import { applyMiddleware,configureStore } from '@reduxjs/toolkit' import { combineReducers } from '@reduxjs/toolkit'; import { userSlice, localClipsSlice ,remoteClipsSlice } from './reducers'; -import thunkMiddleware from 'redux-thunk' +import { localNotifsSlice,remoteNotifsSlice } from './reducers'; +import { persistStore, persistReducer } from 'redux-persist' +import AsyncStorage from '@react-native-async-storage/async-storage'; +const persistConfig = { + key: "root", + storage: AsyncStorage, +}; const rootReducer = combineReducers({ user:userSlice.reducer, - local:localClipsSlice.reducer, - remote:remoteClipsSlice.reducer + localClip:localClipsSlice.reducer, + remoteClip:remoteClipsSlice.reducer, + localNotif:localNotifsSlice.reducer, + remoteNotif:remoteNotifsSlice.reducer }); +const persistedReducer = persistReducer(persistConfig, rootReducer) + const store = configureStore({ - reducer: rootReducer + reducer: persistedReducer }) -export {store}; +const persistor = persistStore(store) + +export {store, persistor};