minor improvement

This commit is contained in:
Djalim Simaila 2023-04-10 01:30:59 +02:00
parent d1d749290a
commit b713911e6b
11 changed files with 316 additions and 228 deletions

2
.gitignore vendored
View File

@ -36,7 +36,7 @@ local.properties
# vscode # vscode
.vscode .vscode
.history
# node.js # node.js
# #
node_modules/ node_modules/

View File

@ -10,15 +10,23 @@
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@react-native-async-storage/async-storage": "^1.18.1",
"@react-navigation/material-bottom-tabs": "^6.2.15",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@reduxjs/toolkit": "^1.9.3",
"axios": "^1.3.4", "axios": "^1.3.4",
"expo": "^48.0.9", "expo": "^48.0.9",
"expo-cli": "^6.3.2", "expo-cli": "^6.3.2",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"react": "18.2.0", "react": "18.2.0",
"react-native": "0.71.4", "react-native": "0.71.4",
"react-native-async-storage": "^0.0.1",
"react-native-paper": "^5.6.0",
"react-native-simple-toast": "^2.0.0", "react-native-simple-toast": "^2.0.0",
"react-native-vector-icons": "^9.2.0" "react-native-vector-icons": "^9.2.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"redux-persist": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
@ -28,6 +36,7 @@
"@tsconfig/react-native": "^2.0.2", "@tsconfig/react-native": "^2.0.2",
"@types/jest": "^29.2.1", "@types/jest": "^29.2.1",
"@types/react": "^18.0.24", "@types/react": "^18.0.24",
"@types/react-native": "^0.71.5",
"@types/react-native-vector-icons": "^6.4.13", "@types/react-native-vector-icons": "^6.4.13",
"@types/react-test-renderer": "^18.0.0", "@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1", "babel-jest": "^29.2.1",

View File

@ -27,54 +27,74 @@ import { Provider } from 'react-redux';
import {store, persistor} from './redux/store'; import {store, persistor} from './redux/store';
import {PersistGate} from 'redux-persist/integration/react'; import {PersistGate} from 'redux-persist/integration/react';
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
const Tab = createMaterialBottomTabNavigator(); const Tab = createMaterialBottomTabNavigator();
class App extends React.Component<any, any> { class App extends React.Component<any, any> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
token: "", token: '',
username: "" username: '',
} };
store.subscribe(() => { store.subscribe(() => {
this.setState({token: store.getState().persistedUserReducer.token}); this.setState({token: store.getState().persistedUserReducer.token});
}); });
} }
render(): React.ReactNode { render(): React.ReactNode {
console.log("render app", store.getState()); console.log('render app', store.getState());
console.log("app state", this.state); console.log('app state', this.state);
return return (
<Provider store={store}> <Provider store={store}>
<PersistGate loading={null} persistor={persistor}></PersistGate> <PersistGate loading={null} persistor={persistor} />
<NavigationContainer> <NavigationContainer>
<Tab.Navigator>{ <Tab.Navigator>
this.state.token === "" {
? this.state.token === '' ? (
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
<> <>
<Tab.Screen children={() => <SignIn store={store} />} name="Login" options={{ title: 'Connexion' }} /> <Tab.Screen
<Tab.Screen children={() => <SignUp store={store} />} name="Register" options={{ title: 'Créer un compte' }} /> children={() => <SignIn store={store} />}
name="Login"
options={{title: 'Connexion'}}
/>
<Tab.Screen
children={() => <SignUp store={store} />}
name="Register"
options={{title: 'Créer un compte'}}
/>
</> </>
: ) : (
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
<> <>
<Tab.Screen name="Local" options={{title: 'local'}}> <Tab.Screen name="Local" options={{title: 'local'}}>
{(props) => <ClipViewLocal store={store} navigation={props.navigation} type={"local"} />} {props => (
<ClipViewLocal
store={store}
navigation={props.navigation}
type={'local'}
/>
)}
</Tab.Screen> </Tab.Screen>
<Tab.Screen name="Remote" options={{title: 'distant'}}> <Tab.Screen name="Remote" options={{title: 'distant'}}>
{(props) => <ClipViewRemote store={store} navigation={props.navigation} type={"remote"} />} {props => (
<ClipViewRemote
store={store}
navigation={props.navigation}
type={'remote'}
/>
)}
</Tab.Screen> </Tab.Screen>
</> </>
)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
}</Tab.Navigator> }
</Tab.Navigator>
</NavigationContainer> </NavigationContainer>
</Provider> </Provider>
}; );
}
} }
export default App; export default App;

