This commit is contained in:
Arnaud Vergnet 2020-04-28 08:32:19 +02:00
commit e1014a173e
22 changed files with 78 additions and 144 deletions

5
App.js
View file

@ -60,7 +60,6 @@ export default class App extends React.Component<Props, State> {
constructor() {
super();
LocaleManager.initTranslations();
// SplashScreen.preventAutoHide();
this.navigatorRef = React.createRef();
this.defaultHomeRoute = null;
this.defaultHomeData = {};
@ -71,8 +70,7 @@ export default class App extends React.Component<Props, State> {
this.loadAssetsAsync().then(() => {
this.onLoadFinished();
});
// console.log(Linking.makeUrl('path/into/app', { hello: 'world', goodbye: 'now' }))
console.log(global.HermesInternal !== null);
console.log("USING HERMES:" + global.HermesInternal !== null);
}
/**
@ -176,6 +174,7 @@ export default class App extends React.Component<Props, State> {
showUpdate: this.storageManager.preferences.updateNumber.current !== Update.number.toString(),
showAprilFools: AprilFoolsManager.getInstance().isAprilFoolsEnabled() && this.storageManager.preferences.showAprilFoolsStart.current === '1',
});
console.log('hiding splash');
SplashScreen.hide();
}

View file

@ -0,0 +1,7 @@
const keychainMock = {
SECURITY_LEVEL_ANY: "MOCK_SECURITY_LEVEL_ANY",
SECURITY_LEVEL_SECURE_SOFTWARE: "MOCK_SECURITY_LEVEL_SECURE_SOFTWARE",
SECURITY_LEVEL_SECURE_HARDWARE: "MOCK_SECURITY_LEVEL_SECURE_HARDWARE",
}
export default keychainMock;

View file

@ -1,7 +1,8 @@
jest.mock('react-native-keychain');
import React from 'react';
import ConnectionManager from "../../src/managers/ConnectionManager";
import {ERROR_TYPE} from "../../src/utils/WebData";
import * as SecureStore from 'expo-secure-store';
let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
@ -25,45 +26,6 @@ test('isLoggedIn no', () => {
return expect(c.isLoggedIn()).toBe(false);
});
test('recoverLogin error crypto', () => {
jest.spyOn(SecureStore, 'getItemAsync').mockImplementationOnce(() => {
return Promise.reject();
});
return expect(c.recoverLogin()).rejects.toBe(false);
});
test('recoverLogin success crypto', () => {
jest.spyOn(SecureStore, 'getItemAsync').mockImplementationOnce(() => {
return Promise.resolve('token1');
});
return expect(c.recoverLogin()).resolves.toBe('token1');
});
test('saveLogin success', () => {
jest.spyOn(SecureStore, 'setItemAsync').mockImplementationOnce(() => {
return Promise.resolve();
});
return expect(c.saveLogin('email', 'token2')).resolves.toBeTruthy();
});
test('saveLogin error', () => {
jest.spyOn(SecureStore, 'setItemAsync').mockImplementationOnce(() => {
return Promise.reject();
});
return expect(c.saveLogin('email', 'token3')).rejects.toBeFalsy();
});
test('recoverLogin error crypto with saved token', () => {
jest.spyOn(SecureStore, 'getItemAsync').mockImplementationOnce(() => {
return Promise.reject();
});
return expect(c.recoverLogin()).resolves.toBe('token2');
});
test('recoverLogin success saved', () => {
return expect(c.recoverLogin()).resolves.toBe('token2');
});
test("isConnectionResponseValid", () => {
let json = {
error: 0,

View file

@ -1,24 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="fr.amicaleinsat.application">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme">
<meta-data android:name="com.facebook.sdk.AutoInitEnabled" android:value="false"/>

View file

@ -16,10 +16,8 @@ public class BasePackageList {
new expo.modules.imageloader.ImageLoaderPackage(),
new expo.modules.keepawake.KeepAwakePackage(),
new expo.modules.lineargradient.LinearGradientPackage(),
new expo.modules.localization.LocalizationPackage(),
new expo.modules.location.LocationPackage(),
new expo.modules.permissions.PermissionsPackage(),
new expo.modules.securestore.SecureStorePackage(),
new expo.modules.sqlite.SQLitePackage(),
new expo.modules.webbrowser.WebBrowserPackage()
);

View file

@ -16,5 +16,3 @@ echo "Installing dependencies..."
npm install
echo -e "Done\n"
echo "Starting expo with clear cache..."
expo r -c

View file

@ -52,28 +52,8 @@
</dict>
</dict>
</dict>
<key>NSCalendarsUsageDescription</key>
<string>Allow Campus to access your calendar</string>
<key>NSCameraUsageDescription</key>
<string>Allow Campus to use the camera</string>
<key>NSContactsUsageDescription</key>
<string>Allow Campus experiences to access your contacts</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Allow Campus to use your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Allow Campus to use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Allow Campus to use your location</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow Campus to access your microphone</string>
<key>NSMotionUsageDescription</key>
<string>Allow Campus to access your device's accelerometer</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Give Campus periences permission to save photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Give Campus periences permission to access your photos</string>
<key>NSRemindersUsageDescription</key>
<string>Allow Campus to access your reminders</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View file

@ -44,4 +44,10 @@ target 'Campus' do
# react-native-cli autolinking
use_native_modules!
# Permissions
permissions_path = '../node_modules/react-native-permissions/ios'
pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec"
pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
end

View file

@ -27,10 +27,6 @@
"expo": "^37.0.0",
"expo-barcode-scanner": "~8.1.0",
"expo-camera": "latest",
"expo-linear-gradient": "~8.1.0",
"expo-localization": "~8.1.0",
"expo-permissions": "~8.1.0",
"expo-secure-store": "~8.1.0",
"i18n-js": "^3.3.0",
"react": "~16.9.0",
"react-dom": "16.9.0",
@ -43,8 +39,12 @@
"react-native-collapsible": "^1.5.2",
"react-native-gesture-handler": "~1.6.0",
"react-native-image-modal": "^1.0.6",
"react-native-keychain": "^6.0.0",
"react-native-linear-gradient": "^2.5.6",
"react-native-localize": "^1.4.0",
"react-native-modalize": "^1.3.6",
"react-native-paper": "^3.8.0",
"react-native-permissions": "^2.1.3",
"react-native-reanimated": "~1.7.0",
"react-native-render-html": "^4.1.2",
"react-native-safe-area-context": "0.7.3",

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import {Text, withTheme} from 'react-native-paper';
import HTML from "react-native-render-html";
import {Linking} from "expo";
import {Linking} from "react-native";
type Props = {
theme: Object,
@ -18,7 +18,6 @@ class CustomHTML extends React.Component<Props> {
};
getBasicText = (htmlAttribs, children, convertedCSSStyles, passProps) => {
// console.log(convertedCSSStyles);
return <Text {...passProps}>{children}</Text>;
};

View file

@ -1,7 +1,6 @@
// @flow
import * as React from 'react';
import {LinearGradient} from "expo-linear-gradient";
import {Image, Platform, StatusBar, StyleSheet, View} from "react-native";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {Text} from "react-native-paper";
@ -9,6 +8,7 @@ import i18n from 'i18n-js';
import AppIntroSlider from "react-native-app-intro-slider";
import Update from "../../constants/Update";
import ThemeManager from "../../managers/ThemeManager";
import LinearGradient from 'react-native-linear-gradient';
type Props = {
onDone: Function,
@ -40,49 +40,49 @@ export default class CustomIntroSlider extends React.Component<Props> {
title: i18n.t('intro.slide1.title'),
text: i18n.t('intro.slide1.text'),
image: require('../../../assets/splash.png'),
colors: ['#e01928', '#be1522'],
colors: ['#dc2634', '#be1522'],
},
{
key: '2',
title: i18n.t('intro.slide2.title'),
text: i18n.t('intro.slide2.text'),
icon: 'calendar-range',
colors: ['#d99e09', '#c28d08'],
colors: ['#d99e09', '#9e7205'],
},
{
key: '3',
title: i18n.t('intro.slide3.title'),
text: i18n.t('intro.slide3.text'),
icon: 'washing-machine',
colors: ['#1fa5ee', '#1c97da'],
colors: ['#1fa5ee', '#0976b1'],
},
{
key: '4',
title: i18n.t('intro.slide4.title'),
text: i18n.t('intro.slide4.text'),
icon: 'shopping',
colors: ['#ec5904', '#da5204'],
colors: ['#ec5904', '#b64300'],
},
{
key: '5',
title: i18n.t('intro.slide5.title'),
text: i18n.t('intro.slide5.text'),
icon: 'timetable',
colors: ['#7c33ec', '#732fda'],
colors: ['#7c33ec', '#5e11d1'],
},
{
key: '6',
title: i18n.t('intro.slide6.title'),
text: i18n.t('intro.slide6.text'),
icon: 'silverware-fork-knife',
colors: ['#ec1213', '#ff372f'],
colors: ['#ec1213', '#970902'],
},
{
key: '7',
title: i18n.t('intro.slide7.title'),
text: i18n.t('intro.slide7.text'),
icon: 'cogs',
colors: ['#37c13e', '#26852b'],
colors: ['#37c13e', '#1a5a1d'],
},
];
this.updateSlides = [

View file

@ -7,9 +7,8 @@ import ErrorView from "./ErrorView";
import {ERROR_TYPE} from "../../utils/WebData";
import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
import {HiddenItem} from "react-navigation-header-buttons";
import {Linking} from "expo";
import i18n from 'i18n-js';
import {Animated, BackHandler} from "react-native";
import {Animated, BackHandler, Linking} from "react-native";
import {withCollapsible} from "../../utils/withCollapsible";
type Props = {

View file

@ -1,6 +1,6 @@
// @flow
import * as SecureStore from 'expo-secure-store';
import * as Keychain from 'react-native-keychain';
import {apiRequest, ERROR_TYPE, isResponseValid} from "../utils/WebData";
/**
@ -14,7 +14,7 @@ import {apiRequest, ERROR_TYPE, isResponseValid} from "../utils/WebData";
* 500 : SERVER_ERROR -> pb coté serveur
*/
const SERVER_NAME = "amicale-insat.fr";
const AUTH_PATH = "password";
export default class ConnectionManager {
@ -60,16 +60,16 @@ export default class ConnectionManager {
if (this.getToken() !== null)
resolve(this.getToken());
else {
SecureStore.getItemAsync('token')
.then((token) => {
this.#token = token;
if (token !== null) {
Keychain.getInternetCredentials(SERVER_NAME)
.then((data) => {
if (data) {
this.#token = data.password;
this.onLoginStateChange(true);
resolve(token);
resolve(this.#token);
} else
reject(false);
})
.catch(error => {
.catch(() => {
reject(false);
});
}
@ -82,14 +82,14 @@ export default class ConnectionManager {
async saveLogin(email: string, token: string) {
return new Promise((resolve, reject) => {
SecureStore.setItemAsync('token', token)
Keychain.setInternetCredentials(SERVER_NAME, 'token', token)
.then(() => {
this.#token = token;
this.#email = email;
this.onLoginStateChange(true);
resolve(true);
})
.catch(error => {
.catch(() => {
reject(false);
});
});
@ -97,13 +97,13 @@ export default class ConnectionManager {
async disconnect() {
return new Promise((resolve, reject) => {
SecureStore.deleteItemAsync('token')
Keychain.resetInternetCredentials(SERVER_NAME)
.then(() => {
this.#token = null;
this.onLoginStateChange(false);
resolve(true);
})
.catch((error) => {
.catch(() => {
reject(false);
});
});
@ -143,7 +143,6 @@ export default class ConnectionManager {
async authenticatedRequest(path: string, params: Object) {
return new Promise((resolve, reject) => {
if (this.getToken() !== null) {
// console.log(data);
let data = {
token: this.getToken(),
...params

View file

@ -1,7 +1,7 @@
// @flow
import i18n from 'i18n-js';
import * as Localization from 'expo-localization';
import * as RNLocalize from "react-native-localize";
import en from '../../translations/en';
import fr from '../../translations/fr';
@ -17,10 +17,10 @@ export default class LocaleManager {
static initTranslations() {
i18n.fallbacks = true;
i18n.translations = {fr, en};
i18n.locale = Localization.locale;
i18n.locale = RNLocalize.findBestAvailableLanguage(["en", "fr"]).languageTag;
}
static getCurrentLocale() {
return Localization.locale;
return RNLocalize.findBestAvailableLanguage(["en", "fr"]).languageTag;
}
}

View file

@ -1,10 +1,9 @@
// @flow
import * as React from 'react';
import {Animated, FlatList, Image, View} from 'react-native';
import {Animated, FlatList, Image, Linking, View} from 'react-native';
import {Card, List, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js';
import {Linking} from "expo";
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {ScrollView, View} from 'react-native';
import {Linking, ScrollView, View} from 'react-native';
import {Avatar, Button, Card, Chip, Paragraph, withTheme} from 'react-native-paper';
import ImageModal from 'react-native-image-modal';
import i18n from "i18n-js";
@ -11,7 +11,6 @@ import CustomTabBar from "../../../components/Tabbar/CustomTabBar";
import type {category, club} from "./ClubListScreen";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {StackNavigationProp} from "@react-navigation/stack";
import {Linking} from "expo";
import {ERROR_TYPE} from "../../../utils/WebData";
type Props = {

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Animated, KeyboardAvoidingView, StyleSheet, View} from "react-native";
import {Animated, KeyboardAvoidingView, Linking, StyleSheet, View} from "react-native";
import {Avatar, Button, Card, HelperText, Paragraph, TextInput, withTheme} from 'react-native-paper';
import ConnectionManager from "../../managers/ConnectionManager";
import i18n from 'i18n-js';
@ -10,7 +10,6 @@ import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import type {CustomTheme} from "../../managers/ThemeManager";
import {Linking} from "expo";
type Props = {
navigation: Object,

View file

@ -1,12 +1,11 @@
// @flow
import * as React from 'react';
import {ScrollView, View} from 'react-native';
import {Linking, ScrollView, View} from 'react-native';
import {Avatar, Card, Text, withTheme} from 'react-native-paper';
import ImageModal from 'react-native-image-modal';
import Autolink from "react-native-autolink";
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import {Linking} from "expo";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
type Props = {

View file

@ -1,12 +1,11 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from "react-native";
import {Linking, StyleSheet, View} from "react-native";
import {Button, Text, withTheme} from 'react-native-paper';
import {BarCodeScanner} from "expo-barcode-scanner";
import {Camera} from 'expo-camera';
import URLHandler from "../../utils/URLHandler";
import {Linking} from "expo";
import AlertDialog from "../../components/Dialogs/AlertDialog";
import i18n from 'i18n-js';
import CustomTabBar from "../../components/Tabbar/CustomTabBar";

View file

@ -3,8 +3,7 @@
import * as React from 'react';
import {Avatar, Button, Card, Paragraph, withTheme} from "react-native-paper";
import i18n from "i18n-js";
import {ScrollView} from "react-native";
import {Linking} from "expo";
import {Linking, ScrollView} from "react-native";
import type {CustomTheme} from "../../managers/ThemeManager";
type Props = {

View file

@ -228,7 +228,6 @@ class PlanningScreen extends React.Component<Props, State> {
}
render() {
// console.log("rendering PlanningScreen");
return (
<CustomAgenda
{...this.props}

View file

@ -1,6 +1,6 @@
// @flow
import * as Permissions from 'expo-permissions';
import {checkNotifications, requestNotifications, RESULTS} from 'react-native-permissions';
import {Notifications} from 'expo';
import AsyncStorageManager from "../managers/AsyncStorageManager";
import LocaleManager from "../managers/LocaleManager";
@ -14,13 +14,22 @@ const EXPO_TOKEN_SERVER = 'https://etud.insa-toulouse.fr/~amicale_app/expo_notif
* @returns {Promise}
*/
export async function askPermissions() {
const {status: existingStatus} = await Permissions.getAsync(Permissions.NOTIFICATIONS);
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const {status} = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
return finalStatus === 'granted';
return new Promise(((resolve, reject) => {
checkNotifications().then(({status, settings}) => {
if (status === RESULTS.GRANTED)
resolve();
else if (status === RESULTS.BLOCKED)
reject()
else {
requestNotifications().then(({status, settings}) => {
if (status === RESULTS.GRANTED)
resolve();
else
reject();
});
}
});
}));
}
/**
@ -33,12 +42,12 @@ export async function askPermissions() {
export async function initExpoToken() {
let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
if (token === '') {
try {
await askPermissions();
let expoToken = await Notifications.getExpoPushTokenAsync();
// Save token for instant use later on
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, expoToken);
} catch (e) {}
askPermissions().then(() => {
Notifications.getExpoPushTokenAsync().then((token) => {
// Save token for instant use later on
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, token);
});
});
}
}