lint source + fix error on app closing tag

This commit is contained in:
Jérémy Munsch 2023-08-22 10:32:40 +02:00
parent f14f37ab0f
commit 8d3a99fde4
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
63 changed files with 8329 additions and 6815 deletions

21
.editorconfig Normal file
View file

@ -0,0 +1,21 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.{vue,js}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

3
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

9
.idea/com-app.iml generated Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/com-app.iml" filepath="$PROJECT_DIR$/.idea/com-app.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,7 +1,7 @@
module.exports = {
arrowParens: 'avoid',
bracketSameLine: true,
bracketSpacing: false,
bracketSpacing: true,
singleQuote: true,
trailingComma: 'all',
};

View file

@ -14436,8 +14436,6 @@
},
"node_modules/npm/node_modules/debug/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"inBundle": true,
"license": "MIT"
},
@ -15237,8 +15235,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"inBundle": true,
"license": "ISC",
"dependencies": {
@ -15741,8 +15737,6 @@
},
"node_modules/npm/node_modules/rimraf/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"inBundle": true,
"license": "ISC",
"dependencies": {
@ -15818,8 +15812,6 @@
},
"node_modules/npm/node_modules/semver/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"inBundle": true,
"license": "ISC",
"dependencies": {
@ -31283,8 +31275,6 @@
"dependencies": {
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"bundled": true
}
}
@ -31852,8 +31842,6 @@
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"bundled": true,
"requires": {
"fs.realpath": "^1.0.0",
@ -32195,8 +32183,6 @@
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"bundled": true,
"requires": {
"fs.realpath": "^1.0.0",
@ -32240,8 +32226,6 @@
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"bundled": true,
"requires": {
"yallist": "^4.0.0"

View file

@ -1,49 +1,58 @@
import React from 'react';
// Sentry
import * as Sentry from "@sentry/react-native";
import * as Sentry from '@sentry/react-native';
// Redux
import { Provider } from 'react-redux';
import { store } from './App/Store/ConfigureStore';
import { initDocuments } from './App/Store/Actions/documents';
import { store } from './App/Store/ConfigureStore';
// Deps
import 'react-native-gesture-handler';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { navigationRef, navigate } from './Navigation';
import { TouchableOpacity, View, Image, Text, TextInput, Alert, DrawerLayoutAndroid, Button } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome5';
import FlashMessage, { showMessage } from 'react-native-flash-message';
import * as SplashScreen from 'expo-splash-screen';
import {
Alert,
Button,
DrawerLayoutAndroid,
Image,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import FlashMessage, { showMessage } from 'react-native-flash-message';
import 'react-native-gesture-handler';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { navigate, navigationRef } from './Navigation';
// Application infos
import * as Application from 'expo-application';
// i18n
import i18n from "./I18n";
import i18n from './I18n';
// FileSytem
import * as FileSystem from 'expo-file-system';
// Views
import ContactDetailsScreen from './App/Views/ContactDetailsScreen';
import ContactScreen from './App/Views/ContactScreen';
import DirectoriesScreen from './App/Views/DirectoriesScreen';
import HomeScreen from './App/Views/HomeScreen';
import InstallScreen from './App/Views/InstallScreen';
import LoginScreen from './App/Views/LoginScreen';
import SyncScreen from './App/Views/SyncScreen';
import HomeScreen from './App/Views/HomeScreen';
import DirectoriesScreen from './App/Views/DirectoriesScreen';
import PDFScreen from './App/Views/PDFScreen';
import VideoScreen from './App/Views/VideoScreen';
import OnlyVideosScreen from './App/Views/OnlyVideosScreen';
import ContactScreen from './App/Views/ContactScreen';
import ContactDetailsScreen from './App/Views/ContactDetailsScreen';
import SearchScreen from "./App/Views/SearchScreen";
import MySpaceScreen from './App/Views/MySpaceScreen';
import OnlyVideosScreen from './App/Views/OnlyVideosScreen';
import PDFScreen from './App/Views/PDFScreen';
import SearchScreen from './App/Views/SearchScreen';
import SyncScreen from './App/Views/SyncScreen';
import VideoScreen from './App/Views/VideoScreen';
// Style
import styles from './Resources/Styles/NavigationStyle';
// Controllers
import SyncController from './App/Controllers/SyncController';
Sentry.init({
dsn: "https://ab87e8d840324f41873c95b06903b0bf@o4504314935640064.ingest.sentry.io/4504441255886848",
dsn: 'https://ab87e8d840324f41873c95b06903b0bf@o4504314935640064.ingest.sentry.io/4504441255886848',
});
Sentry.setContext("app", {
boUrl: SyncController.getUrlBo()
Sentry.setContext('app', {
boUrl: SyncController.getUrlBo(),
});
SplashScreen.preventAutoHideAsync(); //Keep
@ -61,7 +70,7 @@ class App extends React.Component {
searchTxt: '',
sendEmail: '',
mainColor: '',
drawerRef: React.createRef()
drawerRef: React.createRef(),
};
componentDidMount() {
@ -69,7 +78,7 @@ class App extends React.Component {
this.setState({
isSetup: store.getState().app.isSetup,
isLogged: store.getState().app.logged,
mainColor: store.getState().app.mainColor
mainColor: store.getState().app.mainColor,
});
}
@ -77,9 +86,9 @@ class App extends React.Component {
this.setState({
isSetup: store.getState().app.isSetup,
isLogged: store.getState().app.logged,
mainColor: store.getState().app.mainColor
mainColor: store.getState().app.mainColor,
}); // to force re-rendering
console.log("state changed")
console.log('state changed');
}
navigationView = () => (
@ -89,16 +98,23 @@ class App extends React.Component {
onPress={() => {
this.state.drawerRef.current.closeDrawer();
store.dispatch({ type: 'EDIT_LOCALE' });
store.dispatch(initDocuments(store.getState().locale.locale, store.getState().directories.global))
store.dispatch(
initDocuments(
store.getState().locale.locale,
store.getState().directories.global,
),
);
}}
style={styles.drawer.btn}>
<Image source={this.renderFlag()} style={{ width: 25, height: 25 }} />
<Text style={styles.drawer.btnText}>{i18n.t('global.change_lang')}</Text>
<Text style={styles.drawer.btnText}>
{i18n.t('global.change_lang')}
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.state.drawerRef.current.closeDrawer();
navigate('SyncScreen')
navigate('SyncScreen');
}}
style={styles.drawer.btn}>
<Icon name="sync-alt" size={20} />
@ -115,7 +131,9 @@ class App extends React.Component {
</TouchableOpacity>
</View>
<View>
<Text style={{ marginBottom: 10 }}>Version : {Application.nativeApplicationVersion}</Text>
<Text style={{ marginBottom: 10 }}>
Version : {Application.nativeApplicationVersion}
</Text>
<Button
title={i18n.t('global.close')}
color={this.state.mainColor}
@ -132,202 +150,260 @@ class App extends React.Component {
<DrawerLayoutAndroid
ref={this.state.drawerRef}
drawerWidth={300}
drawerPosition={"right"}
drawerPosition={'right'}
drawerLockMode={'locked-open'}
renderNavigationView={this.navigationView}
>
<Stack.Navigator initialRouteName="HomeScreen">
{this.state.isSetup === false &&
<Stack.Screen
name="InstallScreen"
component={InstallScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('install.installation'), false),
headerRight: () => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
store.dispatch({ type: 'EDIT_LOCALE' });
}}
style={{ marginRight: 15 }}>
<Image source={this.renderFlag()} style={{ width: 25, height: 25 }} />
</TouchableOpacity>
</View>
),
})}
/>
}
{this.state.isLogged === false ? (
<Stack.Screen
name="LoginScreen"
component={LoginScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('global.login'), false),
headerRight: () => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
store.dispatch({ type: 'EDIT_LOCALE' });
}}
style={{ marginRight: 15 }}>
<Image source={this.renderFlag()} style={{ width: 25, height: 25 }} />
</TouchableOpacity>
</View>
),
})}
/>
) : (
renderNavigationView={this.navigationView}>
<Stack.Navigator initialRouteName="HomeScreen">
{this.state.isSetup === false && (
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('global.home')),
headerRight: () => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ flexDirection: 'row', marginRight: 50 }}>
<TextInput
style={{ ...styles.searchInput, borderColor: this.state.mainColor }}
placeholder={i18n.t('global.search')}
value={this.state.searchTxt}
onChangeText={(text) => this.setState({ searchTxt: text })}
/>
name="InstallScreen"
component={InstallScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(
navigation,
i18n.t('install.installation'),
false,
),
headerRight: () => (
<View
style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
style={{ ...styles.btnSearch, backgroundColor: this.state.mainColor }}
onPress={() => {
store.dispatch({ type: 'EDIT_LOCALE' });
}}
style={{ marginRight: 15 }}>
<Image
source={this.renderFlag()}
style={{ width: 25, height: 25 }}
/>
</TouchableOpacity>
</View>
),
})}
/>
)}
{this.state.isLogged === false ? (
<Stack.Screen
name="LoginScreen"
component={LoginScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('global.login'), false),
headerRight: () => (
<View
style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
store.dispatch({ type: 'EDIT_LOCALE' });
}}
style={{ marginRight: 15 }}>
<Image
source={this.renderFlag()}
style={{ width: 25, height: 25 }}
/>
</TouchableOpacity>
</View>
),
})}
/>
) : (
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('global.home')),
headerRight: () => (
<View
style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ flexDirection: 'row', marginRight: 50 }}>
<TextInput
style={{
...styles.searchInput,
borderColor: this.state.mainColor,
}}
placeholder={i18n.t('global.search')}
value={this.state.searchTxt}
onChangeText={text =>
this.setState({ searchTxt: text })
}
/>
<TouchableOpacity
style={{
...styles.btnSearch,
backgroundColor: this.state.mainColor,
}}
onPress={() => {
if (this.state.searchTxt.length > 0) {
navigation.navigation.navigate('SearchScreen', {
needle: this.state.searchTxt,
onlyVideos: false,
});
this.setState({ searchTxt: '' });
}
}}>
<Icon name={'search'} size={20} color={'white'} />
</TouchableOpacity>
</View>
{store.getState().app.ShowIsActive === '1' && (
<TouchableOpacity
onPress={() =>
navigation.navigation.navigate('MySpaceScreen')
}
style={{ marginRight: 25 }}>
<Icon name="user-circle" size={25} />
</TouchableOpacity>
)}
<TouchableOpacity
onPress={() =>
navigation.navigation.navigate('ContactScreen', {
action: 'add',
})
}
style={{ marginRight: 25 }}>
<Icon name="user-plus" size={20} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.state.drawerRef.current.openDrawer();
}}
style={{ marginRight: 25 }}>
<Icon name="sliders-h" size={20} />
</TouchableOpacity>
</View>
),
})}
/>
)}
<Stack.Screen
name="SyncScreen"
component={SyncScreen}
initialParams={{ forceSync: false }}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(
navigation,
i18n.t('global.synchronization'),
),
})}
/>
<Stack.Screen
name="DirectoriesScreen"
component={DirectoriesScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('app.folders')),
})}
/>
<Stack.Screen
name="OnlyVideosScreen"
component={OnlyVideosScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('global.videos')),
headerRight: () => (
<View style={{ flexDirection: 'row', marginRight: 5 }}>
<TextInput
style={{
...styles.searchInput,
borderColor: this.state.mainColor,
}}
placeholder={i18n.t('global.search')}
value={this.state.searchTxt}
onChangeText={text =>
this.setState({ searchTxt: text })
}
/>
<TouchableOpacity
style={{
...styles.btnSearch,
backgroundColor: this.state.mainColor,
}}
onPress={() => {
if (this.state.searchTxt.length > 0) {
navigation.navigation.navigate('SearchScreen', {
needle: this.state.searchTxt,
onlyVideos: false,
onlyVideos: true,
});
this.setState({ searchTxt: '' });
}
}}>
<Icon name={'search'} size={20} color={'white'} />
</TouchableOpacity>
</View>
{store.getState().app.ShowIsActive === '1' && (
<TouchableOpacity
onPress={() => navigation.navigation.navigate('MySpaceScreen')}
style={{ marginRight: 25 }}>
<Icon name="user-circle" size={25} />
</TouchableOpacity>
)}
<TouchableOpacity
onPress={() => navigation.navigation.navigate('ContactScreen', {
action: 'add',
})}
style={{ marginRight: 25 }}>
<Icon name="user-plus" size={20} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.state.drawerRef.current.openDrawer()
}}
style={{ marginRight: 25 }}>
<Icon name="sliders-h" size={20} />
<Icon name={'search'} size={20} color={'white'} />
</TouchableOpacity>
</View>
),
})}
/>
)}
<Stack.Screen
name="SyncScreen"
component={SyncScreen}
initialParams={{ forceSync: false }}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('global.synchronization'))
})}
/>
<Stack.Screen
name="DirectoriesScreen"
component={DirectoriesScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('app.folders'))
})}
/>
<Stack.Screen
name="OnlyVideosScreen"
component={OnlyVideosScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('global.videos')),
headerRight: () => (
<View style={{ flexDirection: 'row', marginRight: 5 }}>
<TextInput style={{ ...styles.searchInput, borderColor: this.state.mainColor }} placeholder={i18n.t('global.search')} value={this.state.searchTxt} onChangeText={(text) => this.setState({ searchTxt: text })} />
<TouchableOpacity style={{ ...styles.btnSearch, backgroundColor: this.state.mainColor }} onPress={() => {
if (this.state.searchTxt.length > 0) {
navigation.navigation.navigate('SearchScreen', {
needle: this.state.searchTxt,
onlyVideos: true,
});
this.setState({ searchTxt: '' });
}
}}>
<Icon name={'search'} size={20} color={'white'} />
</TouchableOpacity>
</View>
),
})}
/>
<Stack.Screen
name="VideoScreen"
component={VideoScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('app.video')),
headerRight: () => this.SendEmail(),
orientation: 'landscape'
})}
/>
<Stack.Screen
name="PDFScreen"
component={PDFScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, 'PDF'),
headerRight: () => this.SendEmail()
})}
/>
<Stack.Screen
name="ContactScreen"
component={ContactScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('global.contacts'))
})}
initialParams={{ contactAdded: false }}
/>
<Stack.Screen
name="ContactDetailsScreen"
component={ContactDetailsScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('app.contact_details'))
})}
/>
<Stack.Screen
name="SearchScreen"
component={SearchScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('app.results_search'))
})}
/>
<Stack.Screen
name="MySpaceScreen"
component={MySpaceScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, i18n.t('app.my_space')),
headerRight: () => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
const folder = store.getState().directories.treeview.filter(tmp => tmp.id === 1);
navigation.navigation.navigate('DirectoriesScreen', { folder: folder })
}}
style={{ marginRight: 15 }}>
<Icon name="briefcase" size={20} />
</TouchableOpacity>
</View>
),
})}
/>
</Stack.Navigator>
/>
<Stack.Screen
name="VideoScreen"
component={VideoScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('app.video')),
headerRight: () => this.SendEmail(),
orientation: 'landscape',
})}
/>
<Stack.Screen
name="PDFScreen"
component={PDFScreen}
options={(navigation, route) => ({
headerTitle: () => this.LogoTitle(navigation, 'PDF'),
headerRight: () => this.SendEmail(),
})}
/>
<Stack.Screen
name="ContactScreen"
component={ContactScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('global.contacts')),
})}
initialParams={{ contactAdded: false }}
/>
<Stack.Screen
name="ContactDetailsScreen"
component={ContactDetailsScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('app.contact_details')),
})}
/>
<Stack.Screen
name="SearchScreen"
component={SearchScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('app.results_search')),
})}
/>
<Stack.Screen
name="MySpaceScreen"
component={MySpaceScreen}
options={(navigation, route) => ({
headerTitle: () =>
this.LogoTitle(navigation, i18n.t('app.my_space')),
headerRight: () => (
<View
style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
const folder = store
.getState()
.directories.treeview.filter(tmp => tmp.id === 1);
navigation.navigation.navigate('DirectoriesScreen', {
folder: folder,
});
}}
style={{ marginRight: 15 }}>
<Icon name="briefcase" size={20} />
</TouchableOpacity>
</View>
),
})}
/>
</Stack.Navigator>
<FlashMessage position="top" />
</DrawerLayoutAndroid>
</NavigationContainer>
@ -338,20 +414,23 @@ class App extends React.Component {
LogoTitle(navigation, title, wantBtn = true) {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity onPress={() => {
if (wantBtn) {
navigation.navigation.navigate('HomeScreen')
}
}}>
<TouchableOpacity
onPress={() => {
if (wantBtn) {
navigation.navigation.navigate('HomeScreen');
}
}}>
{this.state.isSetup ? (
<Image
style={{ width: 100, height: 50, resizeMode: 'stretch' }}
source={{ uri: FileSystem.documentDirectory + 'RectangleLogo.png' }}
source={{
uri: FileSystem.documentDirectory + 'RectangleLogo.png',
}}
/>
) : (
<Image
style={{ width: 100, height: 50, resizeMode: 'stretch' }}
source={require('./Resources/assets/img/logo.png')}
source={require('./Resources/assets/img/logo.png')}
/>
)}
</TouchableOpacity>
@ -362,35 +441,47 @@ class App extends React.Component {
SendEmail() {
return (
<View style={{ flexDirection: 'row', alignItems: 'center', marginRight: 10 }}>
<TextInput style={{ ...styles.searchInput, borderColor: this.state.mainColor }} placeholder={i18n.t('global.email')} value={this.state.sendEmail} onChangeText={(text) => this.setState({ sendEmail: text })} />
<TouchableOpacity style={{ ...styles.btnSearch, backgroundColor: this.state.mainColor }} onPress={() => {
if (this.state.sendEmail.length > 0) {
if (Helpers.isEmailValid(this.state.sendEmail)) {
const email = {
email: this.state.sendEmail,
subject: i18n.t('app.file_sharing'),
message: '',
documents: JSON.stringify([store.getState().currentOpenedDoc]),
user_id: store.getState().currentUser.id
};
store.dispatch({ type: 'SEND_BROCHURE', data: email });
showMessage({
message: i18n.t('global.message_sent') + '.',
description: i18n.t('global.message_will_be_sent_after_sync') + '.',
type: 'success',
duration: 1500,
});
this.setState({ sendEmail: '' });
} else {
Alert.alert(i18n.t('global.warning') + ' !', i18n.t('error.invalid_email_address') + '.', [
{ text: 'OK' },
],
{ cancelable: false },
);
<View
style={{ flexDirection: 'row', alignItems: 'center', marginRight: 10 }}>
<TextInput
style={{ ...styles.searchInput, borderColor: this.state.mainColor }}
placeholder={i18n.t('global.email')}
value={this.state.sendEmail}
onChangeText={text => this.setState({ sendEmail: text })}
/>
<TouchableOpacity
style={{ ...styles.btnSearch, backgroundColor: this.state.mainColor }}
onPress={() => {
if (this.state.sendEmail.length > 0) {
if (Helpers.isEmailValid(this.state.sendEmail)) {
const email = {
email: this.state.sendEmail,
subject: i18n.t('app.file_sharing'),
message: '',
documents: JSON.stringify([
store.getState().currentOpenedDoc,
]),
user_id: store.getState().currentUser.id,
};
store.dispatch({ type: 'SEND_BROCHURE', data: email });
showMessage({
message: i18n.t('global.message_sent') + '.',
description:
i18n.t('global.message_will_be_sent_after_sync') + '.',
type: 'success',
duration: 1500,
});
this.setState({ sendEmail: '' });
} else {
Alert.alert(
i18n.t('global.warning') + ' !',
i18n.t('error.invalid_email_address') + '.',
[{ text: 'OK' }],
{ cancelable: false },
);
}
}
}
}}>
}}>
<Icon name={'paper-plane'} size={20} color={'white'} />
</TouchableOpacity>
</View>
@ -400,22 +491,21 @@ class App extends React.Component {
renderFlag() {
let flag;
switch (store.getState().locale.locale) {
case "fr":
case 'fr':
flag = require('./assets/img/flags/fr.png');
break;
case "en":
case 'en':
flag = require('./assets/img/flags/en.png');
break;
case "de":
case 'de':
flag = require('./assets/img/flags/de.png');
break;
case "es":
case 'es':
flag = require('./assets/img/flags/es.png');
break;
}
return flag;
}
}
export default Sentry.wrap(App);

View file

@ -1,63 +1,72 @@
import { Alert } from "react-native";
import { Alert } from 'react-native';
// Lodash
import _ from 'lodash';
// Http Client
import axios from 'axios';
// Network Infos
import NetInfo from "@react-native-community/netinfo";
import NetInfo from '@react-native-community/netinfo';
// Translations
import i18n from "../../I18n";
import i18n from '../../I18n';
// Database
import { Setting } from "../../Database";
import { Setting } from '../../Database';
const api_token = 'G9Q62jt8eL8gN4Ls';
class InstallController {
async getConfig(url) {
return axios({
method: 'GET',
url: url + '/api/settings',
responseType: 'json',
params: {
token: api_token,
},
});
}
async getConfig(url) {
return axios({
method: 'GET',
url: url + '/api/settings',
responseType: 'json',
params: {
token: api_token
},
});
}
getAppConfig(url) {
NetInfo.fetch().then(state => {
if (state.isConnected) {
axios({
method: 'GET',
url: url + '/api/app',
responseType: 'json',
params: {
token: api_token
},
})
.then(function (response) {
if (response.data) {
if (response.data.status === "OK") {
Setting.upsert({ key: 'HomeFolders', value: JSON.stringify(response.data.datas.folders) });
} else {
Alert.alert(i18n.t("alert.error"), i18n.t("error.error_has_occurred"));
return false;
}
}
})
.catch(function (error) {
console.log(error)
Alert.alert(i18n.t("alert.error"), i18n.t("error.error_has_occurred"));
return false;
})
} else {
Alert.alert(i18n.t("alert.error"), i18n.t("error.tablet_not_connected_internet"));
getAppConfig(url) {
NetInfo.fetch().then(state => {
if (state.isConnected) {
axios({
method: 'GET',
url: url + '/api/app',
responseType: 'json',
params: {
token: api_token,
},
})
.then(function (response) {
if (response.data) {
if (response.data.status === 'OK') {
Setting.upsert({
key: 'HomeFolders',
value: JSON.stringify(response.data.datas.folders),
});
} else {
Alert.alert(
i18n.t('alert.error'),
i18n.t('error.error_has_occurred'),
);
return false;
}
}
});
return true;
}
})
.catch(function (error) {
console.log(error);
Alert.alert(
i18n.t('alert.error'),
i18n.t('error.error_has_occurred'),
);
return false;
});
} else {
Alert.alert(
i18n.t('alert.error'),
i18n.t('error.tablet_not_connected_internet'),
);
return false;
}
});
return true;
}
}
export default new InstallController();
export default new InstallController();

View file

@ -1,28 +1,24 @@
// Database
import { Setting } from "../../Database";
import { Setting } from '../../Database';
// BCrypt
import { verify } from 'react-native-b-crypt-bridge';
class LoginController {
verify(user, password) {
return verify(password, user.password);
}
verify(user, password) {
return verify(
password,
user.password
);
}
login(user) {
Setting.upsert({
key: 'isLogged',
value: JSON.stringify(user),
});
Setting.upsert({
key: 'loggedTime',
value: Date.now().toString(),
});
Setting.upsert({ key: 'lastUser', value: user.id.toString() });
}
login(user) {
Setting.upsert({
key: 'isLogged',
value: JSON.stringify(user),
});
Setting.upsert({
key: 'loggedTime',
value: Date.now().toString(),
});
Setting.upsert({ key: 'lastUser', value: user.id.toString() });
}
}
export default new LoginController();
export default new LoginController();

View file

@ -1,213 +1,211 @@
// Lodash
import _ from 'lodash';
// Http Client
import axios from 'axios';
// Network Infos
import NetInfo from "@react-native-community/netinfo";
import NetInfo from '@react-native-community/netinfo';
// Application infos
import * as Application from 'expo-application';
// Database
import { Setting, Contact, SendBrochure, Document } from '../../Database';
// Database
import { Contact, Document, SendBrochure, Setting } from '../../Database';
const api_token = 'G9Q62jt8eL8gN4Ls';
class SyncController {
getUrlBo() {
const test = Setting.get('bo_url');
if (test === false || test === undefined) {
return "noset";
}
return test.value;
getUrlBo() {
const test = Setting.get('bo_url');
if (test === false || test === undefined) {
return 'noset';
}
return test.value;
}
getAppStoreUUID() {
const test = Setting.get('AppStoreUUID');
if (test === false || test === undefined) {
return "noset";
}
return test.value;
getAppStoreUUID() {
const test = Setting.get('AppStoreUUID');
if (test === false || test === undefined) {
return 'noset';
}
return test.value;
}
async checkConnection() {
return NetInfo.fetch();
async checkConnection() {
return NetInfo.fetch();
}
async getVersion() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/version',
responseType: 'json',
params: {
token: api_token,
app_uuid: this.getAppStoreUUID(),
app_version: Application.nativeApplicationVersion,
},
});
}
async getUsers() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/users',
responseType: 'json',
params: {
token: api_token,
},
});
}
async getContacts(userId) {
let contacts = Contact.getAll();
return axios({
method: 'POST',
url: this.getUrlBo() + '/api/contacts',
responseType: 'json',
params: {
token: api_token,
},
data: {
contacts: contacts,
userId: userId,
},
});
}
async sendBrochure(brochure) {
return axios({
method: 'POST',
url: this.getUrlBo() + '/api/sendBrochure',
responseType: 'json',
params: {
token: api_token,
emails: brochure,
},
});
}
async getDirectories() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/directories',
responseType: 'json',
params: {
token: api_token,
},
});
}
async getDocuments() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/documents',
responseType: 'json',
params: {
token: api_token,
},
});
}
updateLastModificationDoc(id, date) {
Document.update({ id: id, updated_at: date.toString() }, true);
}
updateVersion(version) {
const localVersion = Setting.get('version');
if (localVersion === false || localVersion === undefined) {
Setting.add({ key: 'version', value: version });
} else {
Setting.update({ id: localVersion.id, key: 'version', value: version });
}
}
async getVersion() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/version',
responseType: 'json',
params: {
token: api_token,
app_uuid: this.getAppStoreUUID(),
app_version: Application.nativeApplicationVersion
},
});
updateShowIsActive(value) {
const local = Setting.get('ShowIsActive');
if (local === false || local === undefined) {
Setting.add({ key: 'ShowIsActive', value: value });
} else {
Setting.update({ id: local.id, key: 'ShowIsActive', value: value });
}
}
async getUsers() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/users',
responseType: 'json',
params: {
token: api_token
},
});
}
isSynced() {
const brochures = SendBrochure.getAll();
const Contacts = Contact.getAll();
async getContacts(userId) {
let contacts = Contact.getAll();
return axios({
method: 'POST',
url: this.getUrlBo() + '/api/contacts',
responseType: 'json',
params: {
token: api_token
},
data: {
contacts: contacts,
userId: userId,
}
});
}
async sendBrochure(brochure) {
return axios({
method: 'POST',
url: this.getUrlBo() + '/api/sendBrochure',
responseType: 'json',
params: {
token: api_token,
emails: brochure,
},
});
}
async getDirectories() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/directories',
responseType: 'json',
params: {
token: api_token
},
});
}
async getDocuments() {
return axios({
method: 'GET',
url: this.getUrlBo() + '/api/documents',
responseType: 'json',
params: {
token: api_token,
},
});
}
updateLastModificationDoc(id, date) {
Document.update({ id: id, updated_at: date.toString() }, true);
}
updateVersion(version) {
const localVersion = Setting.get('version');
if (localVersion === false || localVersion === undefined) {
Setting.add({ key: 'version', value: version });
} else {
Setting.update({ id: localVersion.id, key: 'version', value: version });
}
}
updateShowIsActive(value) {
const local = Setting.get('ShowIsActive');
if (local === false || local === undefined) {
Setting.add({ key: 'ShowIsActive', value: value });
} else {
Setting.update({ id: local.id, key: 'ShowIsActive', value: value });
}
}
isSynced() {
const brochures = SendBrochure.getAll();
const Contacts = Contact.getAll();
if (brochures !== undefined && Contacts !== undefined) {
const noSynced = Contacts.filtered('synced = false');
if (brochures.length > 0 || noSynced.length > 0) {
return false;
}
}
return true;
}
isLogged() {
const logged = Setting.get('isLogged');
const loggedTime = Setting.get('loggedTime');
if (
logged !== false &&
loggedTime !== false &&
logged !== undefined &&
loggedTime !== undefined
) {
let seconds =
Math.abs(new Date(Number(loggedTime.value)) - Date.now()) / 1000;
if (seconds < 14400) {
// 4h
return true;
}
return false;
}
if (brochures !== undefined && Contacts !== undefined) {
const noSynced = Contacts.filtered('synced = false');
if (brochures.length > 0 || noSynced.length > 0) {
return false;
}
}
return true;
}
needSync(callback) {
NetInfo.fetch().then(state => {
if (state.isConnected) {
axios({
method: 'get',
url: this.getUrlBo() + '/version',
responseType: 'json',
params: {
token: api_token,
},
})
.then(response => {
const localVersion = Setting.get('version');
if (
localVersion === undefined ||
localVersion === false ||
localVersion.value !== response.data.version
) {
return callback();
}
return false;
})
.catch(function (error) {
console.log(error);
});
isLogged() {
const logged = Setting.get('isLogged');
const loggedTime = Setting.get('loggedTime');
if (
logged !== false &&
loggedTime !== false &&
logged !== undefined &&
loggedTime !== undefined
) {
let seconds =
Math.abs(new Date(Number(loggedTime.value)) - Date.now()) / 1000;
if (seconds < 14400) {
// 4h
return true;
}
return false;
}
return false;
}
needSync(callback) {
NetInfo.fetch().then(state => {
if (state.isConnected) {
axios({
method: 'get',
url: this.getUrlBo() + '/version',
responseType: 'json',
params: {
token: api_token,
},
})
.then(response => {
const localVersion = Setting.get('version');
if (
localVersion === undefined ||
localVersion === false ||
localVersion.value !== response.data.version
) {
return callback();
}
return false;
});
return false;
}
})
.catch(function (error) {
console.log(error);
});
}
return false;
});
return false;
}
setInstalled(remove = false) {
if (remove) {
Setting.delete('installed');
} else {
Setting.add({ key: 'installed', value: "true" });
}
setInstalled(remove = false) {
if (remove) {
Setting.delete('installed');
} else {
Setting.add({ key: 'installed', value: 'true' });
}
}
isInstalled() {
const test = Setting.get('installed');
if (test === false || test === undefined) {
return false;
}
return true;
isInstalled() {
const test = Setting.get('installed');
if (test === false || test === undefined) {
return false;
}
return true;
}
}
export default new SyncController();
export default new SyncController();

View file

@ -1,50 +1,55 @@
class Helpers {
compareValues(key, order = 'asc') {
return function innerSort(a, b) {
if (!(key in a) || !(key in b)) {
// property doesn't exist on either object
return 0;
}
const varA = (typeof a[key] === 'string')
? a[key].toUpperCase() : a[key];
const varB = (typeof b[key] === 'string')
? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return (
(order === 'desc') ? (comparison * -1) : comparison
);
};
}
isEmailValid(email) {
const expression = /(?!.*\.{2})^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i;
return expression.test(String(email).toLowerCase());
}
isUrlValid(value) {
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value.toLowerCase());
}
generateUUID() { // Public Domain/MIT
let d = new Date().getTime();//Timestamp
let d2 = new Date().getUTCMilliseconds() + Math.random();//Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16;//random number between 0 and 16
if(d > 0){//Use timestamp until depleted
r = (d + r)%16 | 0;
d = Math.floor(d/16);
} else {//Use microseconds since page-load if supported
r = (d2 + r)%16 | 0;
d2 = Math.floor(d2/16);
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
}
export default new Helpers();
class Helpers {
compareValues(key, order = 'asc') {
return function innerSort(a, b) {
if (!(key in a) || !(key in b)) {
// property doesn't exist on either object
return 0;
}
const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key];
const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return order === 'desc' ? comparison * -1 : comparison;
};
}
isEmailValid(email) {
const expression =
/(?!.*\.{2})^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i;
return expression.test(String(email).toLowerCase());
}
isUrlValid(value) {
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
value.toLowerCase(),
);
}
generateUUID() {
// Public Domain/MIT
let d = new Date().getTime(); //Timestamp
let d2 = new Date().getUTCMilliseconds() + Math.random(); //Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (c) {
let r = Math.random() * 16; //random number between 0 and 16
if (d > 0) {
//Use timestamp until depleted
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else {
//Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
},
);
}
}
export default new Helpers();

View file

@ -101,7 +101,6 @@ class ContactModel {
});
}
}
}
export default ContactModel;

