diff --git a/App.js b/App.js index c46ae1d..faa6fbc 100644 --- a/App.js +++ b/App.js @@ -60,7 +60,6 @@ export default class App extends React.Component { 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 { 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 { 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(); } diff --git a/__mocks__/react-native-keychain/index.js b/__mocks__/react-native-keychain/index.js new file mode 100644 index 0000000..3d30acb --- /dev/null +++ b/__mocks__/react-native-keychain/index.js @@ -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; \ No newline at end of file diff --git a/__tests__/managers/ConnectionManager.test.js b/__tests__/managers/ConnectionManager.test.js index 2a9a996..e307fd5 100644 --- a/__tests__/managers/ConnectionManager.test.js +++ b/__tests__/managers/ConnectionManager.test.js @@ -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, diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 883db5e..f7ddaeb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,24 +1,9 @@ - - - - - - - - - - - - - - - - - - + + + diff --git a/android/app/src/main/java/fr/amicaleinsat/application/generated/BasePackageList.java b/android/app/src/main/java/fr/amicaleinsat/application/generated/BasePackageList.java index 0b8a095..214de2f 100644 --- a/android/app/src/main/java/fr/amicaleinsat/application/generated/BasePackageList.java +++ b/android/app/src/main/java/fr/amicaleinsat/application/generated/BasePackageList.java @@ -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() ); diff --git a/clear-node-cache.sh b/clear-node-cache.sh index 7dc2885..63975cf 100755 --- a/clear-node-cache.sh +++ b/clear-node-cache.sh @@ -16,5 +16,3 @@ echo "Installing dependencies..." npm install echo -e "Done\n" -echo "Starting expo with clear cache..." -expo r -c diff --git a/ios/Campus/Info.plist b/ios/Campus/Info.plist index 0433652..e0a6f5a 100644 --- a/ios/Campus/Info.plist +++ b/ios/Campus/Info.plist @@ -52,28 +52,8 @@ - NSCalendarsUsageDescription - Allow Campus to access your calendar NSCameraUsageDescription Allow Campus to use the camera - NSContactsUsageDescription - Allow Campus experiences to access your contacts - NSLocationAlwaysAndWhenInUseUsageDescription - Allow Campus to use your location - NSLocationAlwaysUsageDescription - Allow Campus to use your location - NSLocationWhenInUseUsageDescription - Allow Campus to use your location - NSMicrophoneUsageDescription - Allow Campus to access your microphone - NSMotionUsageDescription - Allow Campus to access your device's accelerometer - NSPhotoLibraryAddUsageDescription - Give Campus periences permission to save photos - NSPhotoLibraryUsageDescription - Give Campus periences permission to access your photos - NSRemindersUsageDescription - Allow Campus to access your reminders UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/ios/Podfile b/ios/Podfile index 2600a9c..1f333ce 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -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 diff --git a/package.json b/package.json index 03dbad8..d59e6c3 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/Overrides/CustomHTML.js b/src/components/Overrides/CustomHTML.js index 7d88d1f..340717b 100644 --- a/src/components/Overrides/CustomHTML.js +++ b/src/components/Overrides/CustomHTML.js @@ -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 { }; getBasicText = (htmlAttribs, children, convertedCSSStyles, passProps) => { - // console.log(convertedCSSStyles); return {children}; }; diff --git a/src/components/Overrides/CustomIntroSlider.js b/src/components/Overrides/CustomIntroSlider.js index 7b6a3b2..0c8ddfa 100644 --- a/src/components/Overrides/CustomIntroSlider.js +++ b/src/components/Overrides/CustomIntroSlider.js @@ -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 { 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 = [ diff --git a/src/components/Screens/WebViewScreen.js b/src/components/Screens/WebViewScreen.js index fff5538..7fcce98 100644 --- a/src/components/Screens/WebViewScreen.js +++ b/src/components/Screens/WebViewScreen.js @@ -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 = { diff --git a/src/managers/ConnectionManager.js b/src/managers/ConnectionManager.js index 9ff1675..0151f45 100644 --- a/src/managers/ConnectionManager.js +++ b/src/managers/ConnectionManager.js @@ -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 diff --git a/src/managers/LocaleManager.js b/src/managers/LocaleManager.js index 15c5390..ca45c40 100644 --- a/src/managers/LocaleManager.js +++ b/src/managers/LocaleManager.js @@ -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; } } diff --git a/src/screens/Amicale/AmicaleContactScreen.js b/src/screens/Amicale/AmicaleContactScreen.js index ddc10b1..10c817b 100644 --- a/src/screens/Amicale/AmicaleContactScreen.js +++ b/src/screens/Amicale/AmicaleContactScreen.js @@ -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"; diff --git a/src/screens/Amicale/Clubs/ClubDisplayScreen.js b/src/screens/Amicale/Clubs/ClubDisplayScreen.js index 19c3d7b..e1b8221 100644 --- a/src/screens/Amicale/Clubs/ClubDisplayScreen.js +++ b/src/screens/Amicale/Clubs/ClubDisplayScreen.js @@ -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 = { diff --git a/src/screens/Amicale/LoginScreen.js b/src/screens/Amicale/LoginScreen.js index 671a2d5..fcc2404 100644 --- a/src/screens/Amicale/LoginScreen.js +++ b/src/screens/Amicale/LoginScreen.js @@ -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, diff --git a/src/screens/Home/FeedItemScreen.js b/src/screens/Home/FeedItemScreen.js index a3c46e9..b1cd8e2 100644 --- a/src/screens/Home/FeedItemScreen.js +++ b/src/screens/Home/FeedItemScreen.js @@ -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 = { diff --git a/src/screens/Home/ScannerScreen.js b/src/screens/Home/ScannerScreen.js index 2e36a34..44881ad 100644 --- a/src/screens/Home/ScannerScreen.js +++ b/src/screens/Home/ScannerScreen.js @@ -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"; diff --git a/src/screens/Other/FeedbackScreen.js b/src/screens/Other/FeedbackScreen.js index 3391d30..5c6fae9 100644 --- a/src/screens/Other/FeedbackScreen.js +++ b/src/screens/Other/FeedbackScreen.js @@ -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 = { diff --git a/src/screens/Planning/PlanningScreen.js b/src/screens/Planning/PlanningScreen.js index 4dddc91..438d9d6 100644 --- a/src/screens/Planning/PlanningScreen.js +++ b/src/screens/Planning/PlanningScreen.js @@ -228,7 +228,6 @@ class PlanningScreen extends React.Component { } render() { - // console.log("rendering PlanningScreen"); return ( { + 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); + }); + }); } }