Compare commits

...

10 commits

13 changed files with 115 additions and 56 deletions

31
App.js
View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Platform, StatusBar} from 'react-native'; import {Platform, StatusBar, YellowBox} from 'react-native';
import LocaleManager from './src/managers/LocaleManager'; import LocaleManager from './src/managers/LocaleManager';
import AsyncStorageManager from "./src/managers/AsyncStorageManager"; import AsyncStorageManager from "./src/managers/AsyncStorageManager";
import CustomIntroSlider from "./src/components/Custom/CustomIntroSlider"; import CustomIntroSlider from "./src/components/Custom/CustomIntroSlider";
@ -18,6 +18,10 @@ import ConnectionManager from "./src/managers/ConnectionManager";
import URLHandler from "./src/utils/URLHandler"; import URLHandler from "./src/utils/URLHandler";
import {setSafeBounceHeight} from "react-navigation-collapsible"; import {setSafeBounceHeight} from "react-navigation-collapsible";
YellowBox.ignoreWarnings([ // collapsible headers cause this warning, just ignore as it is not an issue
'Non-serializable values were found in the navigation state',
]);
type Props = {}; type Props = {};
type State = { type State = {
@ -40,9 +44,6 @@ export default class App extends React.Component<Props, State> {
currentTheme: null, currentTheme: null,
}; };
onIntroDone: Function;
onUpdateTheme: Function;
navigatorRef: Object; navigatorRef: Object;
defaultRoute: string | null; defaultRoute: string | null;
@ -55,8 +56,6 @@ export default class App extends React.Component<Props, State> {
constructor() { constructor() {
super(); super();
LocaleManager.initTranslations(); LocaleManager.initTranslations();
this.onIntroDone = this.onIntroDone.bind(this);
this.onUpdateTheme = this.onUpdateTheme.bind(this);
SplashScreen.preventAutoHide(); SplashScreen.preventAutoHide();
this.navigatorRef = React.createRef(); this.navigatorRef = React.createRef();
this.defaultRoute = null; this.defaultRoute = null;
@ -82,25 +81,29 @@ export default class App extends React.Component<Props, State> {
/** /**
* Updates the theme * Updates the theme
*/ */
onUpdateTheme() { onUpdateTheme = () => {
this.setState({ this.setState({
currentTheme: ThemeManager.getCurrentTheme() currentTheme: ThemeManager.getCurrentTheme()
}); });
this.setupStatusBar(); this.setupStatusBar();
} };
setupStatusBar() { setupStatusBar() {
if (ThemeManager.getNightMode()) { if (Platform.OS === 'ios') {
StatusBar.setBarStyle('light-content', true); if (ThemeManager.getNightMode()) {
} else { StatusBar.setBarStyle('light-content', true);
StatusBar.setBarStyle('dark-content', true); } else {
StatusBar.setBarStyle('dark-content', true);
}
} }
// StatusBar.setTranslucent(false);
// StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface);
} }
/** /**
* Callback when user ends the intro. Save in preferences to avaoid showing back the introSlides * Callback when user ends the intro. Save in preferences to avaoid showing back the introSlides
*/ */
onIntroDone() { onIntroDone = () => {
this.setState({ this.setState({
showIntro: false, showIntro: false,
showUpdate: false, showUpdate: false,
@ -109,7 +112,7 @@ export default class App extends React.Component<Props, State> {
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showIntro.key, '0'); AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showIntro.key, '0');
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.updateNumber.key, Update.number.toString()); AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.updateNumber.key, Update.number.toString());
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showAprilFoolsStart.key, '0'); AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showAprilFoolsStart.key, '0');
} };
async componentDidMount() { async componentDidMount() {
await this.loadAssetsAsync(); await this.loadAssetsAsync();

View file

@ -3,18 +3,30 @@
"name": "Campus", "name": "Campus",
"description": "Application mobile compatible Android et iOS pour l'Amicale INSA Toulouse. Grâce à cette application, vous avez facilement accès aux news du campus, aux emplois du temps, à l'état de la laverie, et bien d'autres services ! Ceci est une version Beta, Toutes les fonctionnalités ne sont pas encore implémentées, et il est possible de rencontrer quelques bugs.", "description": "Application mobile compatible Android et iOS pour l'Amicale INSA Toulouse. Grâce à cette application, vous avez facilement accès aux news du campus, aux emplois du temps, à l'état de la laverie, et bien d'autres services ! Ceci est une version Beta, Toutes les fonctionnalités ne sont pas encore implémentées, et il est possible de rencontrer quelques bugs.",
"slug": "application-amicale", "slug": "application-amicale",
"scheme": "campus-insat", "backgroundColor": "#ffffff",
"privacy": "public", "privacy": "public",
"version": "2.0.0",
"platforms": [ "platforms": [
"ios", "ios",
"android", "android"
"web"
], ],
"version": "2.0.0",
"orientation": "portrait", "orientation": "portrait",
"primaryColor": "#be1522", "primaryColor": "#be1522",
"userInterfaceStyle": "automatic",
"icon": "./assets/android.icon.png", "icon": "./assets/android.icon.png",
"scheme": "campus-insat",
"facebookAutoInitEnabled": false,
"facebookAutoLogAppEventsEnabled": false,
"facebookAdvertiserIDCollectionEnabled": false,
"androidStatusBar": {
"barStyle": "light-content",
"hidden": false,
"translucent": false,
"backgroundColor": "#000000"
},
"androidNavigationBar": {
"barStyle": "light-content",
"backgroundColor": "#121212"
},
"splash": { "splash": {
"backgroundColor": "#be1522", "backgroundColor": "#be1522",
"resizeMode": "contain", "resizeMode": "contain",
@ -28,12 +40,13 @@
"updates": { "updates": {
"enabled": false "enabled": false
}, },
"assetBundlePatterns": [
"**/*"
],
"ios": { "ios": {
"bundleIdentifier": "fr.amicaleinsat.application", "bundleIdentifier": "fr.amicaleinsat.application",
"icon": "./assets/ios.icon.png" "icon": "./assets/ios.icon.png",
"appStoreUrl": "https://apps.apple.com/us/app/campus-amicale-insat/id1477722148?ls=1",
"usesIcloudStorage": false,
"usesAppleSignIn": false,
"accessesContactNotes": false
}, },
"android": { "android": {
"package": "fr.amicaleinsat.application", "package": "fr.amicaleinsat.application",
@ -43,10 +56,17 @@
"foregroundImage": "./assets/android.adaptive-icon.png", "foregroundImage": "./assets/android.adaptive-icon.png",
"backgroundColor": "#be1522" "backgroundColor": "#be1522"
}, },
"playStoreUrl": "https://play.google.com/store/apps/details?id=fr.amicaleinsat.application",
"permissions": [ "permissions": [
"VIBRATE", "VIBRATE",
"CAMERA" "CAMERA"
] ]
} },
"userInterfaceStyle": "automatic",
"assetBundlePatterns": [
"**/*"
],
} }
} }

View file

@ -44,7 +44,7 @@
"react-native-calendars": "^1.260.0", "react-native-calendars": "^1.260.0",
"react-native-collapsible": "^1.5.2", "react-native-collapsible": "^1.5.2",
"react-native-gesture-handler": "~1.6.0", "react-native-gesture-handler": "~1.6.0",
"react-native-image-modal": "^1.0.1", "react-native-image-modal": "^1.0.4",
"react-native-modalize": "^1.3.6", "react-native-modalize": "^1.3.6",
"react-native-paper": "^3.8.0", "react-native-paper": "^3.8.0",
"react-native-reanimated": "~1.7.0", "react-native-reanimated": "~1.7.0",

View file

@ -97,10 +97,10 @@ class ProxiwashListItem extends React.Component<Props> {
/> />
: <AnimatedIcon : <AnimatedIcon
icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'} icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
animation={isRunning ? "flash" : undefined} animation={isRunning ? "pulse" : undefined}
iterationCount={"infinite"} iterationCount={"infinite"}
easing={"linear"} easing={"linear"}
duration={2000} duration={1000}
useNativeDriver useNativeDriver
size={40} size={40}
color={colors.text} color={colors.text}

View file

@ -4,7 +4,7 @@ import * as React from 'react';
import {ERROR_TYPE, readData} from "../../utils/WebData"; import {ERROR_TYPE, readData} from "../../utils/WebData";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Snackbar} from 'react-native-paper'; import {Snackbar} from 'react-native-paper';
import {Animated, RefreshControl, View} from "react-native"; import {Animated, Platform, RefreshControl, StatusBar, View} from "react-native";
import ErrorView from "../Custom/ErrorView"; import ErrorView from "../Custom/ErrorView";
import BasicLoadingScreen from "../Custom/BasicLoadingScreen"; import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
@ -205,6 +205,12 @@ class WebSectionList extends React.PureComponent<Props, State> {
dataset = this.props.createDataset(this.state.fetchedData); dataset = this.props.createDataset(this.state.fetchedData);
const shouldRenderHeader = this.props.renderSectionHeader !== null; const shouldRenderHeader = this.props.renderSectionHeader !== null;
const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return ( return (
<View> <View>
{/*$FlowFixMe*/} {/*$FlowFixMe*/}
@ -213,7 +219,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
extraData={this.props.updateData} extraData={this.props.updateData}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
progressViewOffset={containerPaddingTop} progressViewOffset={padding}
refreshing={this.state.refreshing} refreshing={this.state.refreshing}
onRefresh={this.onRefresh} onRefresh={this.onRefresh}
/> />
@ -235,10 +241,10 @@ class WebSectionList extends React.PureComponent<Props, State> {
// Animations // Animations
onScroll={onScrollWithListener(this.props.onScroll)} onScroll={onScrollWithListener(this.props.onScroll)}
contentContainerStyle={{ contentContainerStyle={{
paddingTop: containerPaddingTop, paddingTop: padding,
minHeight: '100%' minHeight: '100%'
}} }}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}} scrollIndicatorInsets={{top: inset}}
/> />
<Snackbar <Snackbar
visible={this.state.snackbarVisible} visible={this.state.snackbarVisible}

View file

@ -9,7 +9,7 @@ import MaterialHeaderButtons, {Item} from '../Custom/HeaderButton';
import {HiddenItem} from "react-navigation-header-buttons"; import {HiddenItem} from "react-navigation-header-buttons";
import {Linking} from "expo"; import {Linking} from "expo";
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import {Animated, BackHandler} from "react-native"; import {Animated, BackHandler, Platform, StatusBar} from "react-native";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
type Props = { type Props = {
@ -133,6 +133,9 @@ class WebViewScreen extends React.PureComponent<Props> {
render() { render() {
const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack; const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return ( return (
<AnimatedWebView <AnimatedWebView
ref={this.webviewRef} ref={this.webviewRef}
@ -149,7 +152,7 @@ class WebViewScreen extends React.PureComponent<Props> {
this.canGoBack = navState.canGoBack; this.canGoBack = navState.canGoBack;
}} }}
onMessage={this.props.onMessage} onMessage={this.props.onMessage}
onLoad={() => this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop))} onLoad={() => this.injectJavaScript(this.getJavascriptPadding(padding))}
// Animations // Animations
onScroll={onScrollWithListener(this.props.onScroll)} onScroll={onScrollWithListener(this.props.onScroll)}
/> />

View file

@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {View} from "react-native";
import {withTheme} from 'react-native-paper'; import {withTheme} from 'react-native-paper';
import TabIcon from "./TabIcon"; import TabIcon from "./TabIcon";
import TabHomeIcon from "./TabHomeIcon"; import TabHomeIcon from "./TabHomeIcon";
import * as Animatable from 'react-native-animatable';
type Props = { type Props = {
state: Object, state: Object,
@ -42,10 +42,15 @@ class CustomTabBar extends React.Component<Props> {
const descriptors = this.props.descriptors; const descriptors = this.props.descriptors;
const navigation = this.props.navigation; const navigation = this.props.navigation;
return ( return (
<View style={{ <Animatable.View
flexDirection: 'row', animation={"fadeInUp"}
height: TAB_BAR_HEIGHT, duration={500}
}}> useNativeDriver
style={{
flexDirection: 'row',
height: TAB_BAR_HEIGHT,
}}
>
{state.routes.map((route, index) => { {state.routes.map((route, index) => {
const {options} = descriptors[route.key]; const {options} = descriptors[route.key];
const label = const label =
@ -77,14 +82,17 @@ class CustomTabBar extends React.Component<Props> {
label={label} label={label}
focused={isFocused} focused={isFocused}
extraData={state.index > index} extraData={state.index > index}
key={route.key}
/> />
} else } else
return <TabHomeIcon return <TabHomeIcon
onPress={onPress} onPress={onPress}
onLongPress={onLongPress} onLongPress={onLongPress}
focused={isFocused}/> focused={isFocused}
key={route.key}
/>
})} })}
</View> </Animatable.View>
); );
} }
} }

View file

@ -30,15 +30,15 @@ class TabHomeIcon extends React.Component<Props> {
scale: 1, translateY: 0 scale: 1, translateY: 0
}, },
"0.9": { "0.9": {
scale: 1.3, translateY: -6 scale: 1.2, translateY: -9
}, },
"1": { "1": {
scale: 1.2, translateY: -5 scale: 1.1, translateY: -7
}, },
}, },
fabFocusOut: { fabFocusOut: {
"0": { "0": {
scale: 1.2, translateY: -5 scale: 1.1, translateY: -6
}, },
"1": { "1": {
scale: 1, translateY: 0 scale: 1, translateY: 0

View file

@ -35,15 +35,15 @@ class TabIcon extends React.Component<Props> {
scale: 1, translateY: 0 scale: 1, translateY: 0
}, },
"0.9": { "0.9": {
scale: 1.5, translateY: 7 scale: 1.3, translateY: 7
}, },
"1": { "1": {
scale: 1.4, translateY: 6 scale: 1.2, translateY: 6
}, },
}, },
focusOut: { focusOut: {
"0": { "0": {
scale: 1.4, translateY: 6 scale: 1.2, translateY: 6
}, },
"1": { "1": {
scale: 1, translateY: 0 scale: 1, translateY: 0
@ -100,6 +100,7 @@ class TabIcon extends React.Component<Props> {
color: props.color, color: props.color,
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto',
fontSize: 10,
}} }}
> >
{props.label} {props.label}

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Animated, Platform} from "react-native"; import {Animated, Platform, StatusBar} from "react-native";
import {Chip, Searchbar, withTheme} from 'react-native-paper'; import {Chip, Searchbar, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import i18n from "i18n-js"; import i18n from "i18n-js";
@ -97,6 +97,12 @@ class ClubListScreen extends React.Component<Props, State> {
getScreen = (data: Object) => { getScreen = (data: Object) => {
this.categories = data[0].categories; this.categories = data[0].categories;
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return ( return (
//$FlowFixMe //$FlowFixMe
<Animated.FlatList <Animated.FlatList
@ -109,8 +115,8 @@ class ClubListScreen extends React.Component<Props, State> {
getItemLayout={this.itemLayout} getItemLayout={this.itemLayout}
// Animations // Animations
onScroll={onScroll} onScroll={onScroll}
contentContainerStyle={{paddingTop: containerPaddingTop}} contentContainerStyle={{paddingTop: padding}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}} scrollIndicatorInsets={{top: inset}}
/> />
) )
}; };

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Animated, Image, Platform, ScrollView, View} from "react-native"; import {Animated, Image, Platform, ScrollView, StatusBar, View} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import CustomModal from "../../components/Custom/CustomModal"; import CustomModal from "../../components/Custom/CustomModal";
import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper"; import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
@ -298,6 +298,12 @@ class ProximoListScreen extends React.Component<Props, State> {
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return ( return (
<View style={{ <View style={{
height: '100%' height: '100%'
@ -317,8 +323,8 @@ class ProximoListScreen extends React.Component<Props, State> {
initialNumToRender={10} initialNumToRender={10}
// Animations // Animations
onScroll={onScroll} onScroll={onScroll}
contentContainerStyle={{paddingTop: containerPaddingTop}} contentContainerStyle={{paddingTop: padding}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}} scrollIndicatorInsets={{top: inset}}
/> />
</View> </View>
); );

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Alert, Platform, View} from 'react-native'; import {Alert, Platform, StatusBar, View} from 'react-native';
import i18n from "i18n-js"; import i18n from "i18n-js";
import WebSectionList from "../../components/Lists/WebSectionList"; import WebSectionList from "../../components/Lists/WebSectionList";
import * as Notifications from "../../utils/Notifications"; import * as Notifications from "../../utils/Notifications";
@ -421,13 +421,16 @@ class ProxiwashScreen extends React.Component<Props, State> {
render() { render() {
const nav = this.props.navigation; const nav = this.props.navigation;
const {containerPaddingTop} = this.props.collapsibleStack; const {containerPaddingTop} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return ( return (
<AnimatedFocusView <AnimatedFocusView
{...this.props} {...this.props}
> >
<Banner <Banner
style={{ style={{
marginTop: this.state.bannerVisible ? containerPaddingTop : 0, marginTop: this.state.bannerVisible ? padding : 0,
}} }}
visible={this.state.bannerVisible} visible={this.state.bannerVisible}
actions={[ actions={[

View file

@ -5,7 +5,7 @@ import ThemeManager from "../../managers/ThemeManager";
import WebViewScreen from "../../components/Screens/WebViewScreen"; import WebViewScreen from "../../components/Screens/WebViewScreen";
import {Avatar, Banner, withTheme} from "react-native-paper"; import {Avatar, Banner, withTheme} from "react-native-paper";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {View} from "react-native"; import {Platform, StatusBar, View} from "react-native";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import AlertDialog from "../../components/Dialog/AlertDialog"; import AlertDialog from "../../components/Dialog/AlertDialog";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
@ -299,13 +299,16 @@ class PlanexScreen extends React.Component<Props, State> {
render() { render() {
const {containerPaddingTop} = this.props.collapsibleStack; const {containerPaddingTop} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return ( return (
<AnimatedFocusView <AnimatedFocusView
{...this.props} {...this.props}
> >
<Banner <Banner
style={{ style={{
marginTop: this.state.bannerVisible ? containerPaddingTop : 0, marginTop: this.state.bannerVisible ? padding : 0,
}} }}
visible={this.state.bannerVisible} visible={this.state.bannerVisible}
actions={[ actions={[