View file

@ -4,7 +4,7 @@ export const DirectoriesSchema = {
properties: {
id: 'int',
name: 'string',
parent: { type: 'int', optional: true },
parent: {type: 'int', optional: true},
order: 'int',
needAuthorization: 'int',
},
@ -46,14 +46,12 @@ class DirectoryModel {
if (authorizedFolders.length > 0) {
tmp = tmp.filtered(
'needAuthorization = 0 OR ' +
authorizedFolders
.map(folder => ' id = "' + folder.id + '"')
.join(' OR '),
authorizedFolders
.map(folder => ' id = "' + folder.id + '"')
.join(' OR '),
);
} else {
tmp = tmp.filtered(
'needAuthorization = 0'
);
tmp = tmp.filtered('needAuthorization = 0');
}
}
if (format) {
@ -90,18 +88,18 @@ class DirectoryModel {
}
getI18nName(lang, name) {
let tmp = undefined;
let tmp;
switch (lang) {
case "fr":
case 'fr':
tmp = name.fr;
break;
case "en":
case 'en':
tmp = name.en;
break;
case "de":
case 'de':
tmp = name.de;
break;
case "es":
case 'es':
tmp = name.es;
break;
}
@ -110,7 +108,6 @@ class DirectoryModel {
}
return tmp;
}
}
export default DirectoryModel;

View file

@ -1,79 +1,83 @@
export const DocumentsSchema = {
name: 'Documents',
primaryKey: 'id',
properties: {
id: 'int',
uuid: 'string',
title: 'string',
type: 'string',
path: 'string',
directory: 'int',
lang: 'string',
updated_at: 'string',
},
name: 'Documents',
primaryKey: 'id',
properties: {
id: 'int',
uuid: 'string',
title: 'string',
type: 'string',
path: 'string',
directory: 'int',
lang: 'string',
updated_at: 'string',
},
};
class DocumentModel {
constructor(realm) {
this.schema = 'Documents';
this.realm = realm;
}
constructor(realm) {
this.schema = 'Documents';
this.realm = realm;
}
add(document) {
this.realm.write(() => {
this.realm.create(this.schema, document, 'never');
});
}
add(document) {
this.realm.write(() => {
this.realm.create(this.schema, document, 'never');
});
}
update(document, updatedAtChange = false) {
let tmp = this.get(document.id);
if (tmp === undefined) {
this.add(document);
update(document, updatedAtChange = false) {
let tmp = this.get(document.id);
if (tmp === undefined) {
this.add(document);
} else {
this.realm.write(() => {
if (updatedAtChange === false) {
this.realm.create(
this.schema,
{
id: tmp.id,
uuid: document.uuid,
title: document.title,
type: document.type,
path: document.path,
directory: document.directory,
},
'modified',
);
} else {
this.realm.write(() => {
if (updatedAtChange === false) {
this.realm.create(this.schema, {
id: tmp.id,
uuid: document.uuid,
title: document.title,
type: document.type,
path: document.path,
directory: document.directory
}, 'modified');
} else {
this.realm.create(this.schema, document, 'modified');
}
});
this.realm.create(this.schema, document, 'modified');
}
});
}
}
get(id) {
let tmp = this.realm.objects(this.schema).filtered('id = ' + id)[0];
return tmp;
get(id) {
let tmp = this.realm.objects(this.schema).filtered('id = ' + id)[0];
return tmp;
}
getAll(authorizedDirs = false) {
let tmp = this.realm.objects(this.schema);
if (authorizedDirs) {
authorizedDirs = authorizedDirs.map(item => item.id);
tmp = tmp.filter(doc => authorizedDirs.includes(doc.directory));
}
return tmp;
}
getAll(authorizedDirs = false) {
let tmp = this.realm.objects(this.schema);
if (authorizedDirs) {
authorizedDirs = authorizedDirs.map(item => item.id);
tmp = tmp.filter(doc => authorizedDirs.includes(doc.directory));
}
return tmp;
}
clear(docs, toDelete = []) {
const localDocs = this.getAll();
localDocs.forEach(doc => {
let tmp = docs.filter(item => item.uuid === doc.uuid);
if (tmp.length === 0) {
toDelete.push({ uuid: doc.uuid, type: doc.type });
this.realm.write(() => {
this.realm.delete(doc);
});
}
clear(docs, toDelete = []) {
const localDocs = this.getAll();
localDocs.forEach(doc => {
let tmp = docs.filter(item => item.uuid === doc.uuid);
if (tmp.length === 0) {
toDelete.push({uuid: doc.uuid, type: doc.type});
this.realm.write(() => {
this.realm.delete(doc);
});
return toDelete;
}
}
});
return toDelete;
}
}
export default DocumentModel;

View file

@ -7,7 +7,7 @@ export const SendBrochuresSchema = {
subject: 'string',
message: 'string',
documents: 'string',
user_id: 'int'
user_id: 'int',
},
};
@ -80,7 +80,6 @@ class SendBrochureModel {
});
}
}
}
export default SendBrochureModel;

View file

@ -10,10 +10,10 @@ export const SettingsSchema = {
class SettingModel {
constructor(realm) {
this.schema = 'Settings';
this.realm = realm;
this.schema = 'Settings';
this.realm = realm;
}
add(data) {
this.realm.write(() => {
if (data.id === undefined) {
@ -66,7 +66,6 @@ class SettingModel {
});
}
}
}
export default SettingModel;
export default SettingModel;

View file

@ -72,7 +72,6 @@ class UserModel {
this.realm.delete(this.getAll());
});
}
}
export default UserModel;