View File

@ -3,15 +3,13 @@ import { View, Text, Button, TextInput } from 'react-native';
import {accessibilityProps} from 'react-native-paper/lib/typescript/src/components/MaterialCommunityIcon'; import {accessibilityProps} from 'react-native-paper/lib/typescript/src/components/MaterialCommunityIcon';
import {setUser} from '../redux/actions'; import {setUser} from '../redux/actions';
export default class SignUp extends React.Component<any, any> { export default class SignUp extends React.Component<any, any> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
username: '', username: '',
password: '' password: '',
} };
this.signUpFunction = this.signUpFunction.bind(this); this.signUpFunction = this.signUpFunction.bind(this);
this.updateUsername = this.updateUsername.bind(this); this.updateUsername = this.updateUsername.bind(this);
this.updatePassword = this.updatePassword.bind(this); this.updatePassword = this.updatePassword.bind(this);
@ -19,29 +17,33 @@ export default class SignUp extends React.Component<any, any> {
async signUpFunction() { async signUpFunction() {
const data = new FormData(); const data = new FormData();
data.append("username", this.state.username); data.append('username', this.state.username);
data.append("password", this.state.password); data.append('password', this.state.password);
const signUpResponse = await fetch( const signUpResponse = await fetch(
'https://notifysync.simailadjalim.fr/user', 'https://notifysync.simailadjalim.fr/user',
{ {
method: 'PUT', method: 'PUT',
body: data body: data,
} },
); );
const signUpJson = await signUpResponse.json(); const signUpJson = await signUpResponse.json();
if (signUpJson.status === "ok") { if (signUpJson.status === 'ok') {
const signInResponse = await fetch( const signInResponse = await fetch(
'https://notifysync.simailadjalim.fr/user', 'https://notifysync.simailadjalim.fr/user',
{ {
method: 'POST', method: 'POST',
body: data body: data,
} },
); );
const signInJson = await signInResponse.json(); const signInJson = await signInResponse.json();
if (signInJson.status === "ok") { if (signInJson.status === 'ok') {
this.props.store.dispatch(setUser(signInJson.token)); this.props.store.dispatch(setUser(signInJson.token));
} else console.log(signInJson); } else {
} else console.log(signUpJson); console.log(signInJson);
}
} else {
console.log(signUpJson);
}
} }
updateUsername(username: string) { updateUsername(username: string) {
@ -55,9 +57,19 @@ export default class SignUp extends React.Component<any, any> {
render(): React.ReactNode { render(): React.ReactNode {
return ( return (
<View> <View>
<Text style={{ fontWeight: 'bold', fontSize: 30, margin: 20 }}>Créer un compte</Text> <Text style={{fontWeight: 'bold', fontSize: 30, margin: 20}}>
<TextInput placeholder="Nom d'utilisateur" value={this.state.username} onChangeText={this.updateUsername} /> Créer un compte
<TextInput placeholder="Mot de Passe" value={this.state.password} onChangeText={this.updatePassword} /> </Text>
<TextInput
placeholder="Nom d'utilisateur"
value={this.state.username}
onChangeText={this.updateUsername}
/>
<TextInput
placeholder="Mot de Passe"
value={this.state.password}
onChangeText={this.updatePassword}
/>
<Button title="S'enregistrer" onPress={this.signUpFunction} /> <Button title="S'enregistrer" onPress={this.signUpFunction} />
</View> </View>
); );

View File

@ -1,34 +1,46 @@
import {View, Text, Button} from 'react-native'; import {View, Text, Button} from 'react-native';
import IconVector from 'react-native-vector-icons/FontAwesome5'; import IconVector from 'react-native-vector-icons/FontAwesome5';
import AClipElement from './AClipElement'; import AClipElement from './AClipElement';
import Toast from 'react-native-simple-toast';
export default class ClipElementLocal extends AClipElement { export default class ClipElementLocal extends AClipElement {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
} }
async sendToRemote() { async sendToRemote() {
const data = new FormData(); const data = new FormData();
data.append("token", this.props.store.getState().persistedUserReducer.token); data.append(
data.append("content", this.props.content); 'token',
data.append("deviceName", "TODOChangeThisMobileDevice"); this.props.store.getState().persistedUserReducer.token,
);
data.append('content', this.props.content);
data.append('deviceName', 'TODOChangeThisMobileDevice');
const sendToRemoteResponse = await fetch( const sendToRemoteResponse = await fetch(
'https://notifysync.simailadjalim.fr/clipboard', 'https://notifysync.simailadjalim.fr/clipboard',
{ {
method: 'PUT', method: 'PUT',
body: data body: data,
} },
); );
const response = await sendToRemoteResponse.json(); const response = await sendToRemoteResponse.json();
console.log(response); Toast.show(this.props.content + 'was sent to the server', Toast.SHORT);
} }
render(): JSX.Element { render(): JSX.Element {
return <View style={{ flex: 1, margin: 10, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}> return (
<Text style={{ fontSize: 20, }}>{this.props.content}</Text> <View
<Button title="Send to remote" onPress={() => this.sendToRemote()} /> style={{
flex: 1,
margin: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Text style={{fontSize: 20}}>{this.props.content.length >28 ?this.props.content.slice(0,24)+"...":this.props.content }</Text>
<IconVector name="sendTo" size={40} onPress={() => this.sendToRemote()} />
<IconVector name="clipboard" size={40} onPress={() => this.onCopy()} /> <IconVector name="clipboard" size={40} onPress={() => this.onCopy()} />
</View>; </View>
);
} }
} }

View File

@ -9,11 +9,13 @@ export default class ClipElementRemote extends AClipElement {
} }
render(): JSX.Element { render(): JSX.Element {
const date= new Date(this.props.timestamp*1000);
console.log(this.props.timestamp*1000);
return <View style={{flex:1,margin:10,flexDirection:'row',justifyContent:'space-between',alignItems:'center'}}> return <View style={{flex:1,margin:10,flexDirection:'row',justifyContent:'space-between',alignItems:'center'}}>
<View style={{flex:1,margin:10,flexDirection:'column'}}> <View style={{flex:1,margin:10,flexDirection:'column'}}>
<Text style={{fontSize:20,}}>{this.props.content}</Text> <Text style={{fontSize:20,}}>{this.props.content.length >28 ?this.props.content.slice(0,24)+"...":this.props.content}</Text>
<Text style={{fontSize:10,}}>{this.props.deviceName}</Text> <Text style={{fontSize:10,}}>{this.props.deviceName}</Text>
<Text style={{fontSize:10,}}>{this.props.timestamp}</Text> <Text style={{fontSize:10,}}>{date.getHours() + ":" + date.getMinutes() + ", "+ date.toDateString()}</Text>
</View> </View>
<IconVector name="clipboard" size={40} onPress={() => this.onCopy()} /> <IconVector name="clipboard" size={40} onPress={() => this.onCopy()} />
</View>; </View>;

View File

@ -4,7 +4,6 @@ import ClipElementLocal from './ClipElementLocal';
import ClipElementRemote from './ClipElementRemote'; import ClipElementRemote from './ClipElementRemote';
export default class ClipList extends React.Component<any, any> { export default class ClipList extends React.Component<any, any> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
} }
@ -13,14 +12,36 @@ export default class ClipList extends React.Component<any, any> {
return <ClipElementLocal store={this.props.store} content={content} />; return <ClipElementLocal store={this.props.store} content={content} />;
} }
createClipElementRemote(content: string, deviceName: string, timestamp: number): JSX.Element { createClipElementRemote(
return <ClipElementRemote store={this.props.store} content={content} deviceName={deviceName} timestamp={timestamp} />; content: string,
deviceName: string,
timestamp: number,
): JSX.Element {
return (
<ClipElementRemote
store={this.props.store}
content={content}
deviceName={deviceName}
timestamp={timestamp}
/>
);
} }
render(): JSX.Element { render(): JSX.Element {
let clips; let clips;
if (this.props.type === "local") clips = this.props.clips.map((entry: any) => this.createClipElementLocal(entry.content)); if (this.props.type === 'local') {
else clips = this.props.clips.map((entry: any) => this.createClipElementRemote(entry.content, entry.deviceName, entry.timestamp)); clips = this.props.clips.map((entry: any) =>
this.createClipElementLocal(entry.content),
);
} else {
clips = this.props.clips.map((entry: any) =>
this.createClipElementRemote(
entry.content,
entry.deviceName,
entry.timestamp,
),
);
}
return <ScrollView>{clips}</ScrollView>; return <ScrollView>{clips}</ScrollView>;
} }
} }

View File

@ -5,44 +5,56 @@ import ClipList from './ClipList';
import AClipView from './AClipView'; import AClipView from './AClipView';
import {addToLocal as addToLocalStorage} from '../redux/actions'; import {addToLocal as addToLocalStorage} from '../redux/actions';
export default class ClipViewLocal extends AClipView { export default class ClipViewLocal extends AClipView {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
localClips : [] clips: [],
}; };
this.getClips(); this.getClips();
this.props.store.subscribe(() => { this.props.store.subscribe(() => {
this.setState({localClips : this.props.store.getState().persistedUserReducer.clips }); this.setState({
clips: this.props.store.getState().persistedUserReducer.clips,
});
}); });
} }
getClips() { getClips() {
const clips = this.props.store.getState().persistedUserReducer.clips; const clips = this.props.store.getState().persistedUserReducer.clips;
this.setState({localClips: clips}); this.setState({clips: clips});
} }
async addToLocal() { async addToLocal() {
console.log("should add clip"); console.log('should add clip');
this.props.store.dispatch(addToLocalStorage(await Clipboard.getString())); this.props.store.dispatch(addToLocalStorage(await Clipboard.getString()));
} }
componentDidMount() { componentDidMount() {
let clips; this.getClips();
clips = this.getClips();
this.setState({clips: clips});
} }
render(): JSX.Element { render(): JSX.Element {
let title = "Local Clipboard"; let title = 'Local Clipboard';
let notTitle = "Remote Clipboard"; let notTitle = 'Remote Clipboard';
return <ScrollView> return (
<Text style={{ fontWeight: 'bold', fontSize: 30, margin: 20 }}>{title}</Text> <ScrollView>
<Button title="Coller depuis le presse papier" onPress={() => {this.addToLocal}} /> <Text style={{fontWeight: 'bold', fontSize: 30, margin: 20}}>
<ClipList store={this.props.store} type={this.props.type} clips={this.state.clips} /> {title}
</Text>
<Button
title="Coller depuis le presse papier"
onPress={() => {
this.addToLocal();
}}
/>
<ClipList
store={this.props.store}
type={this.props.type}
clips={this.state.clips}
/>
{this.getSignOutBtn()} {this.getSignOutBtn()}
</ScrollView>; </ScrollView>
);
} }
} }