View file

@ -1,14 +1,14 @@
// Locale
export const INIT_LOCALE = "INIT_LOCALE";
export const EDIT_LOCALE = "EDIT_LOCALE";
export const INIT_LOCALE = 'INIT_LOCALE';
export const EDIT_LOCALE = 'EDIT_LOCALE';
// Users
export const INIT_USERS = "INIT_USERS";
export const INIT_USERS = 'INIT_USERS';
// Contacts
export const INIT_CONTACT = "INIT_CONTACT";
export const ADD_CONTACT = "ADD_CONTACT";
export const EDIT_CONTACT = "EDIT_CONTACT";
export const DELETE_CONTACT = "DELETE_CONTACT";
export const INIT_CONTACT = 'INIT_CONTACT';
export const ADD_CONTACT = 'ADD_CONTACT';
export const EDIT_CONTACT = 'EDIT_CONTACT';
export const DELETE_CONTACT = 'DELETE_CONTACT';
// Directories
export const INIT_DIRECTORIES = "INIT_DIRECTORIES";
export const INIT_DIRECTORIES = 'INIT_DIRECTORIES';
// Documents
export const INIT_DOCUMENTS = "INIT_DOCUMENTS";
export const INIT_DOCUMENTS = 'INIT_DOCUMENTS';

View file

@ -1,4 +1,9 @@
import { INIT_CONTACT, ADD_CONTACT, EDIT_CONTACT, DELETE_CONTACT } from "./actionType";
import {
ADD_CONTACT,
DELETE_CONTACT,
EDIT_CONTACT,
INIT_CONTACT,
} from './actionType';
/**
* Init contact
@ -6,11 +11,11 @@ import { INIT_CONTACT, ADD_CONTACT, EDIT_CONTACT, DELETE_CONTACT } from "./actio
* @param {object} data The contact data
*/
export const initContact = (currentUser, data) => {
return {
type: INIT_CONTACT,
currentUser,
data,
};
return {
type: INIT_CONTACT,
currentUser,
data,
};
};
/**
@ -19,11 +24,11 @@ export const initContact = (currentUser, data) => {
* @param {object} data The contact data
*/
export const addContact = (currentUser, data) => {
return {
type: ADD_CONTACT,
currentUser,
data,
};
return {
type: ADD_CONTACT,
currentUser,
data,
};
};
/**
@ -32,11 +37,11 @@ export const addContact = (currentUser, data) => {
* @param {object} data The contact data
*/
export const editContact = (currentUser, data) => {
return {
type: EDIT_CONTACT,
currentUser,
data,
};
return {
type: EDIT_CONTACT,
currentUser,
data,
};
};
/**
@ -45,9 +50,9 @@ export const editContact = (currentUser, data) => {
* @param {string} id The contact id
*/
export const deleteContact = (currentUser, id) => {
return {
type: DELETE_CONTACT,
currentUser,
id,
};
};
return {
type: DELETE_CONTACT,
currentUser,
id,
};
};

View file

@ -1,4 +1,4 @@
import { INIT_DIRECTORIES } from "./actionType";
import {INIT_DIRECTORIES} from './actionType';
/**
* Init
@ -7,10 +7,10 @@ import { INIT_DIRECTORIES } from "./actionType";
* @param {object} data The document data
*/
export const initDirectories = (lang, user, data) => {
return {
type: INIT_DIRECTORIES,
lang,
user,
data,
};
};
return {
type: INIT_DIRECTORIES,
lang,
user,
data,
};
};

View file

@ -1,4 +1,4 @@
import { INIT_DOCUMENTS } from "./actionType";
import {INIT_DOCUMENTS} from './actionType';
/**
* Init contact
@ -7,10 +7,10 @@ import { INIT_DOCUMENTS } from "./actionType";
* @param {object} data The document data
*/
export const initDocuments = (lang, directories, data) => {
return {
type: INIT_DOCUMENTS,
lang,
directories,
data,
};
};
return {
type: INIT_DOCUMENTS,
lang,
directories,
data,
};
};

View file

@ -1,14 +1,14 @@
import { INIT_LOCALE, EDIT_LOCALE } from "./actionType";
import {EDIT_LOCALE, INIT_LOCALE} from './actionType';
/**
* Init locale
* @param {object} data The locale data
*/
export const initLocale = data => {
return {
type: INIT_LOCALE,
data,
};
return {
type: INIT_LOCALE,
data,
};
};
/**
@ -16,8 +16,8 @@ export const initLocale = data => {
* @param {object} data The locale data
*/
export const editLocale = data => {
return {
type: EDIT_LOCALE,
data,
};
return {
type: EDIT_LOCALE,
data,
};
};

View file

@ -1,12 +1,12 @@
import { INIT_USERS } from "./actionType";
import {INIT_USERS} from './actionType';
/**
* Init users
* @param {object} data The users data
*/
export const initUsers = data => {
return {
type: INIT_USERS,
data,
};
};
return {
type: INIT_USERS,
data,
};
};

View file

@ -1,19 +1,19 @@
import { createStore, combineReducers } from "redux";
import {combineReducers, createStore} from 'redux';
import appReducer from "./Reducers/AppReducer";
import contactsReducer from "./Reducers/contacts";
import documentsReducer from "./Reducers/documents";
import directoriesReducer from "./Reducers/directories";
import localesReducer from "./Reducers/locales";
import usersReducer from "./Reducers/users";
import appReducer from './Reducers/AppReducer';
import contactsReducer from './Reducers/contacts';
import directoriesReducer from './Reducers/directories';
import documentsReducer from './Reducers/documents';
import localesReducer from './Reducers/locales';
import usersReducer from './Reducers/users';
const rootReducer = combineReducers({
app: appReducer,
contacts: contactsReducer,
documents: documentsReducer,
directories: directoriesReducer,
locale: localesReducer,
users: usersReducer
app: appReducer,
contacts: contactsReducer,
documents: documentsReducer,
directories: directoriesReducer,
locale: localesReducer,
users: usersReducer,
});
export const store = createStore(rootReducer);
export const store = createStore(rootReducer);

View file

@ -1,125 +1,147 @@
import SyncController from "../../Controllers/SyncController";
// Database
import { Setting, SendBrochure } from '../../../Database';
import SyncController from '../../Controllers/SyncController';
// Database
import { SendBrochure, Setting } from '../../../Database';
const initialState = {
isSetup: false,
locale: 'fr',
currentUser: {},
logged: false,
syncVersion: 0,
forceSync: false,
currentOpenedDoc: {},
ShowIsActive: 0,
HomeBtns: {},
mainColor: '#D4021D',
FormSelect1: {},
FormSelect2: {},
FormCheck: [],
isSetup: false,
locale: 'fr',
currentUser: {},
logged: false,
syncVersion: 0,
forceSync: false,
currentOpenedDoc: {},
ShowIsActive: 0,
HomeBtns: {},
mainColor: '#D4021D',
FormSelect1: {},
FormSelect2: {},
FormCheck: [],
};
const reducer = (state = initialState, action) => {
let saved;
switch (action.type) {
case 'INIT':
let ShowIsActive = Setting.get('ShowIsActive');
let MainColor = Setting.get('MainColor');
let FormSelect1 = Setting.get('FormSelect1');
let FormSelect2 = Setting.get('FormSelect2');
let FormCheck = Setting.get('FormCheck');
saved = Setting.get('isSetup');
return {
...state,
isSetup: saved === undefined ? false : saved.value === "true" ? true : false,
ShowIsActive: ShowIsActive === undefined ? false : ShowIsActive.value,
mainColor: MainColor === undefined ? initialState.mainColor : MainColor === false ? initialState.mainColor : MainColor.value,
FormSelect1: FormSelect1 === undefined ? initialState.FormSelect1 : FormSelect1 === false ? initialState.FormSelect1 : JSON.parse(FormSelect1.value),
FormSelect2: FormSelect2 === undefined ? initialState.FormSelect2 : FormSelect2 === false ? initialState.FormSelect2 : JSON.parse(FormSelect2.value),
FormCheck: FormCheck === undefined ? initialState.FormCheck : FormCheck === false ? initialState.FormCheck : JSON.parse(FormCheck.value),
};
case 'INIT_HOME_BTNS': {
let HomeFolders = Setting.get('HomeFolders');
let Btns = JSON.parse(HomeFolders.value);
return {
...state,
HomeBtns: Btns
}
}
case 'SET_ISSETUP':
Setting.upsert({ key: 'isSetup', value: "true" });
saved = Setting.get('isSetup');
return {
...state,
isSetup: saved === undefined ? false : saved.value === "true" ? true : false
};
case 'SET_CURRENT_USER':
let user = false;
let isLogged = false;
if (action.data === false) {
Setting.delete('isLogged');
} else {
userObj = Setting.get('isLogged').value
user = JSON.parse(userObj)
isLogged = true
}
return {
...state,
currentUser: user !== false ? user : initialState.currentUser,
logged: isLogged
};
// case 'UPDATE_LOCALE':
// let nxt;
// if (action.data === undefined) {
// const availableLocales = ['fr', 'en', 'de'];
// if (availableLocales.indexOf(i18n.locale) === availableLocales.length - 1) {
// nxt = availableLocales[0];
// } else {
// nxt = availableLocales[availableLocales.indexOf(i18n.locale) + 1];
// }
// } else {
// nxt = action.data;
// }
// i18n.locale = nxt;
// Setting.upsert({ key: 'locale', value: nxt });
// let docsL = Document.getAll(Directory.getAll(
// state.currentUser.role,
// state.currentUser.authorizedFolders,
// false,
// nxt
// ));
// return {
// ...state,
// locale: nxt,
// directories: Directory.getAll(
// state.currentUser.role,
// state.currentUser.authorizedFolders,
// true,
// nxt
// ),
// documents: docsL.filter(item => item.lang === nxt),
// };
case 'SET_CURRENT_OPENED_DOC':
return {
...state,
currentOpenedDoc: action.data
};
case 'UPDATE_VERSION':
return {
...state,
syncVersion: action.data,
};
case 'UPDATE_SHOW_IS_ACTIVE':
SyncController.updateShowIsActive(action.data);
return {
...state,
ShowIsActive: action.data,
};
case 'SEND_BROCHURE':
SendBrochure.add(action.data);
return state;
default:
return state;
let saved;
switch (action.type) {
case 'INIT':
let ShowIsActive = Setting.get('ShowIsActive');
let MainColor = Setting.get('MainColor');
let FormSelect1 = Setting.get('FormSelect1');
let FormSelect2 = Setting.get('FormSelect2');
let FormCheck = Setting.get('FormCheck');
saved = Setting.get('isSetup');
return {
...state,
isSetup:
saved === undefined ? false : saved.value === 'true' ? true : false,
ShowIsActive: ShowIsActive === undefined ? false : ShowIsActive.value,
mainColor:
MainColor === undefined
? initialState.mainColor
: MainColor === false
? initialState.mainColor
: MainColor.value,
FormSelect1:
FormSelect1 === undefined
? initialState.FormSelect1
: FormSelect1 === false
? initialState.FormSelect1
: JSON.parse(FormSelect1.value),
FormSelect2:
FormSelect2 === undefined
? initialState.FormSelect2
: FormSelect2 === false
? initialState.FormSelect2
: JSON.parse(FormSelect2.value),
FormCheck:
FormCheck === undefined
? initialState.FormCheck
: FormCheck === false
? initialState.FormCheck
: JSON.parse(FormCheck.value),
};
case 'INIT_HOME_BTNS': {
let HomeFolders = Setting.get('HomeFolders');
let Btns = JSON.parse(HomeFolders.value);
return {
...state,
HomeBtns: Btns,
};
}
}
case 'SET_ISSETUP':
Setting.upsert({ key: 'isSetup', value: 'true' });
saved = Setting.get('isSetup');
return {
...state,
isSetup:
saved === undefined ? false : saved.value === 'true' ? true : false,
};
case 'SET_CURRENT_USER':
let user = false;
let isLogged = false;
if (action.data === false) {
Setting.delete('isLogged');
} else {
userObj = Setting.get('isLogged').value;
user = JSON.parse(userObj);
isLogged = true;
}
return {
...state,
currentUser: user !== false ? user : initialState.currentUser,
logged: isLogged,
};
// case 'UPDATE_LOCALE':
// let nxt;
// if (action.data === undefined) {
// const availableLocales = ['fr', 'en', 'de'];
// if (availableLocales.indexOf(i18n.locale) === availableLocales.length - 1) {
// nxt = availableLocales[0];
// } else {
// nxt = availableLocales[availableLocales.indexOf(i18n.locale) + 1];
// }
// } else {
// nxt = action.data;
// }
// i18n.locale = nxt;
// Setting.upsert({ key: 'locale', value: nxt });
// let docsL = Document.getAll(Directory.getAll(
// state.currentUser.role,
// state.currentUser.authorizedFolders,
// false,
// nxt
// ));
// return {
// ...state,
// locale: nxt,
// directories: Directory.getAll(
// state.currentUser.role,
// state.currentUser.authorizedFolders,
// true,
// nxt
// ),
// documents: docsL.filter(item => item.lang === nxt),
// };
case 'SET_CURRENT_OPENED_DOC':
return {
...state,
currentOpenedDoc: action.data,
};
case 'UPDATE_VERSION':
return {
...state,
syncVersion: action.data,
};
case 'UPDATE_SHOW_IS_ACTIVE':
SyncController.updateShowIsActive(action.data);
return {
...state,
ShowIsActive: action.data,
};
case 'SEND_BROCHURE':
SendBrochure.add(action.data);
return state;
default:
return state;
}
};
export default reducer;
export default reducer;

View file