View File

@ -1,19 +1,19 @@
export function setUser(token: string) { export function setUser(token: string) {
return { return {
type: "auth/connect", type: 'auth/connect',
payload: { token } payload: {token},
}; };
} }
export function clearUser() { export function clearUser() {
return { return {
type: "auth/disconnect" type: 'auth/disconnect',
}; };
} }
export function addToLocal(content: string) { export function addToLocal(content: string) {
return { return {
type: "local/add", type: 'local/add',
payload: {content} payload: {content},
}; };
} }

View File

@ -1,11 +1,11 @@
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistReducer } from 'redux-persist' import {persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage' import storage from 'redux-persist/lib/storage';
import { userReducer } from "./reducers"; import {userReducer} from './reducers';
const persistConfig = { const persistConfig = {
key: 'root', key: 'root',
storage: AsyncStorage, storage: AsyncStorage,
} };
export const persistedUserReducer = persistReducer(persistConfig, userReducer); export const persistedUserReducer = persistReducer(persistConfig, userReducer);

View File

@ -1,12 +1,12 @@
import { createStore, combineReducers } from "redux"; import {createStore, combineReducers} from 'redux';
import { userReducer } from "./reducers"; import {userReducer} from './reducers';
import { persistedUserReducer } from "./persistance" import {persistedUserReducer} from './persistance';
import { persistStore } from "redux-persist"; import {persistStore} from 'redux-persist';
export const store = createStore( export const store = createStore(
combineReducers({ combineReducers({
persistedUserReducer persistedUserReducer,
}) }),
); );
export const persistor = persistStore(store); export const persistor = persistStore(store);