@ -1,32 +1,37 @@
import { INIT_CONTACT, ADD_CONTACT, EDIT_CONTACT, DELETE_CONTACT } from "../Actions/actionType";
import {
ADD_CONTACT,
DELETE_CONTACT,
EDIT_CONTACT,
INIT_CONTACT,
} from '../Actions/actionType';
// Models
import { Contact } from "../../../Database";
import {Contact} from '../../../Database';
const initialState = [];
const reducer = (state = initialState, action) => {
switch (action.type) {
case INIT_CONTACT: {
return Contact.getByUser(action.currentUser.id);
}
case ADD_CONTACT: {
action.data.user_id = action.currentUser.id;
Contact.add(action.data);
return Contact.getByUser(action.currentUser.id);
}
case EDIT_CONTACT: {
Contact.update(action.data);
return Contact.getByUser(action.currentUser.id);
}
case DELETE_CONTACT: {
Contact.delete(action.id);
return Contact.getByUser(action.currentUser.id);
}
default: {
return state;
}
switch (action.type) {
case INIT_CONTACT: {
return Contact.getByUser(action.currentUser.id);
}
case ADD_CONTACT: {
action.data.user_id = action.currentUser.id;
Contact.add(action.data);
return Contact.getByUser(action.currentUser.id);
}
case EDIT_CONTACT: {
Contact.update(action.data);
return Contact.getByUser(action.currentUser.id);
}
case DELETE_CONTACT: {
Contact.delete(action.id);
return Contact.getByUser(action.currentUser.id);
}
default: {
return state;
}
}
};
export default reducer;

View file

@ -1,32 +1,32 @@
import { INIT_DIRECTORIES } from "../Actions/actionType";
import {INIT_DIRECTORIES} from '../Actions/actionType';
// Models
import { Directory } from "../../../Database";
import {Directory} from '../../../Database';
const initialState = [];
const reducer = (state = initialState, action) => {
switch (action.type) {
case INIT_DIRECTORIES: {
return {
global: Directory.getAll(
action.user.role,
action.user.authorizedFolders,
false,
action.lang
),
treeview: Directory.getAll(
action.user.role,
action.user.authorizedFolders,
true,
action.lang
)
}
}
default: {
return state;
}
switch (action.type) {
case INIT_DIRECTORIES: {
return {
global: Directory.getAll(
action.user.role,
action.user.authorizedFolders,
false,
action.lang,
),
treeview: Directory.getAll(
action.user.role,
action.user.authorizedFolders,
true,
action.lang,
),
};
}
default: {
return state;
}
}
};
export default reducer;

View file

@ -1,23 +1,23 @@
import { INIT_DOCUMENTS } from "../Actions/actionType";
import {INIT_DOCUMENTS} from '../Actions/actionType';
// Models
import { Document } from "../../../Database";
import {Document} from '../../../Database';
const initialState = [];
const reducer = (state = initialState, action) => {
switch (action.type) {
case INIT_DOCUMENTS: {
let docs = Document.getAll(action.directories);
if (docs.length === 0) {
return state
}
return docs.filter(item => item.lang === action.lang);
}
default: {
return state;
}
switch (action.type) {
case INIT_DOCUMENTS: {
let docs = Document.getAll(action.directories);
if (docs.length === 0) {
return state;
}
return docs.filter(item => item.lang === action.lang);
}
default: {
return state;
}
}
};
export default reducer;

View file

@ -1,45 +1,53 @@
import { INIT_LOCALE, EDIT_LOCALE } from "../Actions/actionType";
import {EDIT_LOCALE, INIT_LOCALE} from '../Actions/actionType';
// Models
import { Setting } from "../../../Database";
import {Setting} from '../../../Database';
// Translations
import i18n from "../../../I18n";
import i18n from '../../../I18n';
const initialState = [];
const reducer = (state = initialState, action) => {
switch (action.type) {
case INIT_LOCALE: {
const savedLocale = Setting.get('locale');
return {
...state,
locale: savedLocale === undefined ? 'fr' : savedLocale === false ? 'fr' : savedLocale.value
};
}
case EDIT_LOCALE: {
let nxt;
if (action.data === undefined) {
const availableLocales = ['fr', 'en', 'de'];
if (availableLocales.indexOf(i18n.locale) === availableLocales.length - 1) {
nxt = availableLocales[0];
} else {
nxt = availableLocales[availableLocales.indexOf(i18n.locale) + 1];
}
} else {
nxt = action.data;
}
i18n.locale = nxt;
Setting.upsert({ key: 'locale', value: nxt });
return {
...state,
locale: nxt,
};
}
default: {
return state;
}
switch (action.type) {
case INIT_LOCALE: {
const savedLocale = Setting.get('locale');
return {
...state,
locale:
savedLocale === undefined
? 'fr'
: savedLocale === false
? 'fr'
: savedLocale.value,
};
}
case EDIT_LOCALE: {
let nxt;
if (action.data === undefined) {
const availableLocales = ['fr', 'en', 'de'];
if (
availableLocales.indexOf(i18n.locale) ===
availableLocales.length - 1
) {
nxt = availableLocales[0];
} else {
nxt = availableLocales[availableLocales.indexOf(i18n.locale) + 1];
}
} else {
nxt = action.data;
}
i18n.locale = nxt;
Setting.upsert({key: 'locale', value: nxt});
return {
...state,
locale: nxt,
};
}
default: {
return state;
}
}
};
export default reducer;

View file

@ -1,22 +1,22 @@
import { INIT_USERS } from "../Actions/actionType";
import {INIT_USERS} from '../Actions/actionType';
// Models
import { User } from "../../../Database";
import {User} from '../../../Database';
const initialState = [];
const reducer = (state = initialState, action) => {
switch (action.type) {
case INIT_USERS: {
return {
...state,
users: User.getAll(),
};
}
default: {
return state;
}
switch (action.type) {
case INIT_USERS: {
return {
...state,
users: User.getAll(),
};
}
default: {
return state;
}
}
};
export default reducer;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,7 @@
import React from 'react';
import {
View,
ScrollView,
TouchableOpacity,
Text,
Image
} from 'react-native';
import {Image, ScrollView, Text, TouchableOpacity, View} from 'react-native';
// Redux
import { connect } from 'react-redux';
import { initDocuments } from '../Store/Actions/documents';
import {connect} from 'react-redux';
// Treeview
import TreeView from 'react-native-animated-tree-view';
// Icon
@ -19,72 +12,72 @@ import arrowDown from '../../Resources/assets/img/arrow_down.png';
import styles from '../../Resources/Styles/DirectoriesStyle';
function mapStateToProps(state) {
return {
documents: state.documents,
directories: state.directories,
locale: state.locale.locale,
mainColor: state.app.mainColor
};
return {
documents: state.documents,
directories: state.directories,
locale: state.locale.locale,
mainColor: state.app.mainColor,
};
}
class DirectoriesScreen extends React.Component {
state = {
documents: [],
};
state = {
documents: [],
};
render() {
const { folder } = this.props.route.params;
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{ ...styles.file, backgroundColor: this.props.mainColor }}
onPress={() => this.openDocument(document)}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
render() {
const {folder} = this.props.route.params;
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{...styles.file, backgroundColor: this.props.mainColor}}
onPress={() => this.openDocument(document)}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
<View
style={{ width: 290, borderRightWidth: 1, borderColor: 'lightgrey' }}>
<TreeView
data={folder}
leftImage={{}}
rightImage={{ uri: Image.resolveAssetSource(arrowDown).uri }}
containerStyle={styles.treeviewContainer}
onClick={folder => {
this.setState({
documents: this.props.documents.filter(
tmp => tmp.directory === folder.id,
)
});
}}
/>
</View>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
</ScrollView>
</View>
);
}
openDocument(document) {
this.props.dispatch({ type: 'SET_CURRENT_OPENED_DOC', data: document });
if (document.type === 'pdf') {
this.props.navigation.navigate('PDFScreen', { uuid: document.uuid });
} else if (document.type === 'mp4') {
this.props.navigation.navigate('VideoScreen', { uuid: document.uuid });
} else {
console.log('Inconnu');
}
return (
<View style={styles.container}>
<View
style={{width: 290, borderRightWidth: 1, borderColor: 'lightgrey'}}>
<TreeView
data={folder}
leftImage={{}}
rightImage={{uri: Image.resolveAssetSource(arrowDown).uri}}
containerStyle={styles.treeviewContainer}
onClick={folder => {
this.setState({
documents: this.props.documents.filter(
tmp => tmp.directory === folder.id,
),
});
}}
/>
</View>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
</ScrollView>
</View>
);
}
openDocument(document) {
this.props.dispatch({type: 'SET_CURRENT_OPENED_DOC', data: document});
if (document.type === 'pdf') {
this.props.navigation.navigate('PDFScreen', {uuid: document.uuid});
} else if (document.type === 'mp4') {
this.props.navigation.navigate('VideoScreen', {uuid: document.uuid});
} else {
console.log('Inconnu');
}
}
}
export default connect(mapStateToProps)(DirectoriesScreen);

View file

@ -1,18 +1,24 @@
import React from 'react';
import { View, ImageBackground, TouchableOpacity, Text, Image } from 'react-native';
// Common Styles
import {
Image,
ImageBackground,
Text,
TouchableOpacity,
View,
} from 'react-native';
// Common Styles
import commonStyles from '../../Resources/CommonStyles';
// Redux
import { connect } from 'react-redux';
import { initDocuments } from '../Store/Actions/documents';
import { initContact } from '../Store/Actions/contacts';
import { initDirectories } from '../Store/Actions/directories';
// SplashScreen
import {connect} from 'react-redux';
import {initContact} from '../Store/Actions/contacts';
import {initDirectories} from '../Store/Actions/directories';
import {initDocuments} from '../Store/Actions/documents';
// SplashScreen
import * as SplashScreen from 'expo-splash-screen';
// Style
import styles from '../../Resources/Styles/HomeStyle';
// Translations
import i18n from "../../I18n";
import i18n from '../../I18n';
// FileSytem
import * as FileSystem from 'expo-file-system';
// NetInfo
@ -21,105 +27,131 @@ import NetInfo from '@react-native-community/netinfo';
import SyncController from '../Controllers/SyncController';
function mapStateToProps(state) {
return {
btns: state.app.HomeBtns,
directories: state.directories,
locale: state.locale.locale,
currentUser: state.app.currentUser
};
return {
btns: state.app.HomeBtns,
directories: state.directories,
locale: state.locale.locale,
currentUser: state.app.currentUser,
};
}
const mapDispatchToProps = dispatch => {
return {
initBtns: () => dispatch({ type: 'INIT_HOME_BTNS' }),
initDocuments: (lang, directories) => dispatch(initDocuments(lang, directories)),
initContact: currentUser => dispatch(initContact(currentUser)),
initDirectories: (lang, user) => dispatch(initDirectories(lang, user)),
};
return {
initBtns: () => dispatch({type: 'INIT_HOME_BTNS'}),
initDocuments: (lang, directories) =>
dispatch(initDocuments(lang, directories)),
initContact: currentUser => dispatch(initContact(currentUser)),
initDirectories: (lang, user) => dispatch(initDirectories(lang, user)),
};
};
class HomeScreen extends React.Component {
state = {
lastSyncCall: '1970-01-01',
};
state = {
lastSyncCall: '1970-01-01',
};
componentDidMount() {
if (!SyncController.isInstalled()) {
return this.openSync();
}
this.props.initBtns();
this.props.initContact(this.props.currentUser);
this.props.initDirectories(this.props.locale, this.props.currentUser);
this.props.initDocuments(this.props.locale, this.props.directories.global);
componentDidMount() {
if (!SyncController.isInstalled()) {
return this.openSync();
SplashScreen.hideAsync();
}
unsubscribe() {
NetInfo.addEventListener(state => {
if (state.isConnected === true) {
let seconds =
Math.abs(new Date(this.state.lastSyncCall) - Date.now()) / 1000;
if (seconds > 30) {
this.setState({lastSyncCall: Date.now()});
SyncController.needSync(this.openSync.bind(this));
}
this.props.initBtns();
this.props.initContact(this.props.currentUser);
this.props.initDirectories(this.props.locale, this.props.currentUser);
this.props.initDocuments(this.props.locale, this.props.directories.global);
}
});
}
SplashScreen.hideAsync();
}
openSync() {
this.props.navigation.navigate('SyncScreen', {forceSync: true});
}
unsubscribe() {
NetInfo.addEventListener(state => {
if (state.isConnected === true) {
let seconds =
Math.abs(new Date(this.state.lastSyncCall) - Date.now()) / 1000;
if (seconds > 30) {
this.setState({ lastSyncCall: Date.now() });
SyncController.needSync(this.openSync.bind(this));
}
}
});
}
render() {
i18n.locale = this.props.locale;
return (
<ImageBackground
source={{uri: FileSystem.documentDirectory + 'BackgroundImage.png'}}
style={commonStyles.background}
resizeMode="cover">
<View style={styles.container}>
{this.renderBtns()}
<TouchableOpacity
key={'OnlyVideos'}
onPress={() => {
this.props.initDocuments(
this.props.locale,
this.props.directories.global,
);
this.props.navigation.navigate('OnlyVideosScreen');
}}
style={styles.button}>
<Image
source={{
uri: FileSystem.documentDirectory + 'HomeButtonImage.png',
}}
style={styles.btnImg}
/>
<Text style={styles.btnTxt}>{i18n.t('global.videos')}</Text>
</TouchableOpacity>
<TouchableOpacity
key={'Contacts'}
onPress={() => this.props.navigation.navigate('ContactScreen')}
style={styles.button}>
<Image
source={{
uri: FileSystem.documentDirectory + 'HomeButtonImage.png',
}}
style={styles.btnImg}
/>
<Text style={styles.btnTxt}>{i18n.t('global.contacts')}</Text>
</TouchableOpacity>
</View>
</ImageBackground>
);
}
openSync() {
this.props.navigation.navigate('SyncScreen', { forceSync: true });
}
renderBtns() {
let locale = this.props.locale;
let objBtns = this.props.btns;
let array = Object.keys(objBtns).map(function (key) {
return objBtns[key];
});
return array.map((item, index) => {
let names = JSON.parse(item.name);
let name = locale in names ? names[locale] : names.fr;
return (
<TouchableOpacity
key={index}
onPress={() => this.openFolder(item.id)}
style={styles.button}>
<Image
source={{uri: FileSystem.documentDirectory + 'HomeButtonImage.png'}}
style={styles.btnImg}
/>
<Text style={styles.btnTxt}>{name}</Text>
</TouchableOpacity>
);
});
}
render() {
i18n.locale = this.props.locale;
return (
<ImageBackground source={{ uri: FileSystem.documentDirectory + 'BackgroundImage.png' }} style={commonStyles.background} resizeMode='cover'>
<View style={styles.container}>
{this.renderBtns()}
<TouchableOpacity
key={'OnlyVideos'}
onPress={() => {
this.props.initDocuments(this.props.locale, this.props.directories.global);
this.props.navigation.navigate('OnlyVideosScreen');
}}
style={styles.button}>
<Image source={{ uri: FileSystem.documentDirectory + 'HomeButtonImage.png' }} style={styles.btnImg} />
<Text style={styles.btnTxt}>{i18n.t('global.videos')}</Text>
</TouchableOpacity>
<TouchableOpacity key={'Contacts'} onPress={() => this.props.navigation.navigate('ContactScreen')} style={styles.button}>
<Image source={{ uri: FileSystem.documentDirectory + 'HomeButtonImage.png' }} style={styles.btnImg} />
<Text style={styles.btnTxt}>{i18n.t('global.contacts')}</Text>
</TouchableOpacity>
</View>
</ImageBackground>
);
}
renderBtns() {
let locale = this.props.locale;
let objBtns = this.props.btns;
let array = Object.keys(objBtns).map(function (key) { return objBtns[key]; });
return array.map((item, index) => {
let names = JSON.parse(item.name);
let name = (locale in names) ? names[locale] : names.fr;
return (
<TouchableOpacity key={index} onPress={() => this.openFolder(item.id)} style={styles.button}>
<Image source={{ uri: FileSystem.documentDirectory + 'HomeButtonImage.png' }} style={styles.btnImg} />
<Text style={styles.btnTxt}>{name}</Text>
</TouchableOpacity>
)
});
}
openFolder(id) {
this.props.initDocuments(this.props.locale, this.props.directories.global);
const folder = this.props.directories.treeview.filter(tmp => tmp.id === id);
this.props.navigation.navigate('DirectoriesScreen', { folder: folder });
}
openFolder(id) {
this.props.initDocuments(this.props.locale, this.props.directories.global);
const folder = this.props.directories.treeview.filter(tmp => tmp.id === id);
this.props.navigation.navigate('DirectoriesScreen', {folder: folder});
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);

View file

@ -1,191 +1,220 @@
import React from 'react';
import { ImageBackground, View, Image, Text, TextInput, Button, Alert } from 'react-native';
import {
Alert,
Button,
Image,
ImageBackground,
Text,
TextInput,
View,
} from 'react-native';
// Redux
import { connect } from 'react-redux';
// Sentry
import * as Sentry from "@sentry/react-native";
// SplashScreen
import * as Sentry from '@sentry/react-native';
// SplashScreen
import * as SplashScreen from 'expo-splash-screen';
// Helpers
import Helpers from '../Lib/Helpers';
// Lodash
import _ from 'lodash';
// Common Styles
// Common Styles
import commonStyles from '../../Resources/CommonStyles';
// Style
// Style
import styles from '../../Resources/Styles/InstallStyle';
// Translations
import i18n from "../../I18n";
import i18n from '../../I18n';
// Controller
import InstallController from '../Controllers/InstallController';
import SyncController from '../Controllers/SyncController';
// Database
import { User, Setting } from '../../Database';
// Database
import { Setting, User } from '../../Database';
// FileSytem
import * as FileSystem from 'expo-file-system';
function mapStateToProps(state) {
return {
isSetup: state.app.isSetup,
locale: state.locale.locale,
};
return {
isSetup: state.app.isSetup,
locale: state.locale.locale,
};
}
const mapDispatchToProps = dispatch => {
return {
setIsSetup: () => dispatch({ type: 'SET_ISSETUP' }),
init: () => dispatch({ type: 'INIT' })
};
return {
setIsSetup: () => dispatch({ type: 'SET_ISSETUP' }),
init: () => dispatch({ type: 'INIT' }),
};
};
class InstallScreen extends React.Component {
state = {
processing: false,
txtStep: '',
url: "",
}
state = {
processing: false,
txtStep: '',
url: '',
};
componentDidMount() {
SplashScreen.hideAsync();
}
componentDidMount() {
SplashScreen.hideAsync();
}
render() {
i18n.locale = this.props.locale;
return (
<ImageBackground source={require('../../Resources/assets/img/background_ldc.png')} style={commonStyles.background}>
<View style={styles.container}>
<Image source={require('../../Resources/assets/img/logo-square-ldc.png')} style={styles.logo} />
<View style={styles.actions}>
<TextInput
keyboardType='url'
autoCapitalize='none'
style={commonStyles.textInput}
placeholder={i18n.t("install.enter_url")}
onChangeText={(newText) => this.setState({ url: newText })}
value={this.state.url}
/>
<Button
disabled={this.state.processing}
color={'#D4021D'}
title={ i18n.t('install.install')}
onPress={() => {
if (this.state.url.length === 0) {
Alert.alert(i18n.t("alert.error"), i18n.t("install.enter_url"));
} else {
if (Helpers.isUrlValid(this.state.url)) {
this.install();
} else {
Alert.alert(i18n.t("alert.error"), i18n.t("install.url_invalid"));
}
}
}}
/>
</View>
<View style={styles.actions}>
<Text>
{this.state.txtStep}
</Text>
</View>
</View>
</ImageBackground>
);
}
async install() {
let url = this.state.url.toLowerCase();
Setting.upsert({ key: 'bo_url', value: url });
const isConnected = await SyncController.checkConnection().then(state => {
//console.log(state.isConnected)
return state.isConnected
}).catch(error => {
//console.log(error);
return false;
});
if (!isConnected) {
Alert.alert(
i18n.t('global.warning'),
i18n.t('error.must_be_connected_internet_sync')
);
return;
}
this.setState({ processing: true, txtStep: i18n.t("install.get_app_setting") });
const toDL = await InstallController.getConfig(url).then(response => {
let toDL = [];
if (response.data) {
if (response.data.status === "OK") {
_.forEach(response.data.datas, function (value, key) {
switch (key) {
case "MainColor":
case "AppStoreUUID":
case "FormSelect1":
case "FormSelect2":
case "FormCheck":
Setting.upsert({ key: key, value: value });
break;
case "SquaredLogo":
case "RectangleLogo":
case "BackgroundImage":
case "HomeButtonImage":
toDL.push({
source: url + "/" + value,
dest: FileSystem.documentDirectory + key + '.png'
});
break;
}
});
return toDL;
render() {
i18n.locale = this.props.locale;
return (
<ImageBackground
source={require('../../Resources/assets/img/background_ldc.png')}
style={commonStyles.background}>
<View style={styles.container}>
<Image
source={require('../../Resources/assets/img/logo-square-ldc.png')}
style={styles.logo}
/>
<View style={styles.actions}>
<TextInput
keyboardType="url"
autoCapitalize="none"
style={commonStyles.textInput}
placeholder={i18n.t('install.enter_url')}
onChangeText={newText => this.setState({ url: newText })}
value={this.state.url}
/>
<Button
disabled={this.state.processing}
color={'#D4021D'}
title={i18n.t('install.install')}
onPress={() => {
if (this.state.url.length === 0) {
Alert.alert(
i18n.t('alert.error'),
i18n.t('install.enter_url'),
);
} else {
Alert.alert(i18n.t("alert.error"), i18n.t("error.error_has_occurred"));
return false;
if (Helpers.isUrlValid(this.state.url)) {
this.install();
} else {
Alert.alert(
i18n.t('alert.error'),
i18n.t('install.url_invalid'),
);
}
}
}
}).catch(function (error) {
Sentry.captureException(error);
Alert.alert(i18n.t("alert.error"), i18n.t("error.error_has_occurred"));
return false;
});
if (!toDL) {
this.setState({ processing: false, txtStep: "" });
return false;
}
for await (const file of toDL) {
await FileSystem.downloadAsync(
file.source,
file.dest
)
.then(({ uri }) => {
console.log('Finished downloading to ', uri);
})
.catch(error => {
Alert.alert(i18n.t("alert.error"), i18n.t("install.resource_dl_error") + file.dest);
console.error(error);
return false;
});
}
}}
/>
</View>
<View style={styles.actions}>
<Text>{this.state.txtStep}</Text>
</View>
</View>
</ImageBackground>
);
}
this.setState({ txtStep: i18n.t("install.get_content_setting") });
if (InstallController.getAppConfig(url)) {
// getUsers
this.setState({ txtStep: i18n.t('sync_s.updating_users') + '...' });
const resUsers = await SyncController.getUsers().then(res => {
User.clear();
return res.data;
}).catch(error => {
console.log(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
return
});
resUsers.forEach(user => {
User.add(user);
});
this.props.init();
this.props.setIsSetup();
this.props.navigation.navigate('LoginScreen');
}
this.setState({ processing: false, txtStep: "" });
async install() {
let url = this.state.url.toLowerCase();
Setting.upsert({ key: 'bo_url', value: url });
const isConnected = await SyncController.checkConnection()
.then(state => {
//console.log(state.isConnected)
return state.isConnected;
})
.catch(error => {
//console.log(error);
return false;
});
if (!isConnected) {
Alert.alert(
i18n.t('global.warning'),
i18n.t('error.must_be_connected_internet_sync'),
);
return;
}
this.setState({
processing: true,
txtStep: i18n.t('install.get_app_setting'),
});
const toDL = await InstallController.getConfig(url)
.then(response => {
let toDL = [];
if (response.data) {
if (response.data.status === 'OK') {
_.forEach(response.data.datas, function (value, key) {
switch (key) {
case 'MainColor':
case 'AppStoreUUID':
case 'FormSelect1':
case 'FormSelect2':
case 'FormCheck':
Setting.upsert({ key: key, value: value });
break;
case 'SquaredLogo':
case 'RectangleLogo':
case 'BackgroundImage':
case 'HomeButtonImage':
toDL.push({
source: url + '/' + value,
dest: FileSystem.documentDirectory + key + '.png',
});
break;
}
});
return toDL;
} else {
Alert.alert(
i18n.t('alert.error'),
i18n.t('error.error_has_occurred'),
);
return false;
}
}
})
.catch(function (error) {
Sentry.captureException(error);
Alert.alert(i18n.t('alert.error'), i18n.t('error.error_has_occurred'));
return false;
});
if (!toDL) {
this.setState({ processing: false, txtStep: '' });
return false;
}
for await (const file of toDL) {
await FileSystem.downloadAsync(file.source, file.dest)
.then(({ uri }) => {
console.log('Finished downloading to ', uri);
})
.catch(error => {
Alert.alert(
i18n.t('alert.error'),
i18n.t('install.resource_dl_error') + file.dest,
);
console.error(error);
return false;
});
}
this.setState({ txtStep: i18n.t('install.get_content_setting') });
if (InstallController.getAppConfig(url)) {
// getUsers
this.setState({ txtStep: i18n.t('sync_s.updating_users') + '...' });
const resUsers = await SyncController.getUsers()
.then(res => {
User.clear();
return res.data;
})
.catch(error => {
console.log(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred'),
);
return;
});
resUsers.forEach(user => {
User.add(user);
});
this.props.init();
this.props.setIsSetup();
this.props.navigation.navigate('LoginScreen');
}
this.setState({ processing: false, txtStep: '' });
}
}
export default connect(mapStateToProps, mapDispatchToProps)(InstallScreen);
export default connect(mapStateToProps, mapDispatchToProps)(InstallScreen);

View file

@ -1,15 +1,22 @@
import React from 'react';
import { View, ImageBackground, TextInput, Button, Image, Alert } from 'react-native';
import {
Alert,
Button,
Image,
ImageBackground,
TextInput,
View,
} from 'react-native';
// Redux
import { connect } from 'react-redux';
// SplashScreen
// SplashScreen
import * as SplashScreen from 'expo-splash-screen';
// Common Styles
// Common Styles
import commonStyles from '../../Resources/CommonStyles';
// Style
import styles from '../../Resources/Styles/LoginStyle';
// Translations
import i18n from "../../I18n";
import i18n from '../../I18n';
// FileSytem
import * as FileSystem from 'expo-file-system';
// Controller
@ -19,133 +26,157 @@ import { Setting } from '../../Database';
import SyncController from '../Controllers/SyncController';
function mapStateToProps(state) {
return {
app: state.app,
users: state.users.users,
locale: state.locale.locale,
mainColor: state.app.mainColor
};
return {
app: state.app,
users: state.users.users,
locale: state.locale.locale,
mainColor: state.app.mainColor,
};
}
const mapDispatchToProps = dispatch => {
return {
setCurrentUser: () => dispatch({ type: 'SET_CURRENT_USER' }),
};
return {
setCurrentUser: () => dispatch({ type: 'SET_CURRENT_USER' }),
};
};
class LoginScreen extends React.Component {
state = {
login: '',
password: '',
};
state = {
login: '',
password: '',
};
componentDidMount() {
if (SyncController.isLogged()) {
this.props.setCurrentUser(false);
}
SplashScreen.hideAsync();
componentDidMount() {
if (SyncController.isLogged()) {
this.props.setCurrentUser(false);
}
SplashScreen.hideAsync();
}
render() {
return (
<ImageBackground source={{ uri: FileSystem.documentDirectory + 'BackgroundImage.png' }} style={commonStyles.background}>
<View style={styles.container}>
<Image source={{ uri: FileSystem.documentDirectory + 'SquaredLogo.png' }} style={styles.logo} />
<View>
<TextInput
style={styles.text}
placeholder={i18n.t('login_s.username')}
value={this.state.login}
onChangeText={text => this.setState({ login: text })}
autoCapitalize={'none'}
autoCorrect={false}
keyboardType={'email-address'}
/>
<TextInput
style={styles.text}
placeholder={i18n.t('login_s.password')}
value={this.state.password}
onChangeText={text => this.setState({ password: text })}
secureTextEntry={true}
autoCapitalize={'none'}
autoCorrect={false}
/>
<Button
title={i18n.t('global.login')}
onPress={() => this.login(this.state.login.toLowerCase(), this.state.password)}
color={this.props.mainColor}
/>
</View>
</View>
</ImageBackground >
render() {
return (
<ImageBackground
source={{ uri: FileSystem.documentDirectory + 'BackgroundImage.png' }}
style={commonStyles.background}>
<View style={styles.container}>
<Image
source={{ uri: FileSystem.documentDirectory + 'SquaredLogo.png' }}
style={styles.logo}
/>
<View>
<TextInput
style={styles.text}
placeholder={i18n.t('login_s.username')}
value={this.state.login}
onChangeText={text => this.setState({ login: text })}
autoCapitalize={'none'}
autoCorrect={false}
keyboardType={'email-address'}
/>
<TextInput
style={styles.text}
placeholder={i18n.t('login_s.password')}
value={this.state.password}
onChangeText={text => this.setState({ password: text })}
secureTextEntry={true}
autoCapitalize={'none'}
autoCorrect={false}
/>
<Button
title={i18n.t('global.login')}
onPress={() =>
this.login(this.state.login.toLowerCase(), this.state.password)
}
color={this.props.mainColor}
/>
</View>
</View>
</ImageBackground>
);
}
async login(login, password) {
if (login === '' || password === '') {
Alert.alert(
i18n.t('global.warning'),
i18n.t('error.must_fill_login_fields'),
);
return;
}
var usersSize = Object.keys(this.props.users).length;
if (usersSize === 0) {
Alert.alert(i18n.t('global.warning'), i18n.t('login_s.missing_users'));
return;
}
let user = this.props.users.filter(tmp => tmp.email === login);
if (user.length === 0) {
Alert.alert(i18n.t('error.error'), i18n.t('error.incorrect_credentials'));
return;
}
user = user[0];
const verified = LoginController.verify(user, password).then(
verified => verified,
);
if (!verified) {
Alert.alert(i18n.t('error.error'), i18n.t('error.incorrect_credentials'));
return;
}
const isConnected = await SyncController.checkConnection()
.then(state => {
//console.log(state.isConnected)
return state.isConnected;
})
.catch(error => {
//console.log(error);
return false;
});
const lastUser = Setting.get('lastUser');
// Si on est sur une installation
if (lastUser === false || lastUser == undefined) {
// Si la tablette est connectée à internet pour forcer la synchro
if (!isConnected) {
Alert.alert(
i18n.t('error.error'),
i18n.t('error.tablet_not_connected_internet'),
);
return;
}
return this.loginAction(user);
}
// Si on a deja une install
if (lastUser !== false && lastUser !== undefined) {
// Si on a une précédente connexion
if (lastUser.value === user.id.toString()) {
this.loginAction(user);
} else {
// Si le user precedent est different du user courant
// Si user !== last user && last user not synced
if (!SyncController.isSynced()) {
Alert.alert(
i18n.t('error.error'),
i18n.t('error.tablet_not_synced_last_user'),
);
return;
}
// Si la tablette est connectée à internet pour forcer la synchro
if (!isConnected) {
Alert.alert(
i18n.t('error.error'),
i18n.t('error.tablet_not_connected_internet'),
);
return;
}
SyncController.setInstalled(true);
this.loginAction(user, true);
}
}
}
async login(login, password) {
if (login === '' || password === '') {
Alert.alert(i18n.t("global.warning"), i18n.t('error.must_fill_login_fields'));
return;
}
var usersSize = Object.keys(this.props.users).length;
if (usersSize === 0) {
Alert.alert(i18n.t("global.warning"), i18n.t('login_s.missing_users'));
return;
}
let user = this.props.users.filter(tmp => tmp.email === login);
if (user.length === 0) {
Alert.alert(i18n.t('error.error'), i18n.t('error.incorrect_credentials'));
return;
}
user = user[0];
const verified = LoginController.verify(user, password).then(verified => verified);
if (!verified) {
Alert.alert(i18n.t('error.error'), i18n.t('error.incorrect_credentials'));
return;
}
const isConnected = await SyncController.checkConnection().then(state => {
//console.log(state.isConnected)
return state.isConnected
}).catch(error => {
//console.log(error);
return false;
});
const lastUser = Setting.get('lastUser');
// Si on est sur une installation
if (lastUser === false || lastUser == undefined) {
// Si la tablette est connectée à internet pour forcer la synchro
if (!isConnected) {
Alert.alert(i18n.t('error.error'), i18n.t('error.tablet_not_connected_internet'));
return;
}
return this.loginAction(user);
}
// Si on a deja une install
if (lastUser !== false && lastUser !== undefined) {
// Si on a une précédente connexion
if (lastUser.value === user.id.toString()) {
this.loginAction(user);
} else { // Si le user precedent est different du user courant
// Si user !== last user && last user not synced
if (!SyncController.isSynced()) {
Alert.alert(i18n.t('error.error'), i18n.t('error.tablet_not_synced_last_user'));
return;
}
// Si la tablette est connectée à internet pour forcer la synchro
if (!isConnected) {
Alert.alert(i18n.t('error.error'), i18n.t('error.tablet_not_connected_internet'));
return;
}
SyncController.setInstalled(true);
this.loginAction(user, true);
}
}
}
loginAction(user) {
this.setState({ login: '', password: '' });
LoginController.login(user);
this.props.setCurrentUser();
}
loginAction(user) {
this.setState({ login: '', password: '' });
LoginController.login(user);
this.props.setCurrentUser();
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);

View file

@ -1,28 +1,24 @@
import React from 'react';
import {
View,
Text,
} from 'react-native';
// Redux
import { connect } from 'react-redux';
// Style
import styles from '../../Resources/Styles/MySpaceStyle';
function mapStateToProps(state) {
return {
documents: state.app.documents,
};
}
class MySpaceScreen extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Mon Espace</Text>
</View>
);
}
}
export default connect(mapStateToProps)(MySpaceScreen);
import React from 'react';
import { Text, View } from 'react-native';
// Redux
import { connect } from 'react-redux';
// Style
import styles from '../../Resources/Styles/MySpaceStyle';
function mapStateToProps(state) {
return {
documents: state.app.documents,
};
}
class MySpaceScreen extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Mon Espace</Text>
</View>
);
}
}
export default connect(mapStateToProps)(MySpaceScreen);

View file

@ -1,61 +1,63 @@
import React from 'react';
import {
View,
ScrollView,
TouchableOpacity,
Text,
} from 'react-native';
// Redux
import { connect } from 'react-redux';
// Icons
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
// Style
import styles from '../../Resources/Styles/OnlyVideosStyle';
function mapStateToProps(state) {
return {
documents: state.documents,
mainColor: state.app.mainColor
};
}
class OnlyVideosScreen extends React.Component {
state = {
documents: [],
};
componentDidMount() {
this.setState({ documents: this.props.documents.filter(tmp => tmp.type === 'mp4') });
}
render() {
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{ ...styles.file, backgroundColor: this.props.mainColor }}
onPress={() => {
this.props.dispatch({ type: 'SET_CURRENT_OPENED_DOC', data: document });
this.props.navigation.navigate('VideoScreen', { uuid: document.uuid });
}}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
</ScrollView>
</View>
);
}
}
export default connect(mapStateToProps)(OnlyVideosScreen);
import React from 'react';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
// Redux
import { connect } from 'react-redux';
// Icons
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
// Style
import styles from '../../Resources/Styles/OnlyVideosStyle';
function mapStateToProps(state) {
return {
documents: state.documents,
mainColor: state.app.mainColor,
};
}
class OnlyVideosScreen extends React.Component {
state = {
documents: [],
};
componentDidMount() {
this.setState({
documents: this.props.documents.filter(tmp => tmp.type === 'mp4'),
});
}
render() {
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{ ...styles.file, backgroundColor: this.props.mainColor }}
onPress={() => {
this.props.dispatch({
type: 'SET_CURRENT_OPENED_DOC',
data: document,
});
this.props.navigation.navigate('VideoScreen', {
uuid: document.uuid,
});
}}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
</ScrollView>
</View>
);
}
}
export default connect(mapStateToProps)(OnlyVideosScreen);

View file

@ -8,33 +8,33 @@ import * as FileSystem from 'expo-file-system';
import styles from '../../Resources/Styles/PDFStyle';
class PDFScreen extends React.Component {
render() {
const { uuid } = this.props.route.params;
const source = {
uri: FileSystem.documentDirectory + uuid + '.pdf',
cache: false,
};
return (
<View style={styles.container}>
<Pdf
source={source}
onLoadComplete={(numberOfPages, filePath) => {
//console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
//console.log(`current page: ${page}`);
}}
onError={error => {
console.log(error);
}}
onPressLink={uri => {
//console.log(`Link presse: ${uri}`);
}}
style={styles.pdf}
/>
</View>
);
}
render() {
const { uuid } = this.props.route.params;
const source = {
uri: FileSystem.documentDirectory + uuid + '.pdf',
cache: false,
};
return (
<View style={styles.container}>
<Pdf
source={source}
onLoadComplete={(numberOfPages, filePath) => {
//console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
//console.log(`current page: ${page}`);
}}
onError={error => {
console.log(error);
}}
onPressLink={uri => {
//console.log(`Link presse: ${uri}`);
}}
style={styles.pdf}
/>
</View>
);
}
}
export default PDFScreen;

View file

@ -1,86 +1,87 @@
import React from 'react';
import {
View,
ScrollView,
TouchableOpacity,
Text,
} from 'react-native';
// Reduxs
import { connect } from 'react-redux';
// Helpers
import Helpers from "../Lib/Helpers";
// Translations
import i18n from "../../I18n";
// Icons
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
// Style
import styles from '../../Resources/Styles/SearchStyle';
function mapStateToProps(state) {
return {
documents: state.documents,
mainColor: state.app.mainColor
};
}
class SearchScreen extends React.Component {
state = {
documents: [],
};
openDocument(document) {
this.props.dispatch({ type: 'SET_CURRENT_OPENED_DOC', data: document });
if (document.type === 'pdf') {
this.props.navigation.navigate('PDFScreen', { uuid: document.uuid });
} else if (document.type === 'mp4') {
this.props.navigation.navigate('VideoScreen', { uuid: document.uuid });
} else {
console.log('Inconnu');
}
}
componentDidMount() {
const { needle, onlyVideos } = this.props.route.params;
let objDocs = this.props.documents;
let arrayDocs = Object.keys(objDocs).map(function (key) { return objDocs[key]; });
let sorted = arrayDocs.sort(Helpers.compareValues('title', 'asc'));
let filtered = sorted.filter(item => item.title.toLowerCase().indexOf(needle.toLowerCase()) >= 0);
if (onlyVideos === true) {
filtered = filtered.filter(item => item.type === 'mp4');
}
this.setState({
documents: filtered,
});
}
render() {
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{ ...styles.file, backgroundColor: this.props.mainColor }}
onPress={() => this.openDocument(document)}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
{this.state.documents.length === 0 && (
<Text style={{ textAlign: 'center', marginTop: 50, fontSize: 30 }}>{i18n.t('search_s.no_result')}</Text>
)}
</ScrollView>
</View>
);
}
}
export default connect(mapStateToProps)(SearchScreen);
import React from 'react';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
// Reduxs
import { connect } from 'react-redux';
// Helpers
import Helpers from '../Lib/Helpers';
// Translations
import i18n from '../../I18n';
// Icons
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
// Style
import styles from '../../Resources/Styles/SearchStyle';
function mapStateToProps(state) {
return {
documents: state.documents,
mainColor: state.app.mainColor,
};
}
class SearchScreen extends React.Component {
state = {
documents: [],
};
openDocument(document) {
this.props.dispatch({ type: 'SET_CURRENT_OPENED_DOC', data: document });
if (document.type === 'pdf') {
this.props.navigation.navigate('PDFScreen', { uuid: document.uuid });
} else if (document.type === 'mp4') {
this.props.navigation.navigate('VideoScreen', { uuid: document.uuid });
} else {
console.log('Inconnu');
}
}
componentDidMount() {
const { needle, onlyVideos } = this.props.route.params;
let objDocs = this.props.documents;
let arrayDocs = Object.keys(objDocs).map(function (key) {
return objDocs[key];
});
let sorted = arrayDocs.sort(Helpers.compareValues('title', 'asc'));
let filtered = sorted.filter(
item => item.title.toLowerCase().indexOf(needle.toLowerCase()) >= 0,
);
if (onlyVideos === true) {
filtered = filtered.filter(item => item.type === 'mp4');
}
this.setState({
documents: filtered,
});
}
render() {
let documentList = this.state.documents.map(document => {
return (
<TouchableOpacity
key={document.id.toString()}
style={{ ...styles.file, backgroundColor: this.props.mainColor }}
onPress={() => this.openDocument(document)}>
{document.type === 'pdf' ? (
<FontAwesome5 name="file-pdf" color={'white'} size={60} />
) : (
<FontAwesome5 name="file-video" color={'white'} size={60} />
)}
<Text style={styles.fileTxt}>{document.title}</Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
<ScrollView style={styles.scrollview}>
<View style={styles.filesContainer}>{documentList}</View>
{this.state.documents.length === 0 && (
<Text style={{ textAlign: 'center', marginTop: 50, fontSize: 30 }}>
{i18n.t('search_s.no_result')}
</Text>
)}
</ScrollView>
</View>
);
}
}
export default connect(mapStateToProps)(SearchScreen);

View file

@ -1,379 +1,423 @@
import React from 'react';
import { View, Button, Alert, Text } from 'react-native';
import { Alert, Button, Text, View } from 'react-native';
// Redux
import { connect } from 'react-redux';
import { initDirectories } from '../Store/Actions/directories';
import { initContact } from '../Store/Actions/contacts';
import { initDirectories } from '../Store/Actions/directories';
import { initDocuments } from '../Store/Actions/documents';
// Sentry
import * as Sentry from "@sentry/react-native";
import * as Sentry from '@sentry/react-native';
// Style
import styles from '../../Resources/Styles/SyncStyle';
// Translations
import i18n from "../../I18n";
// Controller
import i18n from '../../I18n';
// Controller
import SyncController from '../Controllers/SyncController';
// Progress
import * as Progress from 'react-native-progress';
// Database
import { User, Contact, Directory, Document, SendBrochure } from '../../Database';
// Database
import {
Contact,
Directory,
Document,
SendBrochure,
User,
} from '../../Database';
// FileSytem
import * as FileSystem from 'expo-file-system';
function mapStateToProps(state) {
return {
syncVersion: state.app.syncVersion,
currentUser: state.app.currentUser,
locale: state.locale.locale,
mainColor: state.app.mainColor,
directories: state.directories
};
return {
syncVersion: state.app.syncVersion,
currentUser: state.app.currentUser,
locale: state.locale.locale,
mainColor: state.app.mainColor,
directories: state.directories,
};
}
const mapDispatchToProps = dispatch => {
return {
init: () => dispatch({ type: 'INIT' }),
initUsers: () => dispatch({ type: 'INIT_USERS' }),
initDirectories: (lang, user) => dispatch(initDirectories(lang, user)),
initDocuments: (lang, directories) => dispatch(initDocuments(lang, directories)),
initContact: currentUser => dispatch(initContact(currentUser)),
initBtns: () => dispatch({ type: 'INIT_HOME_BTNS' }),
updateVersion: version => dispatch({
type: 'UPDATE_VERSION',
data: version,
}),
updateShowIsActive: value => dispatch({
type: 'UPDATE_SHOW_IS_ACTIVE',
data: value,
})
};
return {
init: () => dispatch({ type: 'INIT' }),
initUsers: () => dispatch({ type: 'INIT_USERS' }),
initDirectories: (lang, user) => dispatch(initDirectories(lang, user)),
initDocuments: (lang, directories) =>
dispatch(initDocuments(lang, directories)),
initContact: currentUser => dispatch(initContact(currentUser)),
initBtns: () => dispatch({ type: 'INIT_HOME_BTNS' }),
updateVersion: version =>
dispatch({
type: 'UPDATE_VERSION',
data: version,
}),
updateShowIsActive: value =>
dispatch({
type: 'UPDATE_SHOW_IS_ACTIVE',
data: value,
}),
};
};
class SyncScreen extends React.Component {
state = {
progress: 0,
txtStep: '',
downloadProgress: 0,
downloadingFile: false,
processing: false,
newApp: false,
forced: false
};
state = {
progress: 0,
txtStep: '',
downloadProgress: 0,
downloadingFile: false,
processing: false,
newApp: false,
forced: false,
};
componentDidMount() {
const { forceSync } = this.props.route.params;
if (forceSync === true) {
this.setState({ forced: true });
this.sync();
}
componentDidMount() {
const { forceSync } = this.props.route.params;
if (forceSync === true) {
this.setState({ forced: true });
this.sync();
}
}
render() {
const fileProgress = this.fileProgress();
return (
<View style={styles.container}>
<Progress.Circle
size={250}
progress={this.state.progress}
showsText={true}
color={this.props.mainColor}
/>
<View style={styles.btnWrapper}>
<Button
disabled={this.state.processing}
color={this.props.mainColor}
title={i18n.t('sync_s.start_sync')}
onPress={() => this.sync()}
/>
</View>
<View style={styles.btnWrapper}>
<Text>
{this.state.txtStep} {fileProgress}
</Text>
</View>
</View>
);
render() {
const fileProgress = this.fileProgress();
return (
<View style={styles.container}>
<Progress.Circle
size={250}
progress={this.state.progress}
showsText={true}
color={this.props.mainColor}
/>
<View style={styles.btnWrapper}>
<Button
disabled={this.state.processing}
color={this.props.mainColor}
title={i18n.t('sync_s.start_sync')}
onPress={() => this.sync()}
/>
</View>
<View style={styles.btnWrapper}>
<Text>
{this.state.txtStep} {fileProgress}
</Text>
</View>
</View>
);
}
async sync() {
let errorStatus = false;
const isConnected = await SyncController.checkConnection()
.then(state => {
//console.log(state.isConnected)
return state.isConnected;
})
.catch(error => {
//console.log(error);
return false;
});
if (!isConnected) {
Alert.alert(
i18n.t('global.warning'),
i18n.t('error.must_be_connected_internet_sync'),
);
return;
}
this.setState({ processing: true });
// getVersion
const resVersion = await SyncController.getVersion()
.then(res => {
return res.data;
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(i18n.t('error.error'), i18n.t('error.error_has_occurred'));
errorStatus = true;
});
if (errorStatus) {
return false;
}
this.props.updateVersion(resVersion.version);
this.props.updateShowIsActive(resVersion.ShowIsActive);
this.setState({ newApp: resVersion.newApp });
// getUsers
this.setState({ txtStep: i18n.t('sync_s.updating_users') + '...' });
const resUsers = await SyncController.getUsers()
.then(res => {
User.clear();
return res.data;
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(i18n.t('error.error'), i18n.t('error.error_has_occurred'));
errorStatus = true;
});
if (errorStatus) {
return false;
}
resUsers.forEach(user => {
User.add(user);
});
// getContacts
this.setState({
progress: 0.1,
txtStep: i18n.t('sync_s.updating_contacts') + '...',
});
const resContacts = await SyncController.getContacts(
this.props.currentUser.id,
)
.then(res => {
Contact.clear();
return res.data;
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(i18n.t('error.error'), i18n.t('error.error_has_occurred'));
errorStatus = true;
});
if (errorStatus) {
return false;
}
resContacts.forEach(contact => {
Contact.add(contact);
});
//sendBrochures
this.setState({
progress: 0.2,
txtStep: i18n.t('sync_s.sending_flyers') + '...',
});
let brochures = SendBrochure.getAll();
for (const brochure of brochures) {
await SyncController.sendBrochure(brochure)
.then(res => {
SendBrochure.delete(brochure.id);
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred') + ' : Brochures',
);
errorStatus = true;
});
}
if (errorStatus) {
return false;
}
//getDirectories
this.setState({
progress: 0.3,
txtStep: i18n.t('sync_s.updating_folders') + '...',
});
const resDirectories = await SyncController.getDirectories()
.then(res => {
Directory.clear();
return res.data;
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(i18n.t('error.error'), i18n.t('error.error_has_occurred'));
errorStatus = true;
});
if (errorStatus) {
return false;
}
resDirectories.forEach(dir => {
Directory.add(dir);
});
// getDocuments
this.setState({
progress: 0.4,
txtStep: i18n.t('sync_s.updating_docs') + '...',
});
const resDocuments = await SyncController.getDocuments()
.then(res => {
return res.data;
})
.catch(error => {
Sentry.captureException(error);
Alert.alert(i18n.t('error.error'), i18n.t('error.error_has_occurred'));
errorStatus = true;
});
if (errorStatus) {
return false;
}
const toDelete = Document.clear(resDocuments);
resDocuments.forEach(document => {
Document.update(document);
});
this.downloadDocuments(Document.getAll(), toDelete);
// End
this.setState({
txtStep: i18n.t('sync_s.update_completed') + '.',
processing: false,
});
}
async sync() {
let errorStatus = false;
const isConnected = await SyncController.checkConnection().then(state => {
//console.log(state.isConnected)
return state.isConnected
}).catch(error => {
//console.log(error);
return false;
});
if (!isConnected) {
Alert.alert(
i18n.t('global.warning'),
i18n.t('error.must_be_connected_internet_sync')
);
return;
}
this.setState({ processing: true });
// getVersion
const resVersion = await SyncController.getVersion().then(res => {
return res.data;
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
errorStatus = true
});
if (errorStatus) {
return false;
}
this.props.updateVersion(resVersion.version);
this.props.updateShowIsActive(resVersion.ShowIsActive);
this.setState({ newApp: resVersion.newApp });
// getUsers
this.setState({ txtStep: i18n.t('sync_s.updating_users') + '...' });
const resUsers = await SyncController.getUsers().then(res => {
User.clear();
return res.data;
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
errorStatus = true
});
if (errorStatus) {
return false;
}
resUsers.forEach(user => {
User.add(user);
});
// getContacts
this.setState({ progress: 0.1, txtStep: i18n.t('sync_s.updating_contacts') + '...' });
const resContacts = await SyncController.getContacts(this.props.currentUser.id).then(res => {
Contact.clear();
return res.data;
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
errorStatus = true
});
if (errorStatus) {
return false;
}
resContacts.forEach(contact => {
Contact.add(contact);
});
//sendBrochures
this.setState({ progress: 0.2, txtStep: i18n.t('sync_s.sending_flyers') + '...' });
let brochures = SendBrochure.getAll();
for (const brochure of brochures) {
await SyncController.sendBrochure(brochure).then(res => {
SendBrochure.delete(brochure.id);
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred') + ' : Brochures'
);
errorStatus = true;
fileProgress() {
if (this.state.downloadingFile === true) {
return this.state.downloadProgress + ' %';
}
}
downloadDocuments(docs, toDelete) {
const version = this.props.syncVersion;
this.getToDL(docs, 0).then(toDownload => {
if (toDownload.length > 0) {
this.setState({ progress: 0.5 });
const docProgress = 0.5 / toDownload.length;
this.download(toDownload, docProgress, 0).then(status => {
if (status === true) {
this.deleteFile(toDelete, 0).then(status => {
this.finishSync(version);
});
}
if (errorStatus) {
return false;
}
//getDirectories
this.setState({ progress: 0.3, txtStep: i18n.t('sync_s.updating_folders') + '...' });
const resDirectories = await SyncController.getDirectories().then(res => {
Directory.clear();
return res.data;
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
errorStatus = true
} else {
this.setState({
txtStep: i18n.t('error.error_has_occurred') + '.',
});
}
});
if (errorStatus) {
return false;
}
resDirectories.forEach(dir => {
Directory.add(dir);
} else {
this.deleteFile(toDelete, 0).then(status => {
this.setState({ progress: 1 });
this.finishSync(version);
});
// getDocuments
this.setState({ progress: 0.4, txtStep: i18n.t('sync_s.updating_docs') + '...' });
const resDocuments = await SyncController.getDocuments().then(res => {
return res.data;
}).catch(error => {
Sentry.captureException(error);
Alert.alert(
i18n.t('error.error'),
i18n.t('error.error_has_occurred')
);
errorStatus = true
});
if (errorStatus) {
return false;
}
const toDelete = Document.clear(resDocuments);
resDocuments.forEach(document => {
Document.update(document);
});
this.downloadDocuments(Document.getAll(), toDelete);
// End
this.setState({ txtStep: i18n.t('sync_s.update_completed') + '.', processing: false });
}
}
});
}
fileProgress() {
if (this.state.downloadingFile === true) {
return this.state.downloadProgress + ' %';
}
async getToDL(docs, i, toDL = []) {
if (docs.length < 1) {
return toDL;
}
downloadDocuments(docs, toDelete) {
const version = this.props.syncVersion;
this.getToDL(docs, 0).then(toDownload => {
if (toDownload.length > 0) {
this.setState({ progress: 0.5 });
const docProgress = 0.5 / toDownload.length;
this.download(toDownload, docProgress, 0).then(status => {
if (status === true) {
this.deleteFile(toDelete, 0).then(status => {
this.finishSync(version);
});
} else {
this.setState({ txtStep: i18n.t('error.error_has_occurred') + '.' });
}
});
} else {
this.deleteFile(toDelete, 0).then(status => {
this.setState({ progress: 1 });
this.finishSync(version);
});
}
});
const item = docs[i];
const { exists, modificationTime } = await FileSystem.getInfoAsync(
FileSystem.documentDirectory + item.uuid + '.' + item.type,
);
if (exists === false) {
toDL.push(item);
} else {
if (modificationTime.toString() !== item.updated_at) {
toDL.push(item);
}
}
async getToDL(docs, i, toDL = []) {
if (docs.length < 1)
return toDL;
const item = docs[i];
const { exists, modificationTime } = await FileSystem.getInfoAsync(FileSystem.documentDirectory + item.uuid + '.' + item.type);
if (exists === false) {
toDL.push(item);
} else {
if (modificationTime.toString() !== item.updated_at) {
toDL.push(item);
}
}
if (i < docs.length - 1) {
return this.getToDL(docs, i + 1, toDL);
}
return toDL;
if (i < docs.length - 1) {
return this.getToDL(docs, i + 1, toDL);
}
return toDL;
}
async deleteFile(docs, i) {
if (docs.length > 0) {
const file = docs[i];
await FileSystem.deleteAsync(FileSystem.documentDirectory + file.uuid + '.' + file.type, { idempotent: true });
if (i < docs.length - 1) {
return this.deleteFile(docs, i + 1);
}
}
return true;
async deleteFile(docs, i) {
if (docs.length > 0) {
const file = docs[i];
await FileSystem.deleteAsync(
FileSystem.documentDirectory + file.uuid + '.' + file.type,
{ idempotent: true },
);
if (i < docs.length - 1) {
return this.deleteFile(docs, i + 1);
}
}
return true;
}
callback(downloadProgress) {
let progress =
(downloadProgress.totalBytesWritten /
downloadProgress.totalBytesExpectedToWrite) *
100;
progress = Math.round(progress);
progress = progress.toString();
this.setState({
downloadProgress: progress,
});
callback(downloadProgress) {
let progress =
(downloadProgress.totalBytesWritten /
downloadProgress.totalBytesExpectedToWrite) *
100;
progress = Math.round(progress);
progress = progress.toString();
this.setState({
downloadProgress: progress,
});
}
downloadResumable(url, uuid, type) {
return FileSystem.createDownloadResumable(
url,
FileSystem.documentDirectory + uuid + '.' + type,
{},
this.callback.bind(this),
);
}
async download(documents, progress, i) {
if (this.state.downloadingFile === false) {
this.setState({ downloadingFile: true });
}
downloadResumable(url, uuid, type) {
return FileSystem.createDownloadResumable(
url,
FileSystem.documentDirectory + uuid + '.' + type,
{},
this.callback.bind(this),
);
const endpoint = SyncController.getUrlBo() + '/storage/docs/';
const doc = documents[i];
this.setState({
txtStep:
i18n.t('sync_s.download') +
' : ' +
doc.title +
' (' +
doc.type.toUpperCase() +
') ...',
});
try {
await this.downloadResumable(
endpoint + doc.uuid + '.' + doc.type,
doc.uuid,
doc.type,
).downloadAsync();
const { modificationTime } = await FileSystem.getInfoAsync(
FileSystem.documentDirectory + doc.uuid + '.' + doc.type,
);
SyncController.updateLastModificationDoc(doc.id, modificationTime);
//console.log('Finished downloading to ', uri);
this.setState({ progress: this.state.progress + progress });
if (i < documents.length - 1) {
return this.download(documents, progress, i + 1);
}
this.setState({ downloadingFile: false });
return true;
} catch (e) {
console.error(e);
return false;
}
}
async download(documents, progress, i) {
if (this.state.downloadingFile === false) {
this.setState({ downloadingFile: true });
}
const endpoint = SyncController.getUrlBo() + '/storage/docs/'
const doc = documents[i];
this.setState({
txtStep:
i18n.t('sync_s.download') + ' : ' +
doc.title +
' (' +
doc.type.toUpperCase() +
') ...',
});
try {
await this.downloadResumable(
endpoint + doc.uuid + "." + doc.type,
doc.uuid,
doc.type,
).downloadAsync();
const { modificationTime } = await FileSystem.getInfoAsync(FileSystem.documentDirectory + doc.uuid + '.' + doc.type);
SyncController.updateLastModificationDoc(doc.id, modificationTime);
//console.log('Finished downloading to ', uri);
this.setState({ progress: this.state.progress + progress });
if (i < documents.length - 1) {
return this.download(documents, progress, i + 1);
}
this.setState({ downloadingFile: false });
return true;
} catch (e) {
console.error(e);
return false;
}
finishSync(version) {
this.props.init();
this.props.initUsers();
this.props.initContact(this.props.currentUser);
this.props.initDirectories(this.props.locale, this.props.currentUser);
this.props.initDocuments(this.props.locale, this.props.directories.global);
this.props.initBtns();
if (this.state.forced) {
SyncController.setInstalled();
}
finishSync(version) {
this.props.init();
this.props.initUsers();
this.props.initContact(this.props.currentUser);
this.props.initDirectories(this.props.locale, this.props.currentUser);
this.props.initDocuments(this.props.locale, this.props.directories.global);
this.props.initBtns();
if (this.state.forced) {
SyncController.setInstalled();
}
SyncController.updateVersion(version);
this.setState({ txtStep: i18n.t('sync_s.update_completed') + '.', processing: false });
if (this.state.newApp === true) {
const appUUID = SyncController.getAppStoreUUID();
const url = 'https://store.ldc.fr' + '/app/' + appUUID;
Alert.alert(
'LDC Store',
i18n.t('sync_s.new_app_version'),
[
{
text: i18n.t('global.cancel'),
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{ text: i18n.t('sync_s.open_store'), onPress: () => Linking.openURL(url).catch((err) => console.error('An error occurred', err)) },
],
{ cancelable: false },
);
}
SyncController.updateVersion(version);
this.setState({
txtStep: i18n.t('sync_s.update_completed') + '.',
processing: false,
});
if (this.state.newApp === true) {
const appUUID = SyncController.getAppStoreUUID();
const url = 'https://store.ldc.fr' + '/app/' + appUUID;
Alert.alert(
'LDC Store',
i18n.t('sync_s.new_app_version'),
[
{
text: i18n.t('global.cancel'),
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{
text: i18n.t('sync_s.open_store'),
onPress: () =>
Linking.openURL(url).catch(err =>
console.error('An error occurred', err),
),
},
],
{ cancelable: false },
);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SyncScreen);
export default connect(mapStateToProps, mapDispatchToProps)(SyncScreen);

View file

@ -1,5 +1,5 @@
import React from 'react';
import { View, Dimensions } from 'react-native';
import { Dimensions, View } from 'react-native';
// Reduxs
import { connect } from 'react-redux';
// FileSytem
@ -9,51 +9,51 @@ import { ResizeMode } from 'expo-av';
import VideoPlayer from 'expo-video-player';
function mapStateToProps(state) {
return {
mainColor: state.app.mainColor
};
return {
mainColor: state.app.mainColor,
};
}
class VideoScreen extends React.Component {
render() {
const { uuid } = this.props.route.params;
let width = Dimensions.get("window").width;
let height = Dimensions.get("window").height;
render() {
const { uuid } = this.props.route.params;
let width = Dimensions.get('window').width;
let height = Dimensions.get('window').height;
// if portrait
if (height > width) {
let tmpHeight = height;
height = width - 150;
width = tmpHeight + 100;
} else {
height = height - 100;
}
return (
<View style={{ flex: 1, backgroundColor: 'black' }}>
<VideoPlayer
defaultControlsVisible={true}
fullscreen={{
visible: false
}}
slider={{
minimumTrackTintColor: this.props.mainColor,
thumbTintColor: this.props.mainColor,
}}
style={{
height: height,
width: width
}}
videoProps={{
shouldPlay: true,
resizeMode: ResizeMode.CONTAIN,
source: {
uri: FileSystem.documentDirectory + uuid + '.mp4',
},
}}
/>
</View>
);
// if portrait
if (height > width) {
let tmpHeight = height;
height = width - 150;
width = tmpHeight + 100;
} else {
height = height - 100;
}
return (
<View style={{ flex: 1, backgroundColor: 'black' }}>
<VideoPlayer
defaultControlsVisible={true}
fullscreen={{
visible: false,
}}
slider={{
minimumTrackTintColor: this.props.mainColor,
thumbTintColor: this.props.mainColor,
}}
style={{
height: height,
width: width,
}}
videoProps={{
shouldPlay: true,
resizeMode: ResizeMode.CONTAIN,
source: {
uri: FileSystem.documentDirectory + uuid + '.mp4',
},
}}
/>
</View>
);
}
}
export default connect(mapStateToProps)(VideoScreen);
export default connect(mapStateToProps)(VideoScreen);

View file

@ -1,29 +1,31 @@
import Realm from "realm";
import ContactModel, { ContactsSchema } from "./App/Models/Contact";
import DirectoryModel, { DirectoriesSchema } from "./App/Models/Directory";
import DocumentModel, { DocumentsSchema } from "./App/Models/Document";
import SendBrochureModel, { SendBrochuresSchema } from "./App/Models/SendBrochure";
import SettingModel, { SettingsSchema } from "./App/Models/Setting";
import UserModel, { UsersSchema } from "./App/Models/User";
const Database = {
schema: [
ContactsSchema,
DirectoriesSchema,
DocumentsSchema,
SendBrochuresSchema,
SettingsSchema,
UsersSchema
],
//encryptionKey: new Int8Array(64)
};
let realm = new Realm(Database);
export let Contact = new ContactModel(realm);
export let Directory = new DirectoryModel(realm);
export let Document = new DocumentModel(realm);
export let SendBrochure = new SendBrochureModel(realm);
export let Setting = new SettingModel(realm);
export let User = new UserModel(realm);
import Realm from 'realm';
import ContactModel, { ContactsSchema } from './App/Models/Contact';
import DirectoryModel, { DirectoriesSchema } from './App/Models/Directory';
import DocumentModel, { DocumentsSchema } from './App/Models/Document';
import SendBrochureModel, {
SendBrochuresSchema,
} from './App/Models/SendBrochure';
import SettingModel, { SettingsSchema } from './App/Models/Setting';
import UserModel, { UsersSchema } from './App/Models/User';
const Database = {
schema: [
ContactsSchema,
DirectoriesSchema,
DocumentsSchema,
SendBrochuresSchema,
SettingsSchema,
UsersSchema,
],
//encryptionKey: new Int8Array(64)
};
let realm = new Realm(Database);
export let Contact = new ContactModel(realm);
export let Directory = new DirectoryModel(realm);
export let Document = new DocumentModel(realm);
export let SendBrochure = new SendBrochureModel(realm);
export let Setting = new SettingModel(realm);
export let User = new UserModel(realm);

View file

@ -1,17 +1,17 @@
import { I18n } from 'i18n-js';
import fr from './Resources/Locales/fr';
import en from './Resources/Locales/en';
import de from './Resources/Locales/de';
const i18n = new I18n({
fr: fr,
en: en,
de: de
});
i18n.defaultLocale = 'fr';
i18n.locale = 'fr';
i18n.enableFallback = true;
export default i18n;
import { I18n } from 'i18n-js';
import de from './Resources/Locales/de';
import en from './Resources/Locales/en';
import fr from './Resources/Locales/fr';
const i18n = new I18n({
fr: fr,
en: en,
de: de,
});
i18n.defaultLocale = 'fr';
i18n.locale = 'fr';
i18n.enableFallback = true;
export default i18n;

View file

@ -1,9 +1,9 @@
import { createNavigationContainerRef } from '@react-navigation/native';
export const navigationRef = createNavigationContainerRef()
export const navigationRef = createNavigationContainerRef();
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}

View file

@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
const commonStyles = StyleSheet.create({
background: {
flex: 1,
justifyContent: "center"
justifyContent: 'center',
},
textInput: {
height: 40,
@ -12,8 +12,8 @@ const commonStyles = StyleSheet.create({
borderWidth: 1,
marginBottom: 20,
backgroundColor: 'white',
paddingLeft: 10
}
paddingLeft: 10,
},
});
export default commonStyles;
export default commonStyles;

View file

@ -1,43 +1,43 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
input: {
backgroundColor: 'white',
margin: 10,
padding: 10,
},
clearCheckbox: {
backgroundColor: '#00000000',
borderWidth: 0,
},
clearRadio: {
flex: 1,
backgroundColor: '#00000000',
borderWidth: 0,
},
select: {
flex: 2,
margin: 10,
backgroundColor: 'white',
},
textArea: {
flex: 1,
margin: 10,
padding: 10,
backgroundColor: 'white',
textAlignVertical: 'top',
},
button: {
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
margin: 2,
},
container: {
flex: 1,
flexDirection: 'row',
},
input: {
backgroundColor: 'white',
margin: 10,
padding: 10,
},
clearCheckbox: {
backgroundColor: '#00000000',
borderWidth: 0,
},
clearRadio: {
flex: 1,
backgroundColor: '#00000000',
borderWidth: 0,
},
select: {
flex: 2,
margin: 10,
backgroundColor: 'white',
},
textArea: {
flex: 1,
margin: 10,
padding: 10,
backgroundColor: 'white',
textAlignVertical: 'top',
},
button: {
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
margin: 2,
},
});
export default styles;
export default styles;

View file

@ -1,74 +1,74 @@
import { StyleSheet, Dimensions } from 'react-native';
import {Dimensions, StyleSheet} from 'react-native';
const { width } = Dimensions.get('window');
const {width} = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
item: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: 1,
padding: 15,
marginHorizontal: 15,
},
clickable: {
textAlign: 'center',
fontSize: 18,
width: width / 4,
fontWeight: 'bold'
},
company: {
textAlign: 'center',
fontSize: 15,
width: width / 4,
},
email: {
textAlign: 'center',
fontSize: 15,
width: width / 4,
},
buttons: {
justifyContent: 'center',
flexDirection: 'row',
fontSize: 15,
width: width / 4,
},
button: {
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
margin: 2,
},
txtSendBrochure: {
borderWidth: 1,
borderColor: 'lightgrey',
width: 280,
marginBottom: 5,
paddingLeft: 10,
},
textAreaSendBrochure: {
borderWidth: 1,
borderColor: 'lightgrey',
width: 280,
height: 150,
marginBottom: 5,
paddingLeft: 10,
textAlignVertical: 'top',
},
inputSearch: {
backgroundColor: 'white',
width: 200,
paddingLeft: 20
},
btnSearch: {
width: 50,
alignItems: 'center',
justifyContent: 'center'
}
container: {
flex: 1,
flexDirection: 'column',
},
item: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: 1,
padding: 15,
marginHorizontal: 15,
},
clickable: {
textAlign: 'center',
fontSize: 18,
width: width / 4,
fontWeight: 'bold',
},
company: {
textAlign: 'center',
fontSize: 15,
width: width / 4,
},
email: {
textAlign: 'center',
fontSize: 15,
width: width / 4,
},
buttons: {
justifyContent: 'center',
flexDirection: 'row',
fontSize: 15,
width: width / 4,
},
button: {
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
margin: 2,
},
txtSendBrochure: {
borderWidth: 1,
borderColor: 'lightgrey',
width: 280,
marginBottom: 5,
paddingLeft: 10,
},
textAreaSendBrochure: {
borderWidth: 1,
borderColor: 'lightgrey',
width: 280,
height: 150,
marginBottom: 5,
paddingLeft: 10,
textAlignVertical: 'top',
},
inputSearch: {
backgroundColor: 'white',
width: 200,
paddingLeft: 20,
},
btnSearch: {
width: 50,
alignItems: 'center',
justifyContent: 'center',
},
});
export default styles;
export default styles;

View file

@ -1,36 +1,36 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
treeviewContainer: {
width: 290,
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5
}
container: {
flex: 1,
flexDirection: 'row',
},
treeviewContainer: {
width: 290,
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5,
},
});
export default styles;
export default styles;

View file

@ -1,39 +1,39 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-end',
justifyContent: 'center',
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
},
button: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
marginBottom: 20,
marginRight: 50,
width: 400,
height: 80,
borderRadius: 10
},
btnTxt: {
fontSize: 20
},
btnImg: {
position: 'absolute',
left: 15,
height: 50,
width: 50,
resizeMode: 'stretch'
}
container: {
flex: 1,
alignItems: 'flex-end',
justifyContent: 'center',
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
},
button: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
marginBottom: 20,
marginRight: 50,
width: 400,
height: 80,
borderRadius: 10,
},
btnTxt: {
fontSize: 20,
},
btnImg: {
position: 'absolute',
left: 15,
height: 50,
width: 50,
resizeMode: 'stretch',
},
});
export default styles;
export default styles;

View file

@ -1,19 +1,19 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logo: {
width: 150,
height: 150,
marginBottom: 20
},
actions: {
marginTop: 20
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logo: {
width: 150,
height: 150,
marginBottom: 20,
},
actions: {
marginTop: 20,
},
});
export default styles;
export default styles;

View file

@ -1,25 +1,25 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logo: {
width: 150,
height: 150,
marginBottom: 20
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
backgroundColor: 'white',
paddingLeft: 10
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logo: {
width: 150,
height: 150,
marginBottom: 20,
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
backgroundColor: 'white',
paddingLeft: 10,
},
});
export default styles;
export default styles;

View file

@ -1,10 +1,10 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
container: {
flex: 1,
flexDirection: 'row',
},
});
export default styles;
export default styles;

View file

@ -1,38 +1,38 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
searchInput: {
borderWidth: 1,
width: 200,
paddingLeft: 20,
searchInput: {
borderWidth: 1,
width: 200,
paddingLeft: 20,
},
btnSearch: {
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
},
drawer: {
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#ffffff',
padding: 20,
},
btnSearch: {
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
btn: {
borderWidth: 1,
borderColor: 'black',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 10,
marginBottom: 10,
},
drawer: {
container: {
flex: 1,
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "#ffffff",
padding: 20
},
btn: {
borderWidth: 1,
borderColor: 'black',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 10,
marginBottom: 10
},
btnText: {
marginLeft: 10
},
}
btnText: {
marginLeft: 10,
},
},
});
export default styles;
export default styles;

View file

@ -1,33 +1,33 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5
}
container: {
flex: 1,
flexDirection: 'row',
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5,
},
});
export default styles;
export default styles;

View file

@ -1,23 +1,23 @@
import { StyleSheet, Dimensions } from 'react-native';
import { Dimensions, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
},
pdf: {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
height: 40,
width: 300,
borderColor: 'grey',
borderWidth: 1,
marginBottom: 20,
},
pdf: {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
});
export default styles;
export default styles;

View file

@ -1,33 +1,33 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5
}
container: {
flex: 1,
flexDirection: 'row',
},
scrollview: {
flex: 2,
},
filesContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
file: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
width: 180,
margin: 10,
},
fileTxt: {
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
padding: 5,
},
});
export default styles;
export default styles;

View file

@ -1,14 +1,14 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
btnWrapper: {
marginTop: 20
}
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
btnWrapper: {
marginTop: 20,
},
});
export default styles;
export default styles;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff