forked from vergnet/application-amicale
Update prettier and eslint config
This commit is contained in:
parent
18f7a6abbd
commit
02f9241d28
151 changed files with 5434 additions and 5013 deletions
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: '@react-native-community',
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint'],
|
|
||||||
};
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
bracketSpacing: false,
|
|
||||||
jsxBracketSameLine: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
};
|
|
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"i18n-ally.localesPaths": "locales",
|
||||||
|
"i18n-ally.keystyle": "nested"
|
||||||
|
}
|
49
App.tsx
49
App.tsx
|
@ -17,13 +17,13 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import React from 'react';
|
||||||
import {LogBox, Platform, SafeAreaView, View} from 'react-native';
|
import { LogBox, Platform, SafeAreaView, View } from 'react-native';
|
||||||
import {NavigationContainer} from '@react-navigation/native';
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
import {Provider as PaperProvider} from 'react-native-paper';
|
import { Provider as PaperProvider } from 'react-native-paper';
|
||||||
import {setSafeBounceHeight} from 'react-navigation-collapsible';
|
import { setSafeBounceHeight } from 'react-navigation-collapsible';
|
||||||
import SplashScreen from 'react-native-splash-screen';
|
import SplashScreen from 'react-native-splash-screen';
|
||||||
import {OverflowMenuProvider} from 'react-navigation-header-buttons';
|
import { OverflowMenuProvider } from 'react-navigation-header-buttons';
|
||||||
import AsyncStorageManager from './src/managers/AsyncStorageManager';
|
import AsyncStorageManager from './src/managers/AsyncStorageManager';
|
||||||
import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider';
|
import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider';
|
||||||
import ThemeManager from './src/managers/ThemeManager';
|
import ThemeManager from './src/managers/ThemeManager';
|
||||||
|
@ -31,11 +31,12 @@ import MainNavigator from './src/navigation/MainNavigator';
|
||||||
import AprilFoolsManager from './src/managers/AprilFoolsManager';
|
import AprilFoolsManager from './src/managers/AprilFoolsManager';
|
||||||
import Update from './src/constants/Update';
|
import Update from './src/constants/Update';
|
||||||
import ConnectionManager from './src/managers/ConnectionManager';
|
import ConnectionManager from './src/managers/ConnectionManager';
|
||||||
import type {ParsedUrlDataType} from './src/utils/URLHandler';
|
import type { ParsedUrlDataType } from './src/utils/URLHandler';
|
||||||
import URLHandler from './src/utils/URLHandler';
|
import URLHandler from './src/utils/URLHandler';
|
||||||
import {setupStatusBar} from './src/utils/Utils';
|
import { setupStatusBar } from './src/utils/Utils';
|
||||||
import initLocales from './src/utils/Locales';
|
import initLocales from './src/utils/Locales';
|
||||||
import {NavigationContainerRef} from '@react-navigation/core';
|
import { NavigationContainerRef } from '@react-navigation/core';
|
||||||
|
import GENERAL_STYLES from './src/constants/Styles';
|
||||||
|
|
||||||
// Native optimizations https://reactnavigation.org/docs/react-native-screens
|
// Native optimizations https://reactnavigation.org/docs/react-native-screens
|
||||||
// Crashes app when navigating away from webview on android 9+
|
// Crashes app when navigating away from webview on android 9+
|
||||||
|
@ -56,11 +57,11 @@ type StateType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class App extends React.Component<{}, StateType> {
|
export default class App extends React.Component<{}, StateType> {
|
||||||
navigatorRef: {current: null | NavigationContainerRef};
|
navigatorRef: { current: null | NavigationContainerRef };
|
||||||
|
|
||||||
defaultHomeRoute: string | null;
|
defaultHomeRoute: string | null;
|
||||||
|
|
||||||
defaultHomeData: {[key: string]: string};
|
defaultHomeData: { [key: string]: string };
|
||||||
|
|
||||||
urlHandler: URLHandler;
|
urlHandler: URLHandler;
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ export default class App extends React.Component<{}, StateType> {
|
||||||
if (nav != null) {
|
if (nav != null) {
|
||||||
nav.navigate('home', {
|
nav.navigate('home', {
|
||||||
screen: 'index',
|
screen: 'index',
|
||||||
params: {nextScreen: parsedData.route, data: parsedData.data},
|
params: { nextScreen: parsedData.route, data: parsedData.data },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -132,15 +133,15 @@ export default class App extends React.Component<{}, StateType> {
|
||||||
});
|
});
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.showIntro.key,
|
AsyncStorageManager.PREFERENCES.showIntro.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.updateNumber.key,
|
AsyncStorageManager.PREFERENCES.updateNumber.key,
|
||||||
Update.number,
|
Update.number
|
||||||
);
|
);
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
|
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,16 +162,16 @@ export default class App extends React.Component<{}, StateType> {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
currentTheme: ThemeManager.getCurrentTheme(),
|
currentTheme: ThemeManager.getCurrentTheme(),
|
||||||
showIntro: AsyncStorageManager.getBool(
|
showIntro: AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.showIntro.key,
|
AsyncStorageManager.PREFERENCES.showIntro.key
|
||||||
),
|
),
|
||||||
showUpdate:
|
showUpdate:
|
||||||
AsyncStorageManager.getNumber(
|
AsyncStorageManager.getNumber(
|
||||||
AsyncStorageManager.PREFERENCES.updateNumber.key,
|
AsyncStorageManager.PREFERENCES.updateNumber.key
|
||||||
) !== Update.number,
|
) !== Update.number,
|
||||||
showAprilFools:
|
showAprilFools:
|
||||||
AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
|
AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
|
||||||
AsyncStorageManager.getBool(
|
AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
|
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
SplashScreen.hide();
|
SplashScreen.hide();
|
||||||
|
@ -194,7 +195,7 @@ export default class App extends React.Component<{}, StateType> {
|
||||||
* Renders the app based on loading state
|
* Renders the app based on loading state
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -213,12 +214,14 @@ export default class App extends React.Component<{}, StateType> {
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: ThemeManager.getCurrentTheme().colors.background,
|
backgroundColor: ThemeManager.getCurrentTheme().colors.background,
|
||||||
flex: 1,
|
...GENERAL_STYLES.flex,
|
||||||
}}>
|
}}
|
||||||
<SafeAreaView style={{flex: 1}}>
|
>
|
||||||
|
<SafeAreaView style={GENERAL_STYLES.flex}>
|
||||||
<NavigationContainer
|
<NavigationContainer
|
||||||
theme={state.currentTheme}
|
theme={state.currentTheme}
|
||||||
ref={this.navigatorRef}>
|
ref={this.navigatorRef}
|
||||||
|
>
|
||||||
<MainNavigator
|
<MainNavigator
|
||||||
defaultHomeRoute={this.defaultHomeRoute}
|
defaultHomeRoute={this.defaultHomeRoute}
|
||||||
defaultHomeData={this.defaultHomeData}
|
defaultHomeData={this.defaultHomeData}
|
||||||
|
|
8
__mocks__/react-native-keychain/index.js
vendored
8
__mocks__/react-native-keychain/index.js
vendored
|
@ -1,7 +1,7 @@
|
||||||
const keychainMock = {
|
const keychainMock = {
|
||||||
SECURITY_LEVEL_ANY: "MOCK_SECURITY_LEVEL_ANY",
|
SECURITY_LEVEL_ANY: 'MOCK_SECURITY_LEVEL_ANY',
|
||||||
SECURITY_LEVEL_SECURE_SOFTWARE: "MOCK_SECURITY_LEVEL_SECURE_SOFTWARE",
|
SECURITY_LEVEL_SECURE_SOFTWARE: 'MOCK_SECURITY_LEVEL_SECURE_SOFTWARE',
|
||||||
SECURITY_LEVEL_SECURE_HARDWARE: "MOCK_SECURITY_LEVEL_SECURE_HARDWARE",
|
SECURITY_LEVEL_SECURE_HARDWARE: 'MOCK_SECURITY_LEVEL_SECURE_HARDWARE',
|
||||||
}
|
};
|
||||||
|
|
||||||
export default keychainMock;
|
export default keychainMock;
|
|
@ -1,11 +1,9 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ConnectionManager from '../../src/managers/ConnectionManager';
|
import ConnectionManager from '../../src/managers/ConnectionManager';
|
||||||
import {ERROR_TYPE} from '../../src/utils/WebData';
|
import { ERROR_TYPE } from '../../src/utils/WebData';
|
||||||
|
|
||||||
jest.mock('react-native-keychain');
|
jest.mock('react-native-keychain');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
const fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
||||||
|
|
||||||
const c = ConnectionManager.getInstance();
|
const c = ConnectionManager.getInstance();
|
||||||
|
@ -44,7 +42,7 @@ test('connect bad credentials', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||||
ERROR_TYPE.BAD_CREDENTIALS,
|
ERROR_TYPE.BAD_CREDENTIALS
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ test('connect good credentials', () => {
|
||||||
json: () => {
|
json: () => {
|
||||||
return {
|
return {
|
||||||
error: ERROR_TYPE.SUCCESS,
|
error: ERROR_TYPE.SUCCESS,
|
||||||
data: {token: 'token'},
|
data: { token: 'token' },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -79,7 +77,7 @@ test('connect good credentials no consent', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||||
ERROR_TYPE.NO_CONSENT,
|
ERROR_TYPE.NO_CONSENT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,7 +87,7 @@ test('connect good credentials, fail save token', () => {
|
||||||
json: () => {
|
json: () => {
|
||||||
return {
|
return {
|
||||||
error: ERROR_TYPE.SUCCESS,
|
error: ERROR_TYPE.SUCCESS,
|
||||||
data: {token: 'token'},
|
data: { token: 'token' },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -100,7 +98,7 @@ test('connect good credentials, fail save token', () => {
|
||||||
return Promise.reject(false);
|
return Promise.reject(false);
|
||||||
});
|
});
|
||||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||||
ERROR_TYPE.TOKEN_SAVE,
|
ERROR_TYPE.TOKEN_SAVE
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -109,7 +107,7 @@ test('connect connection error', () => {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
});
|
});
|
||||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||||
ERROR_TYPE.CONNECTION_ERROR,
|
ERROR_TYPE.CONNECTION_ERROR
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -125,7 +123,7 @@ test('connect bogus response 1', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||||
ERROR_TYPE.SERVER_ERROR,
|
ERROR_TYPE.SERVER_ERROR
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -140,14 +138,14 @@ test('authenticatedRequest success', () => {
|
||||||
json: () => {
|
json: () => {
|
||||||
return {
|
return {
|
||||||
error: ERROR_TYPE.SUCCESS,
|
error: ERROR_TYPE.SUCCESS,
|
||||||
data: {coucou: 'toi'},
|
data: { coucou: 'toi' },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(
|
return expect(
|
||||||
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'),
|
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check')
|
||||||
).resolves.toStrictEqual({coucou: 'toi'});
|
).resolves.toStrictEqual({ coucou: 'toi' });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('authenticatedRequest error wrong token', () => {
|
test('authenticatedRequest error wrong token', () => {
|
||||||
|
@ -167,7 +165,7 @@ test('authenticatedRequest error wrong token', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(
|
return expect(
|
||||||
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'),
|
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check')
|
||||||
).rejects.toBe(ERROR_TYPE.BAD_TOKEN);
|
).rejects.toBe(ERROR_TYPE.BAD_TOKEN);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -187,7 +185,7 @@ test('authenticatedRequest error bogus response', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return expect(
|
return expect(
|
||||||
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'),
|
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check')
|
||||||
).rejects.toBe(ERROR_TYPE.SERVER_ERROR);
|
).rejects.toBe(ERROR_TYPE.SERVER_ERROR);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -201,7 +199,7 @@ test('authenticatedRequest connection error', () => {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
});
|
});
|
||||||
return expect(
|
return expect(
|
||||||
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'),
|
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check')
|
||||||
).rejects.toBe(ERROR_TYPE.CONNECTION_ERROR);
|
).rejects.toBe(ERROR_TYPE.CONNECTION_ERROR);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -212,6 +210,6 @@ test('authenticatedRequest error no token', () => {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
return expect(
|
return expect(
|
||||||
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'),
|
c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check')
|
||||||
).rejects.toBe(ERROR_TYPE.TOKEN_RETRIEVE);
|
).rejects.toBe(ERROR_TYPE.TOKEN_RETRIEVE);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import * as EquipmentBooking from '../../src/utils/EquipmentBooking';
|
import * as EquipmentBooking from '../../src/utils/EquipmentBooking';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
@ -18,7 +15,7 @@ test('getCurrentDay', () => {
|
||||||
.spyOn(Date, 'now')
|
.spyOn(Date, 'now')
|
||||||
.mockImplementation(() => new Date('2020-01-14 14:50:35').getTime());
|
.mockImplementation(() => new Date('2020-01-14 14:50:35').getTime());
|
||||||
expect(EquipmentBooking.getCurrentDay().getTime()).toBe(
|
expect(EquipmentBooking.getCurrentDay().getTime()).toBe(
|
||||||
new Date('2020-01-14').getTime(),
|
new Date('2020-01-14').getTime()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,19 +27,19 @@ test('isEquipmentAvailable', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Petit barbecue',
|
name: 'Petit barbecue',
|
||||||
caution: 100,
|
caution: 100,
|
||||||
booked_at: [{begin: '2020-07-07', end: '2020-07-10'}],
|
booked_at: [{ begin: '2020-07-07', end: '2020-07-10' }],
|
||||||
};
|
};
|
||||||
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-07', end: '2020-07-09'}];
|
testDevice.booked_at = [{ begin: '2020-07-07', end: '2020-07-09' }];
|
||||||
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-09', end: '2020-07-10'}];
|
testDevice.booked_at = [{ begin: '2020-07-09', end: '2020-07-10' }];
|
||||||
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeFalse();
|
||||||
|
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-07', end: '2020-07-8'},
|
{ begin: '2020-07-07', end: '2020-07-8' },
|
||||||
{begin: '2020-07-10', end: '2020-07-12'},
|
{ begin: '2020-07-10', end: '2020-07-12' },
|
||||||
];
|
];
|
||||||
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeTrue();
|
expect(EquipmentBooking.isEquipmentAvailable(testDevice)).toBeTrue();
|
||||||
});
|
});
|
||||||
|
@ -55,29 +52,29 @@ test('getFirstEquipmentAvailability', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Petit barbecue',
|
name: 'Petit barbecue',
|
||||||
caution: 100,
|
caution: 100,
|
||||||
booked_at: [{begin: '2020-07-07', end: '2020-07-10'}],
|
booked_at: [{ begin: '2020-07-07', end: '2020-07-10' }],
|
||||||
};
|
};
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||||
).toBe(new Date('2020-07-11').getTime());
|
).toBe(new Date('2020-07-11').getTime());
|
||||||
testDevice.booked_at = [{begin: '2020-07-07', end: '2020-07-09'}];
|
testDevice.booked_at = [{ begin: '2020-07-07', end: '2020-07-09' }];
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||||
).toBe(new Date('2020-07-10').getTime());
|
).toBe(new Date('2020-07-10').getTime());
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-07', end: '2020-07-09'},
|
{ begin: '2020-07-07', end: '2020-07-09' },
|
||||||
{begin: '2020-07-10', end: '2020-07-16'},
|
{ begin: '2020-07-10', end: '2020-07-16' },
|
||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||||
).toBe(new Date('2020-07-17').getTime());
|
).toBe(new Date('2020-07-17').getTime());
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-07', end: '2020-07-09'},
|
{ begin: '2020-07-07', end: '2020-07-09' },
|
||||||
{begin: '2020-07-10', end: '2020-07-12'},
|
{ begin: '2020-07-10', end: '2020-07-12' },
|
||||||
{begin: '2020-07-14', end: '2020-07-16'},
|
{ begin: '2020-07-14', end: '2020-07-16' },
|
||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||||
).toBe(new Date('2020-07-13').getTime());
|
).toBe(new Date('2020-07-13').getTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,7 +82,7 @@ test('getRelativeDateString', () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(Date, 'now')
|
.spyOn(Date, 'now')
|
||||||
.mockImplementation(() => new Date('2020-07-09').getTime());
|
.mockImplementation(() => new Date('2020-07-09').getTime());
|
||||||
jest.spyOn(i18n, 't').mockImplementation((translationString: string) => {
|
jest.spyOn(i18n, 't').mockImplementation((translationString) => {
|
||||||
const prefix = 'screens.equipment.';
|
const prefix = 'screens.equipment.';
|
||||||
if (translationString === prefix + 'otherYear') return '0';
|
if (translationString === prefix + 'otherYear') return '0';
|
||||||
else if (translationString === prefix + 'otherMonth') return '1';
|
else if (translationString === prefix + 'otherMonth') return '1';
|
||||||
|
@ -95,25 +92,25 @@ test('getRelativeDateString', () => {
|
||||||
else return null;
|
else return null;
|
||||||
});
|
});
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-09'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-09'))).toBe(
|
||||||
'4',
|
'4'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-10'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-10'))).toBe(
|
||||||
'3',
|
'3'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-11'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-11'))).toBe(
|
||||||
'2',
|
'2'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-30'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-30'))).toBe(
|
||||||
'2',
|
'2'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-08-30'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-08-30'))).toBe(
|
||||||
'1',
|
'1'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-11-10'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2020-11-10'))).toBe(
|
||||||
'1',
|
'1'
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getRelativeDateString(new Date('2021-11-10'))).toBe(
|
expect(EquipmentBooking.getRelativeDateString(new Date('2021-11-10'))).toBe(
|
||||||
'0',
|
'0'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,7 +119,7 @@ test('getValidRange', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Petit barbecue',
|
name: 'Petit barbecue',
|
||||||
caution: 100,
|
caution: 100,
|
||||||
booked_at: [{begin: '2020-07-07', end: '2020-07-10'}],
|
booked_at: [{ begin: '2020-07-07', end: '2020-07-10' }],
|
||||||
};
|
};
|
||||||
let start = new Date('2020-07-11');
|
let start = new Date('2020-07-11');
|
||||||
let end = new Date('2020-07-15');
|
let end = new Date('2020-07-15');
|
||||||
|
@ -134,62 +131,62 @@ test('getValidRange', () => {
|
||||||
'2020-07-15',
|
'2020-07-15',
|
||||||
];
|
];
|
||||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-07', end: '2020-07-10'},
|
{ begin: '2020-07-07', end: '2020-07-10' },
|
||||||
{begin: '2020-07-13', end: '2020-07-15'},
|
{ begin: '2020-07-13', end: '2020-07-15' },
|
||||||
];
|
];
|
||||||
result = ['2020-07-11', '2020-07-12'];
|
result = ['2020-07-11', '2020-07-12'];
|
||||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-12', end: '2020-07-13'}];
|
testDevice.booked_at = [{ begin: '2020-07-12', end: '2020-07-13' }];
|
||||||
result = ['2020-07-11'];
|
result = ['2020-07-11'];
|
||||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
testDevice.booked_at = [{begin: '2020-07-07', end: '2020-07-12'}];
|
testDevice.booked_at = [{ begin: '2020-07-07', end: '2020-07-12' }];
|
||||||
result = ['2020-07-13', '2020-07-14', '2020-07-15'];
|
result = ['2020-07-13', '2020-07-14', '2020-07-15'];
|
||||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
start = new Date('2020-07-14');
|
start = new Date('2020-07-14');
|
||||||
end = new Date('2020-07-14');
|
end = new Date('2020-07-14');
|
||||||
result = ['2020-07-14'];
|
result = ['2020-07-14'];
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.getValidRange(start, start, testDevice),
|
EquipmentBooking.getValidRange(start, start, testDevice)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
start = new Date('2020-07-14');
|
start = new Date('2020-07-14');
|
||||||
end = new Date('2020-07-17');
|
end = new Date('2020-07-17');
|
||||||
result = ['2020-07-14', '2020-07-15', '2020-07-16', '2020-07-17'];
|
result = ['2020-07-14', '2020-07-15', '2020-07-16', '2020-07-17'];
|
||||||
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-17', end: '2020-07-17'}];
|
testDevice.booked_at = [{ begin: '2020-07-17', end: '2020-07-17' }];
|
||||||
result = ['2020-07-14', '2020-07-15', '2020-07-16'];
|
result = ['2020-07-14', '2020-07-15', '2020-07-16'];
|
||||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-12', end: '2020-07-13'},
|
{ begin: '2020-07-12', end: '2020-07-13' },
|
||||||
{begin: '2020-07-15', end: '2020-07-20'},
|
{ begin: '2020-07-15', end: '2020-07-20' },
|
||||||
];
|
];
|
||||||
start = new Date('2020-07-11');
|
start = new Date('2020-07-11');
|
||||||
end = new Date('2020-07-23');
|
end = new Date('2020-07-23');
|
||||||
result = ['2020-07-21', '2020-07-22', '2020-07-23'];
|
result = ['2020-07-21', '2020-07-22', '2020-07-23'];
|
||||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||||
result,
|
result
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -205,7 +202,7 @@ test('generateMarkedDates', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Petit barbecue',
|
name: 'Petit barbecue',
|
||||||
caution: 100,
|
caution: 100,
|
||||||
booked_at: [{begin: '2020-07-07', end: '2020-07-10'}],
|
booked_at: [{ begin: '2020-07-07', end: '2020-07-10' }],
|
||||||
};
|
};
|
||||||
let start = new Date('2020-07-11');
|
let start = new Date('2020-07-11');
|
||||||
let end = new Date('2020-07-13');
|
let end = new Date('2020-07-13');
|
||||||
|
@ -228,7 +225,7 @@ test('generateMarkedDates', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
result = {
|
result = {
|
||||||
'2020-07-11': {
|
'2020-07-11': {
|
||||||
|
@ -248,7 +245,7 @@ test('generateMarkedDates', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(false, theme, range),
|
EquipmentBooking.generateMarkedDates(false, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
result = {
|
result = {
|
||||||
'2020-07-11': {
|
'2020-07-11': {
|
||||||
|
@ -269,10 +266,10 @@ test('generateMarkedDates', () => {
|
||||||
};
|
};
|
||||||
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(false, theme, range),
|
EquipmentBooking.generateMarkedDates(false, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-13', end: '2020-07-15'}];
|
testDevice.booked_at = [{ begin: '2020-07-13', end: '2020-07-15' }];
|
||||||
result = {
|
result = {
|
||||||
'2020-07-11': {
|
'2020-07-11': {
|
||||||
startingDay: true,
|
startingDay: true,
|
||||||
|
@ -287,10 +284,10 @@ test('generateMarkedDates', () => {
|
||||||
};
|
};
|
||||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
|
|
||||||
testDevice.booked_at = [{begin: '2020-07-12', end: '2020-07-13'}];
|
testDevice.booked_at = [{ begin: '2020-07-12', end: '2020-07-13' }];
|
||||||
result = {
|
result = {
|
||||||
'2020-07-11': {
|
'2020-07-11': {
|
||||||
startingDay: true,
|
startingDay: true,
|
||||||
|
@ -300,12 +297,12 @@ test('generateMarkedDates', () => {
|
||||||
};
|
};
|
||||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
|
|
||||||
testDevice.booked_at = [
|
testDevice.booked_at = [
|
||||||
{begin: '2020-07-12', end: '2020-07-13'},
|
{ begin: '2020-07-12', end: '2020-07-13' },
|
||||||
{begin: '2020-07-15', end: '2020-07-20'},
|
{ begin: '2020-07-15', end: '2020-07-20' },
|
||||||
];
|
];
|
||||||
start = new Date('2020-07-11');
|
start = new Date('2020-07-11');
|
||||||
end = new Date('2020-07-23');
|
end = new Date('2020-07-23');
|
||||||
|
@ -318,7 +315,7 @@ test('generateMarkedDates', () => {
|
||||||
};
|
};
|
||||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
|
@ -340,6 +337,6 @@ test('generateMarkedDates', () => {
|
||||||
};
|
};
|
||||||
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
||||||
expect(
|
expect(
|
||||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||||
).toStrictEqual(result);
|
).toStrictEqual(result);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import * as Planning from '../../src/utils/Planning';
|
import * as Planning from '../../src/utils/Planning';
|
||||||
|
|
||||||
test('isDescriptionEmpty', () => {
|
test('isDescriptionEmpty', () => {
|
||||||
|
@ -24,7 +21,7 @@ test('isEventDateStringFormatValid', () => {
|
||||||
expect(Planning.isEventDateStringFormatValid('3214-64-12 01:16')).toBeTrue();
|
expect(Planning.isEventDateStringFormatValid('3214-64-12 01:16')).toBeTrue();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventDateStringFormatValid('3214-64-12 01:16:00'),
|
Planning.isEventDateStringFormatValid('3214-64-12 01:16:00')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(Planning.isEventDateStringFormatValid('3214-64-12 1:16')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('3214-64-12 1:16')).toBeFalse();
|
||||||
expect(Planning.isEventDateStringFormatValid('3214-f4-12 01:16')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('3214-f4-12 01:16')).toBeFalse();
|
||||||
|
@ -32,7 +29,7 @@ test('isEventDateStringFormatValid', () => {
|
||||||
expect(Planning.isEventDateStringFormatValid('2020-03-21')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('2020-03-21')).toBeFalse();
|
||||||
expect(Planning.isEventDateStringFormatValid('2020-03-21 truc')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('2020-03-21 truc')).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventDateStringFormatValid('3214-64-12 1:16:65'),
|
Planning.isEventDateStringFormatValid('3214-64-12 1:16:65')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(Planning.isEventDateStringFormatValid('garbage')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('garbage')).toBeFalse();
|
||||||
expect(Planning.isEventDateStringFormatValid('')).toBeFalse();
|
expect(Planning.isEventDateStringFormatValid('')).toBeFalse();
|
||||||
|
@ -65,17 +62,17 @@ test('getFormattedEventTime', () => {
|
||||||
expect(Planning.getFormattedEventTime(undefined, undefined)).toBe('/ - /');
|
expect(Planning.getFormattedEventTime(undefined, undefined)).toBe('/ - /');
|
||||||
expect(Planning.getFormattedEventTime('20:30', '23:00')).toBe('/ - /');
|
expect(Planning.getFormattedEventTime('20:30', '23:00')).toBe('/ - /');
|
||||||
expect(Planning.getFormattedEventTime('2020-03-30', '2020-03-31')).toBe(
|
expect(Planning.getFormattedEventTime('2020-03-30', '2020-03-31')).toBe(
|
||||||
'/ - /',
|
'/ - /'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
Planning.getFormattedEventTime('2020-03-21 09:00', '2020-03-21 09:00'),
|
Planning.getFormattedEventTime('2020-03-21 09:00', '2020-03-21 09:00')
|
||||||
).toBe('09:00');
|
).toBe('09:00');
|
||||||
expect(
|
expect(
|
||||||
Planning.getFormattedEventTime('2020-03-21 09:00', '2020-03-22 17:00'),
|
Planning.getFormattedEventTime('2020-03-21 09:00', '2020-03-22 17:00')
|
||||||
).toBe('09:00 - 23:59');
|
).toBe('09:00 - 23:59');
|
||||||
expect(
|
expect(
|
||||||
Planning.getFormattedEventTime('2020-03-30 20:30', '2020-03-30 23:00'),
|
Planning.getFormattedEventTime('2020-03-30 20:30', '2020-03-30 23:00')
|
||||||
).toBe('20:30 - 23:00');
|
).toBe('20:30 - 23:00');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,38 +87,38 @@ test('getDateOnlyString', () => {
|
||||||
|
|
||||||
test('isEventBefore', () => {
|
test('isEventBefore', () => {
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 09:00', '2020-03-21 10:00'),
|
Planning.isEventBefore('2020-03-21 09:00', '2020-03-21 10:00')
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 10:15'),
|
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 10:15')
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:15', '2021-03-21 10:15'),
|
Planning.isEventBefore('2020-03-21 10:15', '2021-03-21 10:15')
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:15', '2020-05-21 10:15'),
|
Planning.isEventBefore('2020-03-21 10:15', '2020-05-21 10:15')
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:15', '2020-03-30 10:15'),
|
Planning.isEventBefore('2020-03-21 10:15', '2020-03-30 10:15')
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 10:00'),
|
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 10:00')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 09:00'),
|
Planning.isEventBefore('2020-03-21 10:00', '2020-03-21 09:00')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-21 10:15', '2020-03-21 10:00'),
|
Planning.isEventBefore('2020-03-21 10:15', '2020-03-21 10:00')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2021-03-21 10:15', '2020-03-21 10:15'),
|
Planning.isEventBefore('2021-03-21 10:15', '2020-03-21 10:15')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-05-21 10:15', '2020-03-21 10:15'),
|
Planning.isEventBefore('2020-05-21 10:15', '2020-03-21 10:15')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
Planning.isEventBefore('2020-03-30 10:15', '2020-03-21 10:15'),
|
Planning.isEventBefore('2020-03-30 10:15', '2020-03-21 10:15')
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
|
|
||||||
expect(Planning.isEventBefore('garbage', '2020-03-21 10:15')).toBeFalse();
|
expect(Planning.isEventBefore('garbage', '2020-03-21 10:15')).toBeFalse();
|
||||||
|
@ -162,25 +159,25 @@ test('generateEmptyCalendar', () => {
|
||||||
|
|
||||||
test('pushEventInOrder', () => {
|
test('pushEventInOrder', () => {
|
||||||
let eventArray = [];
|
let eventArray = [];
|
||||||
let event1 = {date_begin: '2020-01-14 09:15'};
|
let event1 = { date_begin: '2020-01-14 09:15' };
|
||||||
Planning.pushEventInOrder(eventArray, event1);
|
Planning.pushEventInOrder(eventArray, event1);
|
||||||
expect(eventArray.length).toBe(1);
|
expect(eventArray.length).toBe(1);
|
||||||
expect(eventArray[0]).toBe(event1);
|
expect(eventArray[0]).toBe(event1);
|
||||||
|
|
||||||
let event2 = {date_begin: '2020-01-14 10:15'};
|
let event2 = { date_begin: '2020-01-14 10:15' };
|
||||||
Planning.pushEventInOrder(eventArray, event2);
|
Planning.pushEventInOrder(eventArray, event2);
|
||||||
expect(eventArray.length).toBe(2);
|
expect(eventArray.length).toBe(2);
|
||||||
expect(eventArray[0]).toBe(event1);
|
expect(eventArray[0]).toBe(event1);
|
||||||
expect(eventArray[1]).toBe(event2);
|
expect(eventArray[1]).toBe(event2);
|
||||||
|
|
||||||
let event3 = {date_begin: '2020-01-14 10:15', title: 'garbage'};
|
let event3 = { date_begin: '2020-01-14 10:15', title: 'garbage' };
|
||||||
Planning.pushEventInOrder(eventArray, event3);
|
Planning.pushEventInOrder(eventArray, event3);
|
||||||
expect(eventArray.length).toBe(3);
|
expect(eventArray.length).toBe(3);
|
||||||
expect(eventArray[0]).toBe(event1);
|
expect(eventArray[0]).toBe(event1);
|
||||||
expect(eventArray[1]).toBe(event2);
|
expect(eventArray[1]).toBe(event2);
|
||||||
expect(eventArray[2]).toBe(event3);
|
expect(eventArray[2]).toBe(event3);
|
||||||
|
|
||||||
let event4 = {date_begin: '2020-01-13 09:00'};
|
let event4 = { date_begin: '2020-01-13 09:00' };
|
||||||
Planning.pushEventInOrder(eventArray, event4);
|
Planning.pushEventInOrder(eventArray, event4);
|
||||||
expect(eventArray.length).toBe(4);
|
expect(eventArray.length).toBe(4);
|
||||||
expect(eventArray[0]).toBe(event4);
|
expect(eventArray[0]).toBe(event4);
|
||||||
|
@ -194,11 +191,11 @@ test('generateEventAgenda', () => {
|
||||||
.spyOn(Date, 'now')
|
.spyOn(Date, 'now')
|
||||||
.mockImplementation(() => new Date('2020-01-14T00:00:00.000Z').getTime());
|
.mockImplementation(() => new Date('2020-01-14T00:00:00.000Z').getTime());
|
||||||
let eventList = [
|
let eventList = [
|
||||||
{date_begin: '2020-01-14 09:15'},
|
{ date_begin: '2020-01-14 09:15' },
|
||||||
{date_begin: '2020-02-01 09:15'},
|
{ date_begin: '2020-02-01 09:15' },
|
||||||
{date_begin: '2020-01-15 09:15'},
|
{ date_begin: '2020-01-15 09:15' },
|
||||||
{date_begin: '2020-02-01 09:30'},
|
{ date_begin: '2020-02-01 09:30' },
|
||||||
{date_begin: '2020-02-01 08:30'},
|
{ date_begin: '2020-02-01 08:30' },
|
||||||
];
|
];
|
||||||
const calendar = Planning.generateEventAgenda(eventList, 2);
|
const calendar = Planning.generateEventAgenda(eventList, 2);
|
||||||
expect(calendar['2020-01-14'].length).toBe(1);
|
expect(calendar['2020-01-14'].length).toBe(1);
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {
|
import {
|
||||||
getCleanedMachineWatched,
|
getCleanedMachineWatched,
|
||||||
getMachineEndDate,
|
getMachineEndDate,
|
||||||
|
@ -15,19 +12,19 @@ test('getMachineEndDate', () => {
|
||||||
let expectDate = new Date('2020-01-14T15:00:00.000Z');
|
let expectDate = new Date('2020-01-14T15:00:00.000Z');
|
||||||
expectDate.setHours(23);
|
expectDate.setHours(23);
|
||||||
expectDate.setMinutes(10);
|
expectDate.setMinutes(10);
|
||||||
expect(getMachineEndDate({endTime: '23:10'}).getTime()).toBe(
|
expect(getMachineEndDate({ endTime: '23:10' }).getTime()).toBe(
|
||||||
expectDate.getTime(),
|
expectDate.getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
expectDate.setHours(16);
|
expectDate.setHours(16);
|
||||||
expectDate.setMinutes(30);
|
expectDate.setMinutes(30);
|
||||||
expect(getMachineEndDate({endTime: '16:30'}).getTime()).toBe(
|
expect(getMachineEndDate({ endTime: '16:30' }).getTime()).toBe(
|
||||||
expectDate.getTime(),
|
expectDate.getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(getMachineEndDate({endTime: '15:30'})).toBeNull();
|
expect(getMachineEndDate({ endTime: '15:30' })).toBeNull();
|
||||||
|
|
||||||
expect(getMachineEndDate({endTime: '13:10'})).toBeNull();
|
expect(getMachineEndDate({ endTime: '13:10' })).toBeNull();
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.spyOn(Date, 'now')
|
.spyOn(Date, 'now')
|
||||||
|
@ -35,8 +32,8 @@ test('getMachineEndDate', () => {
|
||||||
expectDate = new Date('2020-01-14T23:00:00.000Z');
|
expectDate = new Date('2020-01-14T23:00:00.000Z');
|
||||||
expectDate.setHours(0);
|
expectDate.setHours(0);
|
||||||
expectDate.setMinutes(30);
|
expectDate.setMinutes(30);
|
||||||
expect(getMachineEndDate({endTime: '00:30'}).getTime()).toBe(
|
expect(getMachineEndDate({ endTime: '00:30' }).getTime()).toBe(
|
||||||
expectDate.getTime(),
|
expectDate.getTime()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,16 +49,16 @@ test('isMachineWatched', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
isMachineWatched({number: '0', endTime: '23:30'}, machineList),
|
isMachineWatched({ number: '0', endTime: '23:30' }, machineList)
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
isMachineWatched({number: '1', endTime: '20:30'}, machineList),
|
isMachineWatched({ number: '1', endTime: '20:30' }, machineList)
|
||||||
).toBeTrue();
|
).toBeTrue();
|
||||||
expect(
|
expect(
|
||||||
isMachineWatched({number: '3', endTime: '20:30'}, machineList),
|
isMachineWatched({ number: '3', endTime: '20:30' }, machineList)
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
expect(
|
expect(
|
||||||
isMachineWatched({number: '1', endTime: '23:30'}, machineList),
|
isMachineWatched({ number: '1', endTime: '23:30' }, machineList)
|
||||||
).toBeFalse();
|
).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,8 +71,8 @@ test('getMachineOfId', () => {
|
||||||
number: '1',
|
number: '1',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(getMachineOfId('0', machineList)).toStrictEqual({number: '0'});
|
expect(getMachineOfId('0', machineList)).toStrictEqual({ number: '0' });
|
||||||
expect(getMachineOfId('1', machineList)).toStrictEqual({number: '1'});
|
expect(getMachineOfId('1', machineList)).toStrictEqual({ number: '1' });
|
||||||
expect(getMachineOfId('3', machineList)).toBeNull();
|
expect(getMachineOfId('3', machineList)).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -110,7 +107,7 @@ test('getCleanedMachineWatched', () => {
|
||||||
];
|
];
|
||||||
let cleanedList = watchList;
|
let cleanedList = watchList;
|
||||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||||
cleanedList,
|
cleanedList
|
||||||
);
|
);
|
||||||
|
|
||||||
watchList = [
|
watchList = [
|
||||||
|
@ -138,7 +135,7 @@ test('getCleanedMachineWatched', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||||
cleanedList,
|
cleanedList
|
||||||
);
|
);
|
||||||
|
|
||||||
watchList = [
|
watchList = [
|
||||||
|
@ -162,6 +159,6 @@ test('getCleanedMachineWatched', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||||
cleanedList,
|
cleanedList
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* eslint-disable */
|
import { isApiResponseValid } from '../../src/utils/WebData';
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {isApiResponseValid} from '../../src/utils/WebData';
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
const fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
||||||
|
|
||||||
test('isRequestResponseValid', () => {
|
test('isRequestResponseValid', () => {
|
||||||
|
@ -23,7 +21,7 @@ test('isRequestResponseValid', () => {
|
||||||
expect(isApiResponseValid(json)).toBeTrue();
|
expect(isApiResponseValid(json)).toBeTrue();
|
||||||
json = {
|
json = {
|
||||||
error: 50,
|
error: 50,
|
||||||
data: {truc: 'machin'},
|
data: { truc: 'machin' },
|
||||||
};
|
};
|
||||||
expect(isApiResponseValid(json)).toBeTrue();
|
expect(isApiResponseValid(json)).toBeTrue();
|
||||||
json = {
|
json = {
|
||||||
|
@ -32,7 +30,7 @@ test('isRequestResponseValid', () => {
|
||||||
expect(isApiResponseValid(json)).toBeFalse();
|
expect(isApiResponseValid(json)).toBeFalse();
|
||||||
json = {
|
json = {
|
||||||
error: 'coucou',
|
error: 'coucou',
|
||||||
data: {truc: 'machin'},
|
data: { truc: 'machin' },
|
||||||
};
|
};
|
||||||
expect(isApiResponseValid(json)).toBeFalse();
|
expect(isApiResponseValid(json)).toBeFalse();
|
||||||
json = {
|
json = {
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "Removing node_modules..."
|
|
||||||
rm -rf node_modules/
|
|
||||||
echo -e "Done\n"
|
|
||||||
|
|
||||||
echo "Removing locks..."
|
|
||||||
rm -f package-lock.json && rm -f yarn.lock
|
|
||||||
echo -e "Done\n"
|
|
||||||
|
|
||||||
#echo "Verifying npm cache..."
|
|
||||||
#npm cache verify
|
|
||||||
#echo -e "Done\n"
|
|
||||||
|
|
||||||
echo "Installing dependencies..."
|
|
||||||
npm install
|
|
||||||
echo -e "Done\n"
|
|
||||||
|
|
5
index.js
5
index.js
|
@ -21,9 +21,8 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AppRegistry} from 'react-native';
|
import { AppRegistry } from 'react-native';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import {name as appName} from './app.json';
|
import { name as appName } from './app.json';
|
||||||
|
|
||||||
// eslint-disable-next-line flowtype/require-return-type
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App);
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
transformer: {
|
transformer: {
|
||||||
// eslint-disable-next-line flowtype/require-return-type
|
|
||||||
getTransformOptions: async () => ({
|
getTransformOptions: async () => ({
|
||||||
transform: {
|
transform: {
|
||||||
experimentalImportSupport: false,
|
experimentalImportSupport: false,
|
||||||
inlineRequires: false,
|
inlineRequires: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
4676
package-lock.json
generated
4676
package-lock.json
generated
File diff suppressed because it is too large
Load diff
171
package.json
171
package.json
|
@ -3,12 +3,113 @@
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-native start",
|
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
"android-release": "react-native run-android --variant=release",
|
"android-release": "react-native run-android --variant=release",
|
||||||
"ios": "react-native run-ios",
|
"ios": "react-native run-ios",
|
||||||
|
"start": "react-native start",
|
||||||
|
"start-no-cache": "react-native start --reset-cache",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
|
"typescript": "tsc --noEmit",
|
||||||
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||||
|
"lint-fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||||
|
"full-check": "npm run typescript && npm run lint && npm run test",
|
||||||
|
"pod": "cd ios && pod install && cd ..",
|
||||||
|
"bundle": "npm run full-check && cd android && ./gradlew bundleRelease",
|
||||||
|
"postversion": "react-native-version"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nartc/react-native-barcode-mask": "1.2.0",
|
||||||
|
"@react-native-community/async-storage": "1.12.0",
|
||||||
|
"@react-native-community/masked-view": "0.1.10",
|
||||||
|
"@react-native-community/push-notification-ios": "1.5.0",
|
||||||
|
"@react-native-community/slider": "3.0.3",
|
||||||
|
"@react-navigation/bottom-tabs": "5.8.0",
|
||||||
|
"@react-navigation/native": "5.7.3",
|
||||||
|
"@react-navigation/stack": "5.9.0",
|
||||||
|
"i18n-js": "3.7.1",
|
||||||
|
"react": "16.13.1",
|
||||||
|
"react-native": "0.63.2",
|
||||||
|
"react-native-animatable": "1.3.3",
|
||||||
|
"react-native-app-intro-slider": "4.0.4",
|
||||||
|
"react-native-appearance": "0.3.4",
|
||||||
|
"react-native-autolink": "3.0.0",
|
||||||
|
"react-native-calendars": "1.403.0",
|
||||||
|
"react-native-camera": "3.40.0",
|
||||||
|
"react-native-collapsible": "1.5.3",
|
||||||
|
"react-native-gesture-handler": "1.8.0",
|
||||||
|
"react-native-image-zoom-viewer": "3.0.1",
|
||||||
|
"react-native-keychain": "4.0.5",
|
||||||
|
"react-native-linear-gradient": "2.5.6",
|
||||||
|
"react-native-localize": "1.4.1",
|
||||||
|
"react-native-modalize": "2.0.6",
|
||||||
|
"react-native-paper": "4.2.0",
|
||||||
|
"react-native-permissions": "2.2.1",
|
||||||
|
"react-native-push-notification": "5.1.1",
|
||||||
|
"react-native-reanimated": "1.13.0",
|
||||||
|
"react-native-render-html": "4.2.3",
|
||||||
|
"react-native-safe-area-context": "3.1.8",
|
||||||
|
"react-native-screens": "2.11.0",
|
||||||
|
"react-native-splash-screen": "3.2.0",
|
||||||
|
"react-native-vector-icons": "7.1.0",
|
||||||
|
"react-native-webview": "10.9.0",
|
||||||
|
"react-navigation-collapsible": "5.6.4",
|
||||||
|
"react-navigation-header-buttons": "5.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "7.11.0",
|
||||||
|
"@babel/runtime": "7.11.0",
|
||||||
|
"@react-native-community/eslint-config": "1.1.0",
|
||||||
|
"@types/i18n-js": "3.0.3",
|
||||||
|
"@types/jest": "25.2.3",
|
||||||
|
"@types/react-native": "0.63.2",
|
||||||
|
"@types/react-native-calendars": "1.20.10",
|
||||||
|
"@types/react-native-vector-icons": "6.4.6",
|
||||||
|
"@types/react-test-renderer": "16.9.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "2.27.0",
|
||||||
|
"@typescript-eslint/parser": "2.27.0",
|
||||||
|
"babel-jest": "25.1.0",
|
||||||
|
"eslint": "7.2.0",
|
||||||
|
"jest": "25.1.0",
|
||||||
|
"jest-extended": "0.11.5",
|
||||||
|
"jest-fetch-mock": "3.0.3",
|
||||||
|
"metro-react-native-babel-preset": "0.59.0",
|
||||||
|
"prettier": "2.0.5",
|
||||||
|
"react-native-version": "4.0.0",
|
||||||
|
"react-test-renderer": "16.13.1",
|
||||||
|
"typescript": "3.8.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"@react-native-community",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"quoteProps": "consistent",
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eslintIgnore": [
|
||||||
|
"node_modules/"
|
||||||
|
],
|
||||||
|
"prettier": {
|
||||||
|
"quoteProps": "consistent",
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"useTabs": false
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "react-native",
|
"preset": "react-native",
|
||||||
|
@ -23,71 +124,5 @@
|
||||||
"setupFilesAfterEnv": [
|
"setupFilesAfterEnv": [
|
||||||
"jest-extended"
|
"jest-extended"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@nartc/react-native-barcode-mask": "^1.2.0",
|
|
||||||
"@react-native-community/async-storage": "^1.12.0",
|
|
||||||
"@react-native-community/masked-view": "^0.1.10",
|
|
||||||
"@react-native-community/push-notification-ios": "^1.5.0",
|
|
||||||
"@react-native-community/slider": "^3.0.3",
|
|
||||||
"@react-navigation/bottom-tabs": "^5.8.0",
|
|
||||||
"@react-navigation/native": "^5.7.3",
|
|
||||||
"@react-navigation/stack": "^5.9.0",
|
|
||||||
"i18n-js": "^3.7.1",
|
|
||||||
"react": "16.13.1",
|
|
||||||
"react-native": "0.63.2",
|
|
||||||
"react-native-animatable": "^1.3.3",
|
|
||||||
"react-native-app-intro-slider": "^4.0.4",
|
|
||||||
"react-native-appearance": "^0.3.4",
|
|
||||||
"react-native-autolink": "^3.0.0",
|
|
||||||
"react-native-calendars": "^1.403.0",
|
|
||||||
"react-native-camera": "^3.40.0",
|
|
||||||
"react-native-collapsible": "^1.5.3",
|
|
||||||
"react-native-gesture-handler": "^1.8.0",
|
|
||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
|
||||||
"react-native-keychain": "4.0.5",
|
|
||||||
"react-native-linear-gradient": "^2.5.6",
|
|
||||||
"react-native-localize": "^1.4.1",
|
|
||||||
"react-native-modalize": "^2.0.6",
|
|
||||||
"react-native-paper": "^4.2.0",
|
|
||||||
"react-native-permissions": "^2.2.1",
|
|
||||||
"react-native-push-notification": "^5.1.1",
|
|
||||||
"react-native-reanimated": "^1.13.0",
|
|
||||||
"react-native-render-html": "^4.2.3",
|
|
||||||
"react-native-safe-area-context": "^3.1.8",
|
|
||||||
"react-native-screens": "^2.11.0",
|
|
||||||
"react-native-splash-screen": "^3.2.0",
|
|
||||||
"react-native-vector-icons": "^7.1.0",
|
|
||||||
"react-native-webview": "^10.9.0",
|
|
||||||
"react-navigation-collapsible": "^5.6.4",
|
|
||||||
"react-navigation-header-buttons": "^5.0.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.11.0",
|
|
||||||
"@babel/runtime": "^7.11.0",
|
|
||||||
"@react-native-community/eslint-config": "^1.1.0",
|
|
||||||
"@types/i18n-js": "^3.0.3",
|
|
||||||
"@types/jest": "^25.2.3",
|
|
||||||
"@types/react-native": "^0.63.2",
|
|
||||||
"@types/react-native-calendars": "^1.20.10",
|
|
||||||
"@types/react-native-vector-icons": "^6.4.6",
|
|
||||||
"@types/react-test-renderer": "^16.9.2",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^2.27.0",
|
|
||||||
"@typescript-eslint/parser": "^2.27.0",
|
|
||||||
"babel-jest": "^25.1.0",
|
|
||||||
"eslint": "^7.2.0",
|
|
||||||
"eslint-config-airbnb": "^18.2.0",
|
|
||||||
"eslint-config-prettier": "^6.11.0",
|
|
||||||
"eslint-plugin-flowtype": "^5.2.0",
|
|
||||||
"eslint-plugin-import": "^2.22.0",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
|
||||||
"eslint-plugin-react": "^7.20.5",
|
|
||||||
"eslint-plugin-react-hooks": "^4.0.0",
|
|
||||||
"jest": "^25.1.0",
|
|
||||||
"jest-extended": "^0.11.5",
|
|
||||||
"metro-react-native-babel-preset": "^0.59.0",
|
|
||||||
"prettier": "2.0.5",
|
|
||||||
"react-test-renderer": "16.13.1",
|
|
||||||
"typescript": "^3.8.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import ConnectionManager from '../../managers/ConnectionManager';
|
import ConnectionManager from '../../managers/ConnectionManager';
|
||||||
import {ERROR_TYPE} from '../../utils/WebData';
|
import { ERROR_TYPE } from '../../utils/WebData';
|
||||||
import ErrorView from '../Screens/ErrorView';
|
import ErrorView from '../Screens/ErrorView';
|
||||||
import BasicLoadingScreen from '../Screens/BasicLoadingScreen';
|
import BasicLoadingScreen from '../Screens/BasicLoadingScreen';
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
* @param error The error code received
|
* @param error The error code received
|
||||||
*/
|
*/
|
||||||
onRequestFinished(data: T | null, index: number, error?: number) {
|
onRequestFinished(data: T | null, index: number, error?: number) {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
if (index >= 0 && index < props.requests.length) {
|
if (index >= 0 && index < props.requests.length) {
|
||||||
this.fetchedData[index] = data;
|
this.fetchedData[index] = data;
|
||||||
this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS;
|
this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS;
|
||||||
|
@ -101,7 +101,7 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.allRequestsFinished()) {
|
if (this.allRequestsFinished()) {
|
||||||
this.setState({loading: false});
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
* @return {number} The error code or ERROR_TYPE.SUCCESS if no error was found
|
* @return {number} The error code or ERROR_TYPE.SUCCESS if no error was found
|
||||||
*/
|
*/
|
||||||
getError(): number {
|
getError(): number {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
for (let i = 0; i < this.errors.length; i += 1) {
|
for (let i = 0; i < this.errors.length; i += 1) {
|
||||||
if (
|
if (
|
||||||
this.errors[i] !== ERROR_TYPE.SUCCESS &&
|
this.errors[i] !== ERROR_TYPE.SUCCESS &&
|
||||||
|
@ -131,7 +131,7 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getErrorRender() {
|
getErrorRender() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const errorCode = this.getError();
|
const errorCode = this.getError();
|
||||||
let shouldOverride = false;
|
let shouldOverride = false;
|
||||||
let override = null;
|
let override = null;
|
||||||
|
@ -166,9 +166,9 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
* If the user is logged in, send all requests.
|
* If the user is logged in, send all requests.
|
||||||
*/
|
*/
|
||||||
fetchData = () => {
|
fetchData = () => {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
if (!state.loading) {
|
if (!state.loading) {
|
||||||
this.setState({loading: true});
|
this.setState({ loading: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.connectionManager.isLoggedIn()) {
|
if (this.connectionManager.isLoggedIn()) {
|
||||||
|
@ -176,11 +176,11 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
this.connectionManager
|
this.connectionManager
|
||||||
.authenticatedRequest<T>(
|
.authenticatedRequest<T>(
|
||||||
props.requests[i].link,
|
props.requests[i].link,
|
||||||
props.requests[i].params,
|
props.requests[i].params
|
||||||
)
|
)
|
||||||
.then((response: T): void => this.onRequestFinished(response, i))
|
.then((response: T): void => this.onRequestFinished(response, i))
|
||||||
.catch((error: number): void =>
|
.catch((error: number): void =>
|
||||||
this.onRequestFinished(null, i, error),
|
this.onRequestFinished(null, i, error)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -213,7 +213,7 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
if (state.loading) {
|
if (state.loading) {
|
||||||
return <BasicLoadingScreen />;
|
return <BasicLoadingScreen />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import * as React from 'react';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import LoadingConfirmDialog from '../Dialogs/LoadingConfirmDialog';
|
import LoadingConfirmDialog from '../Dialogs/LoadingConfirmDialog';
|
||||||
import ConnectionManager from '../../managers/ConnectionManager';
|
import ConnectionManager from '../../managers/ConnectionManager';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -37,7 +37,7 @@ function LogoutDialog(props: PropsType) {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
navigation.reset({
|
navigation.reset({
|
||||||
index: 0,
|
index: 0,
|
||||||
routes: [{name: 'main'}],
|
routes: [{ name: 'main' }],
|
||||||
});
|
});
|
||||||
props.onDismiss();
|
props.onDismiss();
|
||||||
resolve();
|
resolve();
|
||||||
|
|
|
@ -18,24 +18,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {Headline, useTheme} from 'react-native-paper';
|
import { Headline, useTheme } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
headline: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function VoteNotAvailable() {
|
function VoteNotAvailable() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={styles.container}>
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
}}>
|
|
||||||
<Headline
|
<Headline
|
||||||
style={{
|
style={{
|
||||||
color: theme.colors.textDisabled,
|
color: theme.colors.textDisabled,
|
||||||
textAlign: 'center',
|
...styles.headline,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{i18n.t('screens.vote.noVote')}
|
{i18n.t('screens.vote.noVote')}
|
||||||
</Headline>
|
</Headline>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -26,9 +26,9 @@ import {
|
||||||
Subheading,
|
Subheading,
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {FlatList, StyleSheet} from 'react-native';
|
import { FlatList, StyleSheet } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
|
import type { VoteTeamType } from '../../../screens/Amicale/VoteScreen';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
teams: Array<VoteTeamType>;
|
teams: Array<VoteTeamType>;
|
||||||
|
@ -40,8 +40,11 @@ const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
icon: {
|
itemCard: {
|
||||||
backgroundColor: 'transparent',
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
padding: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -86,16 +89,18 @@ class VoteResults extends React.Component<PropsType> {
|
||||||
|
|
||||||
voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
|
voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
|
||||||
|
|
||||||
resultRenderItem = ({item}: {item: VoteTeamType}) => {
|
resultRenderItem = ({ item }: { item: VoteTeamType }) => {
|
||||||
const isWinner = this.winnerIds.indexOf(item.id) !== -1;
|
const isWinner = this.winnerIds.indexOf(item.id) !== -1;
|
||||||
const isDraw = this.winnerIds.length > 1;
|
const isDraw = this.winnerIds.length > 1;
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
|
const elevation = isWinner ? 5 : 3;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
style={{
|
style={{
|
||||||
marginTop: 10,
|
...styles.itemCard,
|
||||||
elevation: isWinner ? 5 : 3,
|
elevation: elevation,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<List.Item
|
<List.Item
|
||||||
title={item.name}
|
title={item.name}
|
||||||
description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`}
|
description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`}
|
||||||
|
@ -113,7 +118,7 @@ class VoteResults extends React.Component<PropsType> {
|
||||||
? props.theme.colors.primary
|
? props.theme.colors.primary
|
||||||
: props.theme.colors.text,
|
: props.theme.colors.text,
|
||||||
}}
|
}}
|
||||||
style={{padding: 0}}
|
style={styles.item}
|
||||||
/>
|
/>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
progress={item.votes / this.totalVotes}
|
progress={item.votes / this.totalVotes}
|
||||||
|
@ -124,7 +129,7 @@ class VoteResults extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, Button, Card, RadioButton} from 'react-native-paper';
|
import { Avatar, Button, Card, RadioButton } from 'react-native-paper';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import { FlatList, StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import ConnectionManager from '../../../managers/ConnectionManager';
|
import ConnectionManager from '../../../managers/ConnectionManager';
|
||||||
import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog';
|
import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog';
|
||||||
import ErrorDialog from '../../Dialogs/ErrorDialog';
|
import ErrorDialog from '../../Dialogs/ErrorDialog';
|
||||||
import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
|
import type { VoteTeamType } from '../../../screens/Amicale/VoteScreen';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
teams: Array<VoteTeamType>;
|
teams: Array<VoteTeamType>;
|
||||||
|
@ -43,8 +43,8 @@ const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
icon: {
|
button: {
|
||||||
backgroundColor: 'transparent',
|
marginLeft: 'auto',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -63,28 +63,28 @@ export default class VoteSelect extends React.PureComponent<
|
||||||
}
|
}
|
||||||
|
|
||||||
onVoteSelectionChange = (teamName: string): void =>
|
onVoteSelectionChange = (teamName: string): void =>
|
||||||
this.setState({selectedTeam: teamName});
|
this.setState({ selectedTeam: teamName });
|
||||||
|
|
||||||
voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
|
voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
|
||||||
|
|
||||||
voteRenderItem = ({item}: {item: VoteTeamType}) => (
|
voteRenderItem = ({ item }: { item: VoteTeamType }) => (
|
||||||
<RadioButton.Item label={item.name} value={item.id.toString()} />
|
<RadioButton.Item label={item.name} value={item.id.toString()} />
|
||||||
);
|
);
|
||||||
|
|
||||||
showVoteDialog = (): void => this.setState({voteDialogVisible: true});
|
showVoteDialog = (): void => this.setState({ voteDialogVisible: true });
|
||||||
|
|
||||||
onVoteDialogDismiss = (): void => this.setState({voteDialogVisible: false});
|
onVoteDialogDismiss = (): void => this.setState({ voteDialogVisible: false });
|
||||||
|
|
||||||
onVoteDialogAccept = async (): Promise<void> => {
|
onVoteDialogAccept = async (): Promise<void> => {
|
||||||
return new Promise((resolve: () => void) => {
|
return new Promise((resolve: () => void) => {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
ConnectionManager.getInstance()
|
ConnectionManager.getInstance()
|
||||||
.authenticatedRequest('elections/vote', {
|
.authenticatedRequest('elections/vote', {
|
||||||
team: parseInt(state.selectedTeam, 10),
|
team: parseInt(state.selectedTeam, 10),
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onVoteDialogDismiss();
|
this.onVoteDialogDismiss();
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.onVoteSuccess();
|
props.onVoteSuccess();
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
|
@ -103,13 +103,13 @@ export default class VoteSelect extends React.PureComponent<
|
||||||
});
|
});
|
||||||
|
|
||||||
onErrorDialogDismiss = () => {
|
onErrorDialogDismiss = () => {
|
||||||
this.setState({errorDialogVisible: false});
|
this.setState({ errorDialogVisible: false });
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.onVoteError();
|
props.onVoteError();
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
|
@ -123,7 +123,8 @@ export default class VoteSelect extends React.PureComponent<
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<RadioButton.Group
|
<RadioButton.Group
|
||||||
onValueChange={this.onVoteSelectionChange}
|
onValueChange={this.onVoteSelectionChange}
|
||||||
value={state.selectedTeam}>
|
value={state.selectedTeam}
|
||||||
|
>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={props.teams}
|
data={props.teams}
|
||||||
keyExtractor={this.voteKeyExtractor}
|
keyExtractor={this.voteKeyExtractor}
|
||||||
|
@ -137,8 +138,9 @@ export default class VoteSelect extends React.PureComponent<
|
||||||
icon="send"
|
icon="send"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={this.showVoteDialog}
|
onPress={this.showVoteDialog}
|
||||||
style={{marginLeft: 'auto'}}
|
style={styles.button}
|
||||||
disabled={state.selectedTeam === 'none'}>
|
disabled={state.selectedTeam === 'none'}
|
||||||
|
>
|
||||||
{i18n.t('screens.vote.select.sendButton')}
|
{i18n.t('screens.vote.select.sendButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, Card, Paragraph} from 'react-native-paper';
|
import { Avatar, Card, Paragraph } from 'react-native-paper';
|
||||||
import {StyleSheet} from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
@ -30,9 +30,6 @@ const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function VoteTease(props: PropsType) {
|
export default function VoteTease(props: PropsType) {
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, Card, Paragraph, useTheme} from 'react-native-paper';
|
import { Avatar, Card, Paragraph, useTheme } from 'react-native-paper';
|
||||||
import {StyleSheet} from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
@ -33,14 +33,11 @@ const styles = StyleSheet.create({
|
||||||
card: {
|
card: {
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function VoteWait(props: PropsType) {
|
export default function VoteWait(props: PropsType) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {startDate} = props;
|
const { startDate } = props;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -56,12 +53,12 @@ export default function VoteWait(props: PropsType) {
|
||||||
/>
|
/>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
{props.justVoted ? (
|
{props.justVoted ? (
|
||||||
<Paragraph style={{color: theme.colors.success}}>
|
<Paragraph style={{ color: theme.colors.success }}>
|
||||||
{i18n.t('screens.vote.wait.messageSubmitted')}
|
{i18n.t('screens.vote.wait.messageSubmitted')}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
) : null}
|
) : null}
|
||||||
{props.hasVoted ? (
|
{props.hasVoted ? (
|
||||||
<Paragraph style={{color: theme.colors.success}}>
|
<Paragraph style={{ color: theme.colors.success }}>
|
||||||
{i18n.t('screens.vote.wait.messageVoted')}
|
{i18n.t('screens.vote.wait.messageVoted')}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View, ViewStyle} from 'react-native';
|
import { View, ViewStyle } from 'react-native';
|
||||||
import {List, withTheme} from 'react-native-paper';
|
import { List, withTheme } from 'react-native-paper';
|
||||||
import Collapsible from 'react-native-collapsible';
|
import Collapsible from 'react-native-collapsible';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ type StateType = {
|
||||||
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
|
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
|
||||||
|
|
||||||
class AnimatedAccordion extends React.Component<PropsType, StateType> {
|
class AnimatedAccordion extends React.Component<PropsType, StateType> {
|
||||||
chevronRef: {current: null | (typeof AnimatedListIcon & List.Icon)};
|
chevronRef: { current: null | (typeof AnimatedListIcon & List.Icon) };
|
||||||
|
|
||||||
chevronIcon: string;
|
chevronIcon: string;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
if (nextProps.opened != null && nextProps.opened !== props.opened) {
|
if (nextProps.opened != null && nextProps.opened !== props.opened) {
|
||||||
state.expanded = nextProps.opened;
|
state.expanded = nextProps.opened;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
setupChevron() {
|
setupChevron() {
|
||||||
const {expanded} = this.state;
|
const { expanded } = this.state;
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
this.chevronIcon = 'chevron-up';
|
this.chevronIcon = 'chevron-up';
|
||||||
this.animStart = '180deg';
|
this.animStart = '180deg';
|
||||||
|
@ -89,26 +89,26 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAccordion = () => {
|
toggleAccordion = () => {
|
||||||
const {expanded} = this.state;
|
const { expanded } = this.state;
|
||||||
if (this.chevronRef.current != null) {
|
if (this.chevronRef.current != null) {
|
||||||
this.chevronRef.current.transitionTo({
|
this.chevronRef.current.transitionTo({
|
||||||
rotate: expanded ? this.animStart : this.animEnd,
|
rotate: expanded ? this.animStart : this.animEnd,
|
||||||
});
|
});
|
||||||
this.setState((prevState: StateType): {expanded: boolean} => ({
|
this.setState((prevState: StateType): { expanded: boolean } => ({
|
||||||
expanded: !prevState.expanded,
|
expanded: !prevState.expanded,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
const {colors} = props.theme;
|
const { colors } = props.theme;
|
||||||
return (
|
return (
|
||||||
<View style={props.style}>
|
<View style={props.style}>
|
||||||
<List.Item
|
<List.Item
|
||||||
title={props.title}
|
title={props.title}
|
||||||
description={props.subtitle}
|
description={props.subtitle}
|
||||||
titleStyle={state.expanded ? {color: colors.primary} : null}
|
titleStyle={state.expanded ? { color: colors.primary } : null}
|
||||||
onPress={this.toggleAccordion}
|
onPress={this.toggleAccordion}
|
||||||
right={(iconProps) => (
|
right={(iconProps) => (
|
||||||
<AnimatedListIcon
|
<AnimatedListIcon
|
||||||
|
|
|
@ -24,9 +24,9 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {FAB, IconButton, Surface, withTheme} from 'react-native-paper';
|
import { FAB, IconButton, Surface, withTheme } from 'react-native-paper';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import AutoHideHandler from '../../utils/AutoHideHandler';
|
import AutoHideHandler from '../../utils/AutoHideHandler';
|
||||||
import CustomTabBar from '../Tabbar/CustomTabBar';
|
import CustomTabBar from '../Tabbar/CustomTabBar';
|
||||||
|
|
||||||
|
@ -76,14 +76,20 @@ const styles = StyleSheet.create({
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
top: '-25%',
|
top: '-25%',
|
||||||
},
|
},
|
||||||
|
side: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
ref: {current: null | (Animatable.View & View)};
|
ref: { current: null | (Animatable.View & View) };
|
||||||
|
|
||||||
hideHandler: AutoHideHandler;
|
hideHandler: AutoHideHandler;
|
||||||
|
|
||||||
displayModeIcons: {[key: string]: string};
|
displayModeIcons: { [key: string]: string };
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
constructor(props: PropsType) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -101,7 +107,7 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean {
|
shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
nextProps.seekAttention !== props.seekAttention ||
|
nextProps.seekAttention !== props.seekAttention ||
|
||||||
nextState.currentMode !== state.currentMode
|
nextState.currentMode !== state.currentMode
|
||||||
|
@ -124,7 +130,7 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
changeDisplayMode = () => {
|
changeDisplayMode = () => {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
let newMode;
|
let newMode;
|
||||||
switch (state.currentMode) {
|
switch (state.currentMode) {
|
||||||
case DISPLAY_MODES.DAY:
|
case DISPLAY_MODES.DAY:
|
||||||
|
@ -140,12 +146,12 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
newMode = DISPLAY_MODES.WEEK;
|
newMode = DISPLAY_MODES.WEEK;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.setState({currentMode: newMode});
|
this.setState({ currentMode: newMode });
|
||||||
props.onPress('changeView', newMode);
|
props.onPress('changeView', newMode);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
const buttonColor = props.theme.colors.primary;
|
const buttonColor = props.theme.colors.primary;
|
||||||
return (
|
return (
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
|
@ -154,7 +160,8 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
style={{
|
style={{
|
||||||
...styles.container,
|
...styles.container,
|
||||||
bottom: 10 + CustomTabBar.TAB_BAR_HEIGHT,
|
bottom: 10 + CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<Surface style={styles.surface}>
|
<Surface style={styles.surface}>
|
||||||
<View style={styles.fabContainer}>
|
<View style={styles.fabContainer}>
|
||||||
<AnimatedFAB
|
<AnimatedFAB
|
||||||
|
@ -165,10 +172,10 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
style={styles.fab}
|
style={styles.fab}
|
||||||
icon="account-clock"
|
icon="account-clock"
|
||||||
onPress={(): void => props.navigation.navigate('group-select')}
|
onPress={() => props.navigation.navigate('group-select')}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={styles.side}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={this.displayModeIcons[state.currentMode]}
|
icon={this.displayModeIcons[state.currentMode]}
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
|
@ -177,21 +184,21 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="clock-in"
|
icon="clock-in"
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
style={{marginLeft: 5}}
|
style={styles.icon}
|
||||||
onPress={(): void => props.onPress('today')}
|
onPress={() => props.onPress('today')}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={styles.side}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
onPress={(): void => props.onPress('prev')}
|
onPress={() => props.onPress('prev')}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="chevron-right"
|
icon="chevron-right"
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
style={{marginLeft: 5}}
|
style={styles.icon}
|
||||||
onPress={(): void => props.onPress('next')}
|
onPress={() => props.onPress('next')}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Surface>
|
</Surface>
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {FAB} from 'react-native-paper';
|
import { FAB } from 'react-native-paper';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import AutoHideHandler from '../../utils/AutoHideHandler';
|
import AutoHideHandler from '../../utils/AutoHideHandler';
|
||||||
import CustomTabBar from '../Tabbar/CustomTabBar';
|
import CustomTabBar from '../Tabbar/CustomTabBar';
|
||||||
|
@ -43,7 +43,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class AnimatedFAB extends React.Component<PropsType> {
|
export default class AnimatedFAB extends React.Component<PropsType> {
|
||||||
ref: {current: null | (Animatable.View & View)};
|
ref: { current: null | (Animatable.View & View) };
|
||||||
|
|
||||||
hideHandler: AutoHideHandler;
|
hideHandler: AutoHideHandler;
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export default class AnimatedFAB extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
ref={this.ref}
|
ref={this.ref}
|
||||||
|
@ -83,7 +83,8 @@ export default class AnimatedFAB extends React.Component<PropsType> {
|
||||||
style={{
|
style={{
|
||||||
...styles.fab,
|
...styles.fab,
|
||||||
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<FAB icon={props.icon} onPress={props.onPress} />
|
<FAB icon={props.icon} onPress={props.onPress} />
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,19 +18,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useCollapsibleStack} from 'react-navigation-collapsible';
|
import { useCollapsibleStack } from 'react-navigation-collapsible';
|
||||||
import CustomTabBar from '../Tabbar/CustomTabBar';
|
import CustomTabBar from '../Tabbar/CustomTabBar';
|
||||||
import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
|
import {
|
||||||
|
NativeScrollEvent,
|
||||||
|
NativeSyntheticEvent,
|
||||||
|
StyleSheet,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
export interface CollapsibleComponentPropsType {
|
export type CollapsibleComponentPropsType = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
hasTab?: boolean;
|
hasTab?: boolean;
|
||||||
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PropsType extends CollapsibleComponentPropsType {
|
type PropsType = CollapsibleComponentPropsType & {
|
||||||
component: React.ComponentType<any>;
|
component: React.ComponentType<any>;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
main: {
|
||||||
|
minHeight: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function CollapsibleComponent(props: PropsType) {
|
function CollapsibleComponent(props: PropsType) {
|
||||||
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||||
|
@ -44,17 +54,18 @@ function CollapsibleComponent(props: PropsType) {
|
||||||
scrollIndicatorInsetTop,
|
scrollIndicatorInsetTop,
|
||||||
onScrollWithListener,
|
onScrollWithListener,
|
||||||
} = useCollapsibleStack();
|
} = useCollapsibleStack();
|
||||||
|
const paddingBottom = props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0;
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
{...props}
|
{...props}
|
||||||
onScroll={onScrollWithListener(onScroll)}
|
onScroll={onScrollWithListener(onScroll)}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
paddingTop: containerPaddingTop,
|
paddingTop: containerPaddingTop,
|
||||||
paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0,
|
paddingBottom: paddingBottom,
|
||||||
minHeight: '100%',
|
...styles.main,
|
||||||
}}
|
}}
|
||||||
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
|
scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Comp>
|
</Comp>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Animated, FlatListProps} from 'react-native';
|
import { Animated, FlatListProps } from 'react-native';
|
||||||
import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
|
import type { CollapsibleComponentPropsType } from './CollapsibleComponent';
|
||||||
import CollapsibleComponent from './CollapsibleComponent';
|
import CollapsibleComponent from './CollapsibleComponent';
|
||||||
|
|
||||||
type Props<T> = FlatListProps<T> & CollapsibleComponentPropsType;
|
type Props<T> = FlatListProps<T> & CollapsibleComponentPropsType;
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Animated, ScrollViewProps} from 'react-native';
|
import { Animated, ScrollViewProps } from 'react-native';
|
||||||
import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
|
import type { CollapsibleComponentPropsType } from './CollapsibleComponent';
|
||||||
import CollapsibleComponent from './CollapsibleComponent';
|
import CollapsibleComponent from './CollapsibleComponent';
|
||||||
|
|
||||||
type Props = ScrollViewProps & CollapsibleComponentPropsType;
|
type Props = ScrollViewProps & CollapsibleComponentPropsType;
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Animated, SectionListProps} from 'react-native';
|
import { Animated, SectionListProps } from 'react-native';
|
||||||
import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
|
import type { CollapsibleComponentPropsType } from './CollapsibleComponent';
|
||||||
import CollapsibleComponent from './CollapsibleComponent';
|
import CollapsibleComponent from './CollapsibleComponent';
|
||||||
|
|
||||||
type Props<T> = SectionListProps<T> & CollapsibleComponentPropsType;
|
type Props<T> = SectionListProps<T> & CollapsibleComponentPropsType;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Button, Dialog, Paragraph, Portal} from 'react-native-paper';
|
import { Button, Dialog, Paragraph, Portal } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {ERROR_TYPE} from '../../utils/WebData';
|
import { ERROR_TYPE } from '../../utils/WebData';
|
||||||
import AlertDialog from './AlertDialog';
|
import AlertDialog from './AlertDialog';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
Portal,
|
Portal,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -41,6 +42,12 @@ type StateType = {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default class LoadingConfirmDialog extends React.PureComponent<
|
export default class LoadingConfirmDialog extends React.PureComponent<
|
||||||
PropsType,
|
PropsType,
|
||||||
StateType
|
StateType
|
||||||
|
@ -70,8 +77,8 @@ export default class LoadingConfirmDialog extends React.PureComponent<
|
||||||
* Set the dialog into loading state and closes it when operation finishes
|
* Set the dialog into loading state and closes it when operation finishes
|
||||||
*/
|
*/
|
||||||
onClickAccept = () => {
|
onClickAccept = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
this.setState({loading: true});
|
this.setState({ loading: true });
|
||||||
if (props.onAccept != null) {
|
if (props.onAccept != null) {
|
||||||
props.onAccept().then(this.hideLoading);
|
props.onAccept().then(this.hideLoading);
|
||||||
}
|
}
|
||||||
|
@ -83,21 +90,21 @@ export default class LoadingConfirmDialog extends React.PureComponent<
|
||||||
*/
|
*/
|
||||||
hideLoading = (): NodeJS.Timeout =>
|
hideLoading = (): NodeJS.Timeout =>
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState({loading: false});
|
this.setState({ loading: false });
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the dialog if it is not loading
|
* Hide the dialog if it is not loading
|
||||||
*/
|
*/
|
||||||
onDismiss = () => {
|
onDismiss = () => {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
if (!state.loading && props.onDismiss != null) {
|
if (!state.loading && props.onDismiss != null) {
|
||||||
props.onDismiss();
|
props.onDismiss();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<Dialog visible={props.visible} onDismiss={this.onDismiss}>
|
<Dialog visible={props.visible} onDismiss={this.onDismiss}>
|
||||||
|
@ -113,7 +120,7 @@ export default class LoadingConfirmDialog extends React.PureComponent<
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
{state.loading ? null : (
|
{state.loading ? null : (
|
||||||
<Dialog.Actions>
|
<Dialog.Actions>
|
||||||
<Button onPress={this.onDismiss} style={{marginRight: 10}}>
|
<Button onPress={this.onDismiss} style={styles.button}>
|
||||||
{i18n.t('dialog.cancel')}
|
{i18n.t('dialog.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onPress={this.onClickAccept}>
|
<Button onPress={this.onClickAccept}>
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Button, Dialog, Paragraph, Portal} from 'react-native-paper';
|
import { Button, Dialog, Paragraph, Portal } from 'react-native-paper';
|
||||||
import {FlatList} from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
|
|
||||||
export type OptionsDialogButtonType = {
|
export type OptionsDialogButtonType = {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -36,7 +36,7 @@ type PropsType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function OptionsDialog(props: PropsType) {
|
function OptionsDialog(props: PropsType) {
|
||||||
const getButtonRender = ({item}: {item: OptionsDialogButtonType}) => {
|
const getButtonRender = ({ item }: { item: OptionsDialogButtonType }) => {
|
||||||
return (
|
return (
|
||||||
<Button onPress={item.onPress} icon={item.icon}>
|
<Button onPress={item.onPress} icon={item.icon}>
|
||||||
{item.title}
|
{item.title}
|
||||||
|
|
|
@ -18,10 +18,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {List} from 'react-native-paper';
|
import { List } from 'react-native-paper';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
item: {
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function ActionsDashBoardItem() {
|
function ActionsDashBoardItem() {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
@ -45,12 +54,7 @@ function ActionsDashBoardItem() {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
onPress={(): void => navigation.navigate('feedback')}
|
onPress={(): void => navigation.navigate('feedback')}
|
||||||
style={{
|
style={styles.item}
|
||||||
paddingTop: 0,
|
|
||||||
paddingBottom: 0,
|
|
||||||
marginLeft: 10,
|
|
||||||
marginRight: 10,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,8 +25,9 @@ import {
|
||||||
TouchableRipple,
|
TouchableRipple,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
eventNumber: number;
|
eventNumber: number;
|
||||||
|
@ -45,6 +46,9 @@ const styles = StyleSheet.create({
|
||||||
avatar: {
|
avatar: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
text: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,7 +65,7 @@ function EventDashBoardItem(props: PropsType) {
|
||||||
if (isAvailable) {
|
if (isAvailable) {
|
||||||
subtitle = (
|
subtitle = (
|
||||||
<Text>
|
<Text>
|
||||||
<Text style={{fontWeight: 'bold'}}>{props.eventNumber}</Text>
|
<Text style={styles.text}>{props.eventNumber}</Text>
|
||||||
<Text>
|
<Text>
|
||||||
{props.eventNumber > 1
|
{props.eventNumber > 1
|
||||||
? i18n.t('screens.home.dashboard.todayEventsSubtitlePlural')
|
? i18n.t('screens.home.dashboard.todayEventsSubtitlePlural')
|
||||||
|
@ -74,13 +78,13 @@ function EventDashBoardItem(props: PropsType) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
<TouchableRipple style={GENERAL_STYLES.flex} onPress={props.clickAction}>
|
||||||
<View>
|
<View>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.home.dashboard.todayEventsTitle')}
|
title={i18n.t('screens.home.dashboard.todayEventsTitle')}
|
||||||
titleStyle={{color: textColor}}
|
titleStyle={{ color: textColor }}
|
||||||
subtitle={subtitle}
|
subtitle={subtitle}
|
||||||
subtitleStyle={{color: textColor}}
|
subtitleStyle={{ color: textColor }}
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
icon="calendar-range"
|
icon="calendar-range"
|
||||||
|
|
|
@ -18,17 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Button, Card, Text, TouchableRipple} from 'react-native-paper';
|
import { Button, Card, Text, TouchableRipple } from 'react-native-paper';
|
||||||
import {Image, View} from 'react-native';
|
import { Image, StyleSheet, View } from 'react-native';
|
||||||
import Autolink from 'react-native-autolink';
|
import Autolink from 'react-native-autolink';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import type {FeedItemType} from '../../screens/Home/HomeScreen';
|
import type { FeedItemType } from '../../screens/Home/HomeScreen';
|
||||||
import NewsSourcesConstants, {
|
import NewsSourcesConstants, {
|
||||||
AvailablePages,
|
AvailablePages,
|
||||||
} from '../../constants/NewsSourcesConstants';
|
} from '../../constants/NewsSourcesConstants';
|
||||||
import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
|
import type { NewsSourceType } from '../../constants/NewsSourcesConstants';
|
||||||
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
item: FeedItemType;
|
item: FeedItemType;
|
||||||
|
@ -46,6 +47,20 @@ function getFormattedDate(dateString: number): string {
|
||||||
return date.toLocaleString();
|
return date.toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to display a feed item
|
* Component used to display a feed item
|
||||||
*/
|
*/
|
||||||
|
@ -58,7 +73,7 @@ function FeedItem(props: PropsType) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const {item, height} = props;
|
const { item, height } = props;
|
||||||
const image = item.image !== '' && item.image != null ? item.image : null;
|
const image = item.image !== '' && item.image != null ? item.image : null;
|
||||||
const pageSource: NewsSourceType =
|
const pageSource: NewsSourceType =
|
||||||
NewsSourcesConstants[item.page_id as AvailablePages];
|
NewsSourcesConstants[item.page_id as AvailablePages];
|
||||||
|
@ -76,31 +91,23 @@ function FeedItem(props: PropsType) {
|
||||||
style={{
|
style={{
|
||||||
margin: cardMargin,
|
margin: cardMargin,
|
||||||
height: cardHeight,
|
height: cardHeight,
|
||||||
}}>
|
}}
|
||||||
<TouchableRipple style={{flex: 1}} onPress={onPress}>
|
>
|
||||||
|
<TouchableRipple style={GENERAL_STYLES.flex} onPress={onPress}>
|
||||||
<View>
|
<View>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={pageSource.name}
|
title={pageSource.name}
|
||||||
subtitle={getFormattedDate(item.time)}
|
subtitle={getFormattedDate(item.time)}
|
||||||
left={() => (
|
left={() => <Image source={pageSource.icon} style={styles.image} />}
|
||||||
<Image
|
style={{ height: titleHeight }}
|
||||||
source={pageSource.icon}
|
|
||||||
style={{
|
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
style={{height: titleHeight}}
|
|
||||||
/>
|
/>
|
||||||
{image != null ? (
|
{image != null ? (
|
||||||
<ImageGalleryButton
|
<ImageGalleryButton
|
||||||
images={[{url: image}]}
|
images={[{ url: image }]}
|
||||||
style={{
|
style={{
|
||||||
|
...styles.button,
|
||||||
width: imageSize,
|
width: imageSize,
|
||||||
height: imageSize,
|
height: imageSize,
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -110,12 +117,12 @@ function FeedItem(props: PropsType) {
|
||||||
text={item.message}
|
text={item.message}
|
||||||
hashtag="facebook"
|
hashtag="facebook"
|
||||||
component={Text}
|
component={Text}
|
||||||
style={{height: textHeight}}
|
style={{ height: textHeight }}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
<Card.Actions style={{height: actionsHeight}}>
|
<Card.Actions style={{ height: actionsHeight }}>
|
||||||
<Button onPress={onPress} icon="plus" style={{marginLeft: 'auto'}}>
|
<Button onPress={onPress} icon="plus" style={styles.action}>
|
||||||
{i18n.t('screens.home.dashboard.seeMore')}
|
{i18n.t('screens.home.dashboard.seeMore')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Avatar, Button, Card, TouchableRipple} from 'react-native-paper';
|
import { Avatar, Button, Card, TouchableRipple } from 'react-native-paper';
|
||||||
import {getTimeOnlyString, isDescriptionEmpty} from '../../utils/Planning';
|
import { getTimeOnlyString, isDescriptionEmpty } from '../../utils/Planning';
|
||||||
import CustomHTML from '../Overrides/CustomHTML';
|
import CustomHTML from '../Overrides/CustomHTML';
|
||||||
import type {PlanningEventType} from '../../utils/Planning';
|
import type { PlanningEventType } from '../../utils/Planning';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
event?: PlanningEventType | null;
|
event?: PlanningEventType | null;
|
||||||
|
@ -52,19 +53,26 @@ const styles = StyleSheet.create({
|
||||||
* Component used to display an event preview if an event is available
|
* Component used to display an event preview if an event is available
|
||||||
*/
|
*/
|
||||||
function PreviewEventDashboardItem(props: PropsType) {
|
function PreviewEventDashboardItem(props: PropsType) {
|
||||||
const {event} = props;
|
const { event } = props;
|
||||||
const isEmpty = event == null ? true : isDescriptionEmpty(event.description);
|
const isEmpty = event == null ? true : isDescriptionEmpty(event.description);
|
||||||
|
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
const logo = event.logo;
|
const logo = event.logo;
|
||||||
const getImage = logo
|
const getImage = logo
|
||||||
? () => (
|
? () => (
|
||||||
<Avatar.Image source={{uri: logo}} size={50} style={styles.avatar} />
|
<Avatar.Image
|
||||||
|
source={{ uri: logo }}
|
||||||
|
size={50}
|
||||||
|
style={styles.avatar}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
: () => null;
|
: () => null;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card} elevation={3}>
|
<Card style={styles.card} elevation={3}>
|
||||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
<TouchableRipple
|
||||||
|
style={GENERAL_STYLES.flex}
|
||||||
|
onPress={props.clickAction}
|
||||||
|
>
|
||||||
<View>
|
<View>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={event.title}
|
title={event.title}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Badge, TouchableRipple, useTheme} from 'react-native-paper';
|
import { Badge, TouchableRipple, useTheme } from 'react-native-paper';
|
||||||
import {Dimensions, Image, View} from 'react-native';
|
import { Dimensions, Image, StyleSheet, View } from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
@ -28,13 +28,32 @@ type PropsType = {
|
||||||
badgeCount?: number;
|
badgeCount?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
width: '80%',
|
||||||
|
height: '80%',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
badgeContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
badge: {
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to render a small dashboard item
|
* Component used to render a small dashboard item
|
||||||
*/
|
*/
|
||||||
function SmallDashboardItem(props: PropsType) {
|
function SmallDashboardItem(props: PropsType) {
|
||||||
const itemSize = Dimensions.get('window').width / 8;
|
const itemSize = Dimensions.get('window').width / 8;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {image} = props;
|
const { image } = props;
|
||||||
return (
|
return (
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
|
@ -42,23 +61,18 @@ function SmallDashboardItem(props: PropsType) {
|
||||||
style={{
|
style={{
|
||||||
marginLeft: itemSize / 6,
|
marginLeft: itemSize / 6,
|
||||||
marginRight: itemSize / 6,
|
marginRight: itemSize / 6,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: itemSize,
|
width: itemSize,
|
||||||
height: itemSize,
|
height: itemSize,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{image ? (
|
{image ? (
|
||||||
<Image
|
<Image
|
||||||
source={typeof image === 'string' ? {uri: image} : image}
|
source={typeof image === 'string' ? { uri: image } : image}
|
||||||
style={{
|
style={styles.image}
|
||||||
width: '80%',
|
|
||||||
height: '80%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{props.badgeCount != null && props.badgeCount > 0 ? (
|
{props.badgeCount != null && props.badgeCount > 0 ? (
|
||||||
|
@ -66,18 +80,16 @@ function SmallDashboardItem(props: PropsType) {
|
||||||
animation="zoomIn"
|
animation="zoomIn"
|
||||||
duration={300}
|
duration={300}
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
style={{
|
style={styles.badgeContainer}
|
||||||
position: 'absolute',
|
>
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
}}>
|
|
||||||
<Badge
|
<Badge
|
||||||
visible={true}
|
visible={true}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.colors.primary,
|
backgroundColor: theme.colors.primary,
|
||||||
borderColor: theme.colors.background,
|
borderColor: theme.colors.background,
|
||||||
borderWidth: 2,
|
...styles.badge,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{props.badgeCount}
|
{props.badgeCount}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
icon: string;
|
icon: string;
|
||||||
|
@ -37,7 +38,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
function IntroIcon(props: PropsType) {
|
function IntroIcon(props: PropsType) {
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<Animatable.View useNativeDriver style={styles.center} animation="fadeIn">
|
<Animatable.View useNativeDriver style={styles.center} animation="fadeIn">
|
||||||
<MaterialCommunityIcons name={props.icon} color="#fff" size={200} />
|
<MaterialCommunityIcons name={props.icon} color="#fff" size={200} />
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
|
|
|
@ -18,25 +18,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
center: {
|
center: {
|
||||||
marginTop: 'auto',
|
...GENERAL_STYLES.center,
|
||||||
marginBottom: 'auto',
|
width: '80%',
|
||||||
marginRight: 'auto',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function MascotIntroEnd() {
|
function MascotIntroEnd() {
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{
|
style={{
|
||||||
...styles.center,
|
...styles.center,
|
||||||
width: '80%',
|
|
||||||
}}
|
}}
|
||||||
emotion={MASCOT_STYLE.COOL}
|
emotion={MASCOT_STYLE.COOL}
|
||||||
animated
|
animated
|
||||||
|
|
|
@ -18,28 +18,40 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
center: {
|
mascot: {
|
||||||
marginTop: 'auto',
|
...GENERAL_STYLES.center,
|
||||||
marginBottom: 'auto',
|
width: '80%',
|
||||||
marginRight: 'auto',
|
},
|
||||||
marginLeft: 'auto',
|
text: {
|
||||||
|
color: '#fff',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: 25,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 30,
|
||||||
|
right: '20%',
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
...GENERAL_STYLES.center,
|
||||||
|
transform: [{ rotateZ: '70deg' }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function MascotIntroWelcome() {
|
function MascotIntroWelcome() {
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{
|
style={styles.mascot}
|
||||||
...styles.center,
|
|
||||||
width: '80%',
|
|
||||||
}}
|
|
||||||
emotion={MASCOT_STYLE.NORMAL}
|
emotion={MASCOT_STYLE.NORMAL}
|
||||||
animated
|
animated
|
||||||
entryAnimation={{
|
entryAnimation={{
|
||||||
|
@ -51,11 +63,8 @@ function MascotIntroWelcome() {
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation="fadeInUp"
|
animation="fadeInUp"
|
||||||
duration={500}
|
duration={500}
|
||||||
style={{
|
style={styles.text}
|
||||||
color: '#fff',
|
>
|
||||||
textAlign: 'center',
|
|
||||||
fontSize: 25,
|
|
||||||
}}>
|
|
||||||
PABLO
|
PABLO
|
||||||
</Animatable.Text>
|
</Animatable.Text>
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
|
@ -63,18 +72,10 @@ function MascotIntroWelcome() {
|
||||||
animation="fadeInUp"
|
animation="fadeInUp"
|
||||||
duration={500}
|
duration={500}
|
||||||
delay={200}
|
delay={200}
|
||||||
style={{
|
style={styles.container}
|
||||||
position: 'absolute',
|
>
|
||||||
bottom: 30,
|
|
||||||
right: '20%',
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
}}>
|
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
style={{
|
style={styles.icon}
|
||||||
...styles.center,
|
|
||||||
transform: [{rotateZ: '70deg'}],
|
|
||||||
}}
|
|
||||||
name="undo"
|
name="undo"
|
||||||
color="#fff"
|
color="#fff"
|
||||||
size={40}
|
size={40}
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Animated, Dimensions, ViewStyle} from 'react-native';
|
import { Animated, Dimensions, ViewStyle } from 'react-native';
|
||||||
import ImageListItem from './ImageListItem';
|
import ImageListItem from './ImageListItem';
|
||||||
import CardListItem from './CardListItem';
|
import CardListItem from './CardListItem';
|
||||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
import type { ServiceItemType } from '../../../managers/ServicesManager';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
dataset: Array<ServiceItemType>;
|
dataset: Array<ServiceItemType>;
|
||||||
|
@ -45,8 +45,8 @@ export default class CardList extends React.Component<PropsType> {
|
||||||
this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items, and a part of the 4th => user knows he can scroll
|
this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items, and a part of the 4th => user knows he can scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: ServiceItemType}) => {
|
getRenderItem = ({ item }: { item: ServiceItemType }) => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
if (props.isHorizontal) {
|
if (props.isHorizontal) {
|
||||||
return (
|
return (
|
||||||
<ImageListItem
|
<ImageListItem
|
||||||
|
@ -62,7 +62,7 @@ export default class CardList extends React.Component<PropsType> {
|
||||||
keyExtractor = (item: ServiceItemType): string => item.key;
|
keyExtractor = (item: ServiceItemType): string => item.key;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
let containerStyle = {};
|
let containerStyle = {};
|
||||||
if (props.isHorizontal) {
|
if (props.isHorizontal) {
|
||||||
containerStyle = {
|
containerStyle = {
|
||||||
|
|
|
@ -18,29 +18,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper';
|
import { Caption, Card, Paragraph, TouchableRipple } from 'react-native-paper';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
import type { ServiceItemType } from '../../../managers/ServicesManager';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
item: ServiceItemType;
|
item: ServiceItemType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
width: '40%',
|
||||||
|
margin: 5,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
cover: {
|
||||||
|
height: 80,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function CardListItem(props: PropsType) {
|
function CardListItem(props: PropsType) {
|
||||||
const {item} = props;
|
const { item } = props;
|
||||||
const source =
|
const source =
|
||||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
typeof item.image === 'number' ? item.image : { uri: item.image };
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card style={styles.card}>
|
||||||
style={{
|
<TouchableRipple style={GENERAL_STYLES.flex} onPress={item.onPress}>
|
||||||
width: '40%',
|
|
||||||
margin: 5,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}>
|
|
||||||
<TouchableRipple style={{flex: 1}} onPress={item.onPress}>
|
|
||||||
<View>
|
<View>
|
||||||
<Card.Cover style={{height: 80}} source={source} />
|
<Card.Cover style={styles.cover} source={source} />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Paragraph>{item.title}</Paragraph>
|
<Paragraph>{item.title}</Paragraph>
|
||||||
<Caption>{item.subtitle}</Caption>
|
<Caption>{item.subtitle}</Caption>
|
||||||
|
|
|
@ -18,46 +18,50 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Text, TouchableRipple} from 'react-native-paper';
|
import { Text, TouchableRipple } from 'react-native-paper';
|
||||||
import {Image, View} from 'react-native';
|
import { Image, StyleSheet, View } from 'react-native';
|
||||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
import type { ServiceItemType } from '../../../managers/ServicesManager';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
item: ServiceItemType;
|
item: ServiceItemType;
|
||||||
width: number;
|
width: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
ripple: {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
|
marginTop: 5,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function ImageListItem(props: PropsType) {
|
function ImageListItem(props: PropsType) {
|
||||||
const {item} = props;
|
const { item } = props;
|
||||||
const source =
|
const source =
|
||||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
typeof item.image === 'number' ? item.image : { uri: item.image };
|
||||||
return (
|
return (
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
style={{
|
style={{
|
||||||
width: props.width,
|
width: props.width,
|
||||||
height: props.width + 40,
|
height: props.width + 40,
|
||||||
margin: 5,
|
...styles.ripple,
|
||||||
}}
|
}}
|
||||||
onPress={item.onPress}>
|
onPress={item.onPress}
|
||||||
|
>
|
||||||
<View>
|
<View>
|
||||||
<Image
|
<Image
|
||||||
style={{
|
style={{
|
||||||
width: props.width - 20,
|
width: props.width - 20,
|
||||||
height: props.width - 20,
|
height: props.width - 20,
|
||||||
marginLeft: 'auto',
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
}}
|
||||||
source={source}
|
source={source}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text style={styles.text}>{item.title}</Text>
|
||||||
style={{
|
|
||||||
marginTop: 5,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}>
|
|
||||||
{item.title}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</TouchableRipple>
|
</TouchableRipple>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Card, Chip, List, Text} from 'react-native-paper';
|
import { Card, Chip, List, Text } from 'react-native-paper';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||||
import {isItemInCategoryFilter} from '../../../utils/Search';
|
import { isItemInCategoryFilter } from '../../../utils/Search';
|
||||||
import type {ClubCategoryType} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
import type { ClubCategoryType } from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
categories: Array<ClubCategoryType>;
|
categories: Array<ClubCategoryType>;
|
||||||
|
@ -39,8 +40,7 @@ const styles = StyleSheet.create({
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginLeft: 'auto',
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
marginRight: 'auto',
|
|
||||||
},
|
},
|
||||||
chipContainer: {
|
chipContainer: {
|
||||||
justifyContent: 'space-around',
|
justifyContent: 'space-around',
|
||||||
|
@ -49,6 +49,11 @@ const styles = StyleSheet.create({
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
},
|
},
|
||||||
|
chip: {
|
||||||
|
marginRight: 5,
|
||||||
|
marginLeft: 5,
|
||||||
|
marginBottom: 5,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function ClubListHeader(props: PropsType) {
|
function ClubListHeader(props: PropsType) {
|
||||||
|
@ -62,8 +67,9 @@ function ClubListHeader(props: PropsType) {
|
||||||
])}
|
])}
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={{marginRight: 5, marginLeft: 5, marginBottom: 5}}
|
style={styles.chip}
|
||||||
key={key}>
|
key={key}
|
||||||
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</Chip>
|
</Chip>
|
||||||
);
|
);
|
||||||
|
@ -88,7 +94,8 @@ function ClubListHeader(props: PropsType) {
|
||||||
icon="star"
|
icon="star"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
opened>
|
opened
|
||||||
|
>
|
||||||
<Text style={styles.text}>
|
<Text style={styles.text}>
|
||||||
{i18n.t('screens.clubs.categoriesFilterMessage')}
|
{i18n.t('screens.clubs.categoriesFilterMessage')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, Chip, List, withTheme} from 'react-native-paper';
|
import { Avatar, Chip, List, withTheme } from 'react-native-paper';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import type {
|
import type {
|
||||||
ClubCategoryType,
|
ClubCategoryType,
|
||||||
ClubType,
|
ClubType,
|
||||||
} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
|
@ -33,6 +34,28 @@ type PropsType = {
|
||||||
theme: ReactNativePaper.Theme;
|
theme: ReactNativePaper.Theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
chip: {
|
||||||
|
marginRight: 5,
|
||||||
|
marginBottom: 5,
|
||||||
|
},
|
||||||
|
chipContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
...GENERAL_STYLES.centerVertical,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class ClubListItem extends React.Component<PropsType> {
|
class ClubListItem extends React.Component<PropsType> {
|
||||||
hasManagers: boolean;
|
hasManagers: boolean;
|
||||||
|
|
||||||
|
@ -46,30 +69,28 @@ class ClubListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategoriesRender(categories: Array<number | null>) {
|
getCategoriesRender(categories: Array<number | null>) {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const final: Array<React.ReactNode> = [];
|
const final: Array<React.ReactNode> = [];
|
||||||
categories.forEach((cat: number | null) => {
|
categories.forEach((cat: number | null) => {
|
||||||
if (cat != null) {
|
if (cat != null) {
|
||||||
const category = props.categoryTranslator(cat);
|
const category = props.categoryTranslator(cat);
|
||||||
if (category) {
|
if (category) {
|
||||||
final.push(
|
final.push(
|
||||||
<Chip
|
<Chip style={styles.chip} key={`${props.item.id}:${category.id}`}>
|
||||||
style={{marginRight: 5, marginBottom: 5}}
|
|
||||||
key={`${props.item.id}:${category.id}`}>
|
|
||||||
{category.name}
|
{category.name}
|
||||||
</Chip>,
|
</Chip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return <View style={{flexDirection: 'row'}}>{final}</View>;
|
return <View style={styles.chipContainer}>{final}</View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const categoriesRender = () =>
|
const categoriesRender = () =>
|
||||||
this.getCategoriesRender(props.item.category);
|
this.getCategoriesRender(props.item.category);
|
||||||
const {colors} = props.theme;
|
const { colors } = props.theme;
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={props.item.name}
|
title={props.item.name}
|
||||||
|
@ -77,22 +98,14 @@ class ClubListItem extends React.Component<PropsType> {
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
left={() => (
|
left={() => (
|
||||||
<Avatar.Image
|
<Avatar.Image
|
||||||
style={{
|
style={styles.avatar}
|
||||||
backgroundColor: 'transparent',
|
|
||||||
marginLeft: 10,
|
|
||||||
marginRight: 10,
|
|
||||||
}}
|
|
||||||
size={64}
|
size={64}
|
||||||
source={{uri: props.item.logo}}
|
source={{ uri: props.item.logo }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
right={() => (
|
right={() => (
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
style={{
|
style={styles.icon}
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
}}
|
|
||||||
size={48}
|
size={48}
|
||||||
icon={
|
icon={
|
||||||
this.hasManagers ? 'check-circle-outline' : 'alert-circle-outline'
|
this.hasManagers ? 'check-circle-outline' : 'alert-circle-outline'
|
||||||
|
@ -102,7 +115,7 @@ class ClubListItem extends React.Component<PropsType> {
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
height: props.height,
|
height: props.height,
|
||||||
justifyContent: 'center',
|
...styles.item,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useTheme} from 'react-native-paper';
|
import { useTheme } from 'react-native-paper';
|
||||||
import {FlatList, Image, View} from 'react-native';
|
import { FlatList, Image, StyleSheet, View } from 'react-native';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import DashboardEditItem from './DashboardEditItem';
|
import DashboardEditItem from './DashboardEditItem';
|
||||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||||
|
@ -34,12 +34,19 @@ type PropsType = {
|
||||||
onPress: (service: ServiceItemType) => void;
|
onPress: (service: ServiceItemType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const LIST_ITEM_HEIGHT = 64;
|
const LIST_ITEM_HEIGHT = 64;
|
||||||
|
|
||||||
function DashboardEditAccordion(props: PropsType) {
|
function DashboardEditAccordion(props: PropsType) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const getRenderItem = ({item}: {item: ServiceItemType}) => {
|
const getRenderItem = ({ item }: { item: ServiceItemType }) => {
|
||||||
return (
|
return (
|
||||||
<DashboardEditItem
|
<DashboardEditItem
|
||||||
height={LIST_ITEM_HEIGHT}
|
height={LIST_ITEM_HEIGHT}
|
||||||
|
@ -54,27 +61,21 @@ function DashboardEditAccordion(props: PropsType) {
|
||||||
|
|
||||||
const getItemLayout = (
|
const getItemLayout = (
|
||||||
data: Array<ServiceItemType> | null | undefined,
|
data: Array<ServiceItemType> | null | undefined,
|
||||||
index: number,
|
index: number
|
||||||
): {length: number; offset: number; index: number} => ({
|
): { length: number; offset: number; index: number } => ({
|
||||||
length: LIST_ITEM_HEIGHT,
|
length: LIST_ITEM_HEIGHT,
|
||||||
offset: LIST_ITEM_HEIGHT * index,
|
offset: LIST_ITEM_HEIGHT * index,
|
||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {item} = props;
|
const { item } = props;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<AnimatedAccordion
|
<AnimatedAccordion
|
||||||
title={item.title}
|
title={item.title}
|
||||||
left={() =>
|
left={() =>
|
||||||
typeof item.image === 'number' ? (
|
typeof item.image === 'number' ? (
|
||||||
<Image
|
<Image source={item.image} style={styles.image} />
|
||||||
source={item.image}
|
|
||||||
style={{
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={item.image}
|
name={item.image}
|
||||||
|
@ -82,7 +83,8 @@ function DashboardEditAccordion(props: PropsType) {
|
||||||
size={40}
|
size={40}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}>
|
}
|
||||||
|
>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={item.content}
|
data={item.content}
|
||||||
extraData={props.activeDashboard.toString()}
|
extraData={props.activeDashboard.toString()}
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image} from 'react-native';
|
import { Image, StyleSheet } from 'react-native';
|
||||||
import {List, useTheme} from 'react-native-paper';
|
import { List, useTheme } from 'react-native-paper';
|
||||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
import type { ServiceItemType } from '../../../managers/ServicesManager';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
item: ServiceItemType;
|
item: ServiceItemType;
|
||||||
|
@ -29,9 +29,23 @@ type PropsType = {
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingLeft: 30,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function DashboardEditItem(props: PropsType) {
|
function DashboardEditItem(props: PropsType) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {item, onPress, height, isActive} = props;
|
const { item, onPress, height, isActive } = props;
|
||||||
|
const backgroundColor = isActive
|
||||||
|
? theme.colors.proxiwashFinishedColor
|
||||||
|
: 'transparent';
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={item.title}
|
title={item.title}
|
||||||
|
@ -40,12 +54,9 @@ function DashboardEditItem(props: PropsType) {
|
||||||
left={() => (
|
left={() => (
|
||||||
<Image
|
<Image
|
||||||
source={
|
source={
|
||||||
typeof item.image === 'string' ? {uri: item.image} : item.image
|
typeof item.image === 'string' ? { uri: item.image } : item.image
|
||||||
}
|
}
|
||||||
style={{
|
style={styles.image}
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
right={(iconProps) =>
|
right={(iconProps) =>
|
||||||
|
@ -58,12 +69,9 @@ function DashboardEditItem(props: PropsType) {
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
height,
|
...styles.image,
|
||||||
justifyContent: 'center',
|
height: height,
|
||||||
paddingLeft: 30,
|
backgroundColor: backgroundColor,
|
||||||
backgroundColor: isActive
|
|
||||||
? theme.colors.proxiwashFinishedColor
|
|
||||||
: 'transparent',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {TouchableRipple, useTheme} from 'react-native-paper';
|
import { TouchableRipple, useTheme } from 'react-native-paper';
|
||||||
import {Dimensions, Image, View} from 'react-native';
|
import { Dimensions, Image, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
image?: string | number;
|
image?: string | number;
|
||||||
|
@ -27,39 +27,50 @@ type PropsType = {
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
ripple: {
|
||||||
|
marginLeft: 5,
|
||||||
|
marginRight: 5,
|
||||||
|
borderRadius: 5,
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to render a small dashboard item
|
* Component used to render a small dashboard item
|
||||||
*/
|
*/
|
||||||
function DashboardEditPreviewItem(props: PropsType) {
|
function DashboardEditPreviewItem(props: PropsType) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const itemSize = Dimensions.get('window').width / 8;
|
const itemSize = Dimensions.get('window').width / 8;
|
||||||
|
const backgroundColor = props.isActive
|
||||||
|
? theme.colors.textDisabled
|
||||||
|
: 'transparent';
|
||||||
return (
|
return (
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
borderless
|
borderless
|
||||||
style={{
|
style={{
|
||||||
marginLeft: 5,
|
...styles.ripple,
|
||||||
marginRight: 5,
|
backgroundColor: backgroundColor,
|
||||||
backgroundColor: props.isActive
|
}}
|
||||||
? theme.colors.textDisabled
|
>
|
||||||
: 'transparent',
|
|
||||||
borderRadius: 5,
|
|
||||||
}}>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: itemSize,
|
width: itemSize,
|
||||||
height: itemSize,
|
height: itemSize,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{props.image ? (
|
{props.image ? (
|
||||||
<Image
|
<Image
|
||||||
source={
|
source={
|
||||||
typeof props.image === 'string' ? {uri: props.image} : props.image
|
typeof props.image === 'string'
|
||||||
|
? { uri: props.image }
|
||||||
|
: props.image
|
||||||
}
|
}
|
||||||
style={{
|
style={styles.image}
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,15 +18,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, List, useTheme} from 'react-native-paper';
|
import { Avatar, List, useTheme } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
|
import type { DeviceType } from '../../../screens/Amicale/Equipment/EquipmentListScreen';
|
||||||
import {
|
import {
|
||||||
getFirstEquipmentAvailability,
|
getFirstEquipmentAvailability,
|
||||||
getRelativeDateString,
|
getRelativeDateString,
|
||||||
isEquipmentAvailable,
|
isEquipmentAvailable,
|
||||||
} from '../../../utils/EquipmentBooking';
|
} from '../../../utils/EquipmentBooking';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
navigation: StackNavigationProp<any>;
|
navigation: StackNavigationProp<any>;
|
||||||
|
@ -35,9 +37,18 @@ type PropsType = {
|
||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
icon: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function EquipmentListItem(props: PropsType) {
|
function EquipmentListItem(props: PropsType) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {item, userDeviceRentDates, navigation, height} = props;
|
const { item, userDeviceRentDates, navigation, height } = props;
|
||||||
const isRented = userDeviceRentDates != null;
|
const isRented = userDeviceRentDates != null;
|
||||||
const isAvailable = isEquipmentAvailable(item);
|
const isAvailable = isEquipmentAvailable(item);
|
||||||
const firstAvailability = getFirstEquipmentAvailability(item);
|
const firstAvailability = getFirstEquipmentAvailability(item);
|
||||||
|
@ -52,7 +63,7 @@ function EquipmentListItem(props: PropsType) {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
onPress = () => {
|
onPress = () => {
|
||||||
navigation.navigate('equipment-rent', {item});
|
navigation.navigate('equipment-rent', { item });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +82,7 @@ function EquipmentListItem(props: PropsType) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (isAvailable) {
|
} else if (isAvailable) {
|
||||||
description = i18n.t('screens.equipment.bail', {cost: item.caution});
|
description = i18n.t('screens.equipment.bail', { cost: item.caution });
|
||||||
} else {
|
} else {
|
||||||
description = i18n.t('screens.equipment.available', {
|
description = i18n.t('screens.equipment.available', {
|
||||||
date: getRelativeDateString(firstAvailability),
|
date: getRelativeDateString(firstAvailability),
|
||||||
|
@ -101,21 +112,12 @@ function EquipmentListItem(props: PropsType) {
|
||||||
title={item.name}
|
title={item.name}
|
||||||
description={description}
|
description={description}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
left={() => (
|
left={() => <Avatar.Icon style={styles.icon} icon={icon} color={color} />}
|
||||||
<Avatar.Icon
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
}}
|
|
||||||
icon={icon}
|
|
||||||
color={color}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
right={() => (
|
right={() => (
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
...GENERAL_STYLES.centerVertical,
|
||||||
marginBottom: 'auto',
|
...styles.icon,
|
||||||
backgroundColor: 'transparent',
|
|
||||||
}}
|
}}
|
||||||
size={48}
|
size={48}
|
||||||
icon="chevron-right"
|
icon="chevron-right"
|
||||||
|
@ -123,7 +125,7 @@ function EquipmentListItem(props: PropsType) {
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
height,
|
height,
|
||||||
justifyContent: 'center',
|
...styles.item,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {List, withTheme} from 'react-native-paper';
|
import { List, withTheme } from 'react-native-paper';
|
||||||
import {FlatList, View} from 'react-native';
|
import { FlatList, StyleSheet, View } from 'react-native';
|
||||||
import {stringMatchQuery} from '../../../utils/Search';
|
import { stringMatchQuery } from '../../../utils/Search';
|
||||||
import GroupListItem from './GroupListItem';
|
import GroupListItem from './GroupListItem';
|
||||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||||
import type {
|
import type {
|
||||||
|
@ -40,9 +40,15 @@ type PropsType = {
|
||||||
const LIST_ITEM_HEIGHT = 64;
|
const LIST_ITEM_HEIGHT = 64;
|
||||||
const REPLACE_REGEX = /_/g;
|
const REPLACE_REGEX = /_/g;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class GroupListAccordion extends React.Component<PropsType> {
|
class GroupListAccordion extends React.Component<PropsType> {
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
nextProps.currentSearchString !== props.currentSearchString ||
|
nextProps.currentSearchString !== props.currentSearchString ||
|
||||||
nextProps.favorites.length !== props.favorites.length ||
|
nextProps.favorites.length !== props.favorites.length ||
|
||||||
|
@ -50,8 +56,8 @@ class GroupListAccordion extends React.Component<PropsType> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: PlanexGroupType}) => {
|
getRenderItem = ({ item }: { item: PlanexGroupType }) => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
props.onGroupPress(item);
|
props.onGroupPress(item);
|
||||||
};
|
};
|
||||||
|
@ -70,7 +76,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getData(): Array<PlanexGroupType> {
|
getData(): Array<PlanexGroupType> {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const originalData = props.item.content;
|
const originalData = props.item.content;
|
||||||
const displayData: Array<PlanexGroupType> = [];
|
const displayData: Array<PlanexGroupType> = [];
|
||||||
originalData.forEach((data: PlanexGroupType) => {
|
originalData.forEach((data: PlanexGroupType) => {
|
||||||
|
@ -83,8 +89,8 @@ class GroupListAccordion extends React.Component<PropsType> {
|
||||||
|
|
||||||
itemLayout = (
|
itemLayout = (
|
||||||
data: Array<PlanexGroupType> | null | undefined,
|
data: Array<PlanexGroupType> | null | undefined,
|
||||||
index: number,
|
index: number
|
||||||
): {length: number; offset: number; index: number} => ({
|
): { length: number; offset: number; index: number } => ({
|
||||||
length: LIST_ITEM_HEIGHT,
|
length: LIST_ITEM_HEIGHT,
|
||||||
offset: LIST_ITEM_HEIGHT * index,
|
offset: LIST_ITEM_HEIGHT * index,
|
||||||
index,
|
index,
|
||||||
|
@ -93,15 +99,13 @@ class GroupListAccordion extends React.Component<PropsType> {
|
||||||
keyExtractor = (item: PlanexGroupType): string => item.id.toString();
|
keyExtractor = (item: PlanexGroupType): string => item.id.toString();
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {item} = this.props;
|
const { item } = this.props;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<AnimatedAccordion
|
<AnimatedAccordion
|
||||||
title={item.name.replace(REPLACE_REGEX, ' ')}
|
title={item.name.replace(REPLACE_REGEX, ' ')}
|
||||||
style={{
|
style={styles.container}
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
left={(iconProps) =>
|
left={(iconProps) =>
|
||||||
item.id === 0 ? (
|
item.id === 0 ? (
|
||||||
<List.Icon
|
<List.Icon
|
||||||
|
@ -112,7 +116,8 @@ class GroupListAccordion extends React.Component<PropsType> {
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
unmountWhenCollapsed={item.id !== 0} // Only render list if expanded for increased performance
|
unmountWhenCollapsed={item.id !== 0} // Only render list if expanded for increased performance
|
||||||
opened={props.currentSearchString.length > 0}>
|
opened={props.currentSearchString.length > 0}
|
||||||
|
>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={this.getData()}
|
data={this.getData()}
|
||||||
extraData={props.currentSearchString + props.favorites.length}
|
extraData={props.currentSearchString + props.favorites.length}
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {List, TouchableRipple, withTheme} from 'react-native-paper';
|
import { List, TouchableRipple, withTheme } from 'react-native-paper';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen';
|
import type { PlanexGroupType } from '../../../screens/Planex/GroupSelectionScreen';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {getPrettierPlanexGroupName} from '../../../utils/Utils';
|
import { getPrettierPlanexGroupName } from '../../../utils/Utils';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
theme: ReactNativePaper.Theme;
|
theme: ReactNativePaper.Theme;
|
||||||
|
@ -34,10 +34,25 @@ type PropsType = {
|
||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
marginRight: 10,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class GroupListItem extends React.Component<PropsType> {
|
class GroupListItem extends React.Component<PropsType> {
|
||||||
isFav: boolean;
|
isFav: boolean;
|
||||||
|
|
||||||
starRef: {current: null | (Animatable.View & View)};
|
starRef: { current: null | (Animatable.View & View) };
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
constructor(props: PropsType) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -46,7 +61,7 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {favorites} = this.props;
|
const { favorites } = this.props;
|
||||||
const favChanged = favorites.length !== nextProps.favorites.length;
|
const favChanged = favorites.length !== nextProps.favorites.length;
|
||||||
let newFavState = this.isFav;
|
let newFavState = this.isFav;
|
||||||
if (favChanged) {
|
if (favChanged) {
|
||||||
|
@ -58,7 +73,7 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onStarPress = () => {
|
onStarPress = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const ref = this.starRef;
|
const ref = this.starRef;
|
||||||
if (ref.current && ref.current.rubberBand && ref.current.swing) {
|
if (ref.current && ref.current.rubberBand && ref.current.swing) {
|
||||||
if (this.isFav) {
|
if (this.isFav) {
|
||||||
|
@ -71,7 +86,7 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
|
isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
|
||||||
const {item} = this.props;
|
const { item } = this.props;
|
||||||
for (let i = 0; i < favorites.length; i += 1) {
|
for (let i = 0; i < favorites.length; i += 1) {
|
||||||
if (favorites[i].id === item.id) {
|
if (favorites[i].id === item.id) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -81,8 +96,8 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {colors} = props.theme;
|
const { colors } = props.theme;
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={getPrettierPlanexGroupName(props.item.name)}
|
title={getPrettierPlanexGroupName(props.item.name)}
|
||||||
|
@ -98,15 +113,11 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
<Animatable.View ref={this.starRef} useNativeDriver>
|
<Animatable.View ref={this.starRef} useNativeDriver>
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
onPress={this.onStarPress}
|
onPress={this.onStarPress}
|
||||||
style={{
|
style={styles.iconContainer}
|
||||||
marginRight: 10,
|
>
|
||||||
marginLeft: 'auto',
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}>
|
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
size={30}
|
size={30}
|
||||||
style={{padding: 10}}
|
style={styles.icon}
|
||||||
name="star"
|
name="star"
|
||||||
color={this.isFav ? colors.tetrisScore : iconProps.color}
|
color={this.isFav ? colors.tetrisScore : iconProps.color}
|
||||||
/>
|
/>
|
||||||
|
@ -115,7 +126,7 @@ class GroupListItem extends React.Component<PropsType> {
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
height: props.height,
|
height: props.height,
|
||||||
justifyContent: 'center',
|
...styles.item,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, List, Text} from 'react-native-paper';
|
import { Avatar, List, Text } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import type {ProximoArticleType} from '../../../screens/Services/Proximo/ProximoMainScreen';
|
import type { ProximoArticleType } from '../../../screens/Services/Proximo/ProximoMainScreen';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
|
@ -29,28 +30,38 @@ type PropsType = {
|
||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
avatar: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function ProximoListItem(props: PropsType) {
|
function ProximoListItem(props: PropsType) {
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={props.item.name}
|
title={props.item.name}
|
||||||
description={`${props.item.quantity} ${i18n.t(
|
description={`${props.item.quantity} ${i18n.t(
|
||||||
'screens.proximo.inStock',
|
'screens.proximo.inStock'
|
||||||
)}`}
|
)}`}
|
||||||
descriptionStyle={{color: props.color}}
|
descriptionStyle={{ color: props.color }}
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
left={() => (
|
left={() => (
|
||||||
<Avatar.Image
|
<Avatar.Image
|
||||||
style={{backgroundColor: 'transparent'}}
|
style={styles.avatar}
|
||||||
size={64}
|
size={64}
|
||||||
source={{uri: props.item.image}}
|
source={{ uri: props.item.image }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
right={() => (
|
right={() => <Text style={styles.text}>{props.item.price}€</Text>}
|
||||||
<Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
|
|
||||||
)}
|
|
||||||
style={{
|
style={{
|
||||||
height: props.height,
|
height: props.height,
|
||||||
justifyContent: 'center',
|
...styles.item,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,14 +27,14 @@ import {
|
||||||
Text,
|
Text,
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import ProxiwashConstants, {
|
import ProxiwashConstants, {
|
||||||
MachineStates,
|
MachineStates,
|
||||||
} from '../../../constants/ProxiwashConstants';
|
} from '../../../constants/ProxiwashConstants';
|
||||||
import AprilFoolsManager from '../../../managers/AprilFoolsManager';
|
import AprilFoolsManager from '../../../managers/AprilFoolsManager';
|
||||||
import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen';
|
import type { ProxiwashMachineType } from '../../../screens/Proxiwash/ProxiwashScreen';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
item: ProxiwashMachineType;
|
item: ProxiwashMachineType;
|
||||||
|
@ -42,7 +42,7 @@ type PropsType = {
|
||||||
onPress: (
|
onPress: (
|
||||||
title: string,
|
title: string,
|
||||||
item: ProxiwashMachineType,
|
item: ProxiwashMachineType,
|
||||||
isDryer: boolean,
|
isDryer: boolean
|
||||||
) => void;
|
) => void;
|
||||||
isWatched: boolean;
|
isWatched: boolean;
|
||||||
isDryer: boolean;
|
isDryer: boolean;
|
||||||
|
@ -56,6 +56,7 @@ const styles = StyleSheet.create({
|
||||||
margin: 5,
|
margin: 5,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
|
@ -65,17 +66,29 @@ const styles = StyleSheet.create({
|
||||||
left: 0,
|
left: 0,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
|
item: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
textRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to display a proxiwash item, showing machine progression and state
|
* Component used to display a proxiwash item, showing machine progression and state
|
||||||
*/
|
*/
|
||||||
class ProxiwashListItem extends React.Component<PropsType> {
|
class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
stateStrings: {[key in MachineStates]: string} = {
|
stateStrings: { [key in MachineStates]: string } = {
|
||||||
[MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.states.ready'),
|
[MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.states.ready'),
|
||||||
[MachineStates.RUNNING]: i18n.t('screens.proxiwash.states.running'),
|
[MachineStates.RUNNING]: i18n.t('screens.proxiwash.states.running'),
|
||||||
[MachineStates.RUNNING_NOT_STARTED]: i18n.t(
|
[MachineStates.RUNNING_NOT_STARTED]: i18n.t(
|
||||||
'screens.proxiwash.states.runningNotStarted',
|
'screens.proxiwash.states.runningNotStarted'
|
||||||
),
|
),
|
||||||
[MachineStates.FINISHED]: i18n.t('screens.proxiwash.states.finished'),
|
[MachineStates.FINISHED]: i18n.t('screens.proxiwash.states.finished'),
|
||||||
[MachineStates.UNAVAILABLE]: i18n.t('screens.proxiwash.states.broken'),
|
[MachineStates.UNAVAILABLE]: i18n.t('screens.proxiwash.states.broken'),
|
||||||
|
@ -83,7 +96,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
[MachineStates.UNKNOWN]: i18n.t('screens.proxiwash.states.unknown'),
|
[MachineStates.UNKNOWN]: i18n.t('screens.proxiwash.states.unknown'),
|
||||||
};
|
};
|
||||||
|
|
||||||
stateColors: {[key: string]: string};
|
stateColors: { [key: string]: string };
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
@ -97,7 +110,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
const displayMaxWeight = props.item.maxWeight;
|
const displayMaxWeight = props.item.maxWeight;
|
||||||
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
|
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
|
||||||
displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
|
displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
|
||||||
parseInt(props.item.number, 10),
|
parseInt(props.item.number, 10)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +122,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
nextProps.theme.dark !== props.theme.dark ||
|
nextProps.theme.dark !== props.theme.dark ||
|
||||||
nextProps.item.state !== props.item.state ||
|
nextProps.item.state !== props.item.state ||
|
||||||
|
@ -119,13 +132,13 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onListItemPress = () => {
|
onListItemPress = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.onPress(this.titlePopUp, props.item, props.isDryer);
|
props.onPress(this.titlePopUp, props.item, props.isDryer);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateStateColors() {
|
updateStateColors() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {colors} = props.theme;
|
const { colors } = props.theme;
|
||||||
this.stateColors[MachineStates.AVAILABLE] = colors.proxiwashReadyColor;
|
this.stateColors[MachineStates.AVAILABLE] = colors.proxiwashReadyColor;
|
||||||
this.stateColors[MachineStates.RUNNING] = colors.proxiwashRunningColor;
|
this.stateColors[MachineStates.RUNNING] = colors.proxiwashRunningColor;
|
||||||
this.stateColors[MachineStates.RUNNING_NOT_STARTED] =
|
this.stateColors[MachineStates.RUNNING_NOT_STARTED] =
|
||||||
|
@ -137,8 +150,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {colors} = props.theme;
|
const { colors } = props.theme;
|
||||||
const machineState = props.item.state;
|
const machineState = props.item.state;
|
||||||
const isRunning = machineState === MachineStates.RUNNING;
|
const isRunning = machineState === MachineStates.RUNNING;
|
||||||
const isReady = machineState === MachineStates.AVAILABLE;
|
const isReady = machineState === MachineStates.AVAILABLE;
|
||||||
|
@ -184,8 +197,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
style={{
|
style={{
|
||||||
...styles.container,
|
...styles.container,
|
||||||
height: props.height,
|
height: props.height,
|
||||||
borderRadius: 4,
|
}}
|
||||||
}}>
|
>
|
||||||
{!isReady ? (
|
{!isReady ? (
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
style={{
|
style={{
|
||||||
|
@ -201,26 +214,27 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
description={description}
|
description={description}
|
||||||
style={{
|
style={{
|
||||||
height: props.height,
|
height: props.height,
|
||||||
justifyContent: 'center',
|
...styles.item,
|
||||||
}}
|
}}
|
||||||
onPress={this.onListItemPress}
|
onPress={this.onListItemPress}
|
||||||
left={() => icon}
|
left={() => icon}
|
||||||
right={() => (
|
right={() => (
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={styles.textRow}>
|
||||||
<View style={{justifyContent: 'center'}}>
|
<View style={styles.textContainer}>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
machineState === MachineStates.FINISHED
|
machineState === MachineStates.FINISHED
|
||||||
? {fontWeight: 'bold'}
|
? styles.text
|
||||||
: {}
|
: undefined
|
||||||
}>
|
}
|
||||||
|
>
|
||||||
{stateString}
|
{stateString}
|
||||||
</Text>
|
</Text>
|
||||||
{machineState === MachineStates.RUNNING ? (
|
{machineState === MachineStates.RUNNING ? (
|
||||||
<Caption>{props.item.remainingTime} min</Caption>
|
<Caption>{props.item.remainingTime} min</Caption>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
<View style={{justifyContent: 'center'}}>
|
<View style={styles.textContainer}>
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
icon={stateIcon}
|
icon={stateIcon}
|
||||||
color={colors.text}
|
color={colors.text}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, Text, withTheme} from 'react-native-paper';
|
import { Avatar, Text, withTheme } from 'react-native-paper';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
@ -44,6 +44,9 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
|
textContainer: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +54,7 @@ const styles = StyleSheet.create({
|
||||||
*/
|
*/
|
||||||
class ProxiwashListItem extends React.Component<PropsType> {
|
class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
nextProps.theme.dark !== props.theme.dark ||
|
nextProps.theme.dark !== props.theme.dark ||
|
||||||
nextProps.nbAvailable !== props.nbAvailable
|
nextProps.nbAvailable !== props.nbAvailable
|
||||||
|
@ -59,7 +62,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const subtitle = `${props.nbAvailable} ${
|
const subtitle = `${props.nbAvailable} ${
|
||||||
props.nbAvailable <= 1
|
props.nbAvailable <= 1
|
||||||
? i18n.t('screens.proxiwash.numAvailable')
|
? i18n.t('screens.proxiwash.numAvailable')
|
||||||
|
@ -76,9 +79,9 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
||||||
color={iconColor}
|
color={iconColor}
|
||||||
style={styles.icon}
|
style={styles.icon}
|
||||||
/>
|
/>
|
||||||
<View style={{justifyContent: 'center'}}>
|
<View style={styles.textContainer}>
|
||||||
<Text style={styles.text}>{props.title}</Text>
|
<Text style={styles.text}>{props.title}</Text>
|
||||||
<Text style={{color: props.theme.colors.subtitle}}>{subtitle}</Text>
|
<Text style={{ color: props.theme.colors.subtitle }}>{subtitle}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,8 +19,14 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import {Image, TouchableWithoutFeedback, View, ViewStyle} from 'react-native';
|
import {
|
||||||
import {AnimatableProperties} from 'react-native-animatable';
|
Image,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableWithoutFeedback,
|
||||||
|
View,
|
||||||
|
ViewStyle,
|
||||||
|
} from 'react-native';
|
||||||
|
import { AnimatableProperties } from 'react-native-animatable';
|
||||||
|
|
||||||
export type AnimatableViewRefType = {
|
export type AnimatableViewRefType = {
|
||||||
current: null | (typeof Animatable.View & View);
|
current: null | (typeof Animatable.View & View);
|
||||||
|
@ -77,6 +83,34 @@ export enum MASCOT_STYLE {
|
||||||
RANDOM = 999,
|
RANDOM = 999,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
aspectRatio: 1,
|
||||||
|
},
|
||||||
|
mascot: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
glassesImage: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '15%',
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
eyesImage: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '15%',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
eyesContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class Mascot extends React.Component<PropsType, StateType> {
|
class Mascot extends React.Component<PropsType, StateType> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
emotion: MASCOT_STYLE.NORMAL,
|
emotion: MASCOT_STYLE.NORMAL,
|
||||||
|
@ -100,9 +134,9 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
|
|
||||||
viewRef: AnimatableViewRefType;
|
viewRef: AnimatableViewRefType;
|
||||||
|
|
||||||
eyeList: {[key in EYE_STYLE]: number};
|
eyeList: { [key in EYE_STYLE]: number };
|
||||||
|
|
||||||
glassesList: {[key in GLASSES_STYLE]: number};
|
glassesList: { [key in GLASSES_STYLE]: number };
|
||||||
|
|
||||||
onPress: (viewRef: AnimatableViewRefType) => void;
|
onPress: (viewRef: AnimatableViewRefType) => void;
|
||||||
|
|
||||||
|
@ -141,9 +175,9 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
this.onPress = (viewRef: AnimatableViewRefType) => {
|
this.onPress = (viewRef: AnimatableViewRefType) => {
|
||||||
const ref = viewRef.current;
|
const ref = viewRef.current;
|
||||||
if (ref && ref.rubberBand) {
|
if (ref && ref.rubberBand) {
|
||||||
this.setState({currentEmotion: MASCOT_STYLE.LOVE});
|
this.setState({ currentEmotion: MASCOT_STYLE.LOVE });
|
||||||
ref.rubberBand(1500).then(() => {
|
ref.rubberBand(1500).then(() => {
|
||||||
this.setState({currentEmotion: this.initialEmotion});
|
this.setState({ currentEmotion: this.initialEmotion });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -155,9 +189,9 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
this.onLongPress = (viewRef: AnimatableViewRefType) => {
|
this.onLongPress = (viewRef: AnimatableViewRefType) => {
|
||||||
const ref = viewRef.current;
|
const ref = viewRef.current;
|
||||||
if (ref && ref.tada) {
|
if (ref && ref.tada) {
|
||||||
this.setState({currentEmotion: MASCOT_STYLE.ANGRY});
|
this.setState({ currentEmotion: MASCOT_STYLE.ANGRY });
|
||||||
ref.tada(1000).then(() => {
|
ref.tada(1000).then(() => {
|
||||||
this.setState({currentEmotion: this.initialEmotion});
|
this.setState({ currentEmotion: this.initialEmotion });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -174,30 +208,22 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
source={
|
source={
|
||||||
glasses != null ? glasses : this.glassesList[GLASSES_STYLE.NORMAL]
|
glasses != null ? glasses : this.glassesList[GLASSES_STYLE.NORMAL]
|
||||||
}
|
}
|
||||||
style={{
|
style={styles.glassesImage}
|
||||||
position: 'absolute',
|
|
||||||
top: '15%',
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEye(style: EYE_STYLE, isRight: boolean, rotation: string = '0deg') {
|
getEye(style: EYE_STYLE, isRight: boolean, rotation: string = '0deg') {
|
||||||
const eye = this.eyeList[style];
|
const eye = this.eyeList[style];
|
||||||
|
const left = isRight ? '-11%' : '11%';
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
key={isRight ? 'right' : 'left'}
|
key={isRight ? 'right' : 'left'}
|
||||||
source={eye != null ? eye : this.eyeList[EYE_STYLE.NORMAL]}
|
source={eye != null ? eye : this.eyeList[EYE_STYLE.NORMAL]}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
...styles.eyesImage,
|
||||||
top: '15%',
|
left: left,
|
||||||
left: isRight ? '-11%' : '11%',
|
transform: [{ rotateY: rotation }],
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
transform: [{rotateY: rotation}],
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -205,16 +231,7 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
|
|
||||||
getEyes(emotion: MASCOT_STYLE) {
|
getEyes(emotion: MASCOT_STYLE) {
|
||||||
const final = [];
|
const final = [];
|
||||||
final.push(
|
final.push(<View key="container" style={styles.eyesContainer} />);
|
||||||
<View
|
|
||||||
key="container"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
if (emotion === MASCOT_STYLE.CUTE) {
|
if (emotion === MASCOT_STYLE.CUTE) {
|
||||||
final.push(this.getEye(EYE_STYLE.CUTE, true));
|
final.push(this.getEye(EYE_STYLE.CUTE, true));
|
||||||
final.push(this.getEye(EYE_STYLE.CUTE, false));
|
final.push(this.getEye(EYE_STYLE.CUTE, false));
|
||||||
|
@ -249,32 +266,28 @@ class Mascot extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
const entryAnimation = props.animated ? props.entryAnimation : null;
|
const entryAnimation = props.animated ? props.entryAnimation : null;
|
||||||
const loopAnimation = props.animated ? props.loopAnimation : null;
|
const loopAnimation = props.animated ? props.loopAnimation : null;
|
||||||
return (
|
return (
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
style={{
|
style={{
|
||||||
aspectRatio: 1,
|
...styles.container,
|
||||||
...props.style,
|
...props.style,
|
||||||
}}
|
}}
|
||||||
{...entryAnimation}>
|
{...entryAnimation}
|
||||||
|
>
|
||||||
<TouchableWithoutFeedback
|
<TouchableWithoutFeedback
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.onPress(this.viewRef);
|
this.onPress(this.viewRef);
|
||||||
}}
|
}}
|
||||||
onLongPress={() => {
|
onLongPress={() => {
|
||||||
this.onLongPress(this.viewRef);
|
this.onLongPress(this.viewRef);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<Animatable.View ref={this.viewRef}>
|
<Animatable.View ref={this.viewRef}>
|
||||||
<Animatable.View {...loopAnimation}>
|
<Animatable.View {...loopAnimation}>
|
||||||
<Image
|
<Image source={MASCOT_IMAGE} style={styles.mascot} />
|
||||||
source={MASCOT_IMAGE}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{this.getEyes(state.currentEmotion)}
|
{this.getEyes(state.currentEmotion)}
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
|
|
|
@ -31,12 +31,14 @@ import {
|
||||||
BackHandler,
|
BackHandler,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Mascot from './Mascot';
|
import Mascot from './Mascot';
|
||||||
import SpeechArrow from './SpeechArrow';
|
import SpeechArrow from './SpeechArrow';
|
||||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
theme: ReactNativePaper.Theme;
|
theme: ReactNativePaper.Theme;
|
||||||
|
@ -67,6 +69,41 @@ type StateType = {
|
||||||
dialogVisible: boolean;
|
dialogVisible: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
speechBubbleContainer: {
|
||||||
|
marginLeft: '10%',
|
||||||
|
marginRight: '10%',
|
||||||
|
},
|
||||||
|
speechBubbleCard: {
|
||||||
|
borderWidth: 4,
|
||||||
|
borderRadius: 10,
|
||||||
|
},
|
||||||
|
speechBubbleIcon: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
speechBubbleText: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
actionsContainer: {
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
position: 'absolute',
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
marginTop: -80,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to display a popup with the mascot.
|
* Component used to display a popup with the mascot.
|
||||||
*/
|
*/
|
||||||
|
@ -107,12 +144,13 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
BackHandler.addEventListener(
|
BackHandler.addEventListener(
|
||||||
'hardwareBackPress',
|
'hardwareBackPress',
|
||||||
this.onBackButtonPressAndroid,
|
this.onBackButtonPressAndroid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean {
|
shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean {
|
||||||
const {props, state} = this;
|
// TODO this is so dirty it shouldn't even work
|
||||||
|
const { props, state } = this;
|
||||||
if (nextProps.visible) {
|
if (nextProps.visible) {
|
||||||
this.state.shouldRenderDialog = true;
|
this.state.shouldRenderDialog = true;
|
||||||
this.state.dialogVisible = true;
|
this.state.dialogVisible = true;
|
||||||
|
@ -134,10 +172,10 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onBackButtonPressAndroid = (): boolean => {
|
onBackButtonPressAndroid = (): boolean => {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
if (state.dialogVisible) {
|
if (state.dialogVisible) {
|
||||||
const {cancel} = props.buttons;
|
const { cancel } = props.buttons;
|
||||||
const {action} = props.buttons;
|
const { action } = props.buttons;
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
this.onDismiss(cancel.onPress);
|
this.onDismiss(cancel.onPress);
|
||||||
} else if (action) {
|
} else if (action) {
|
||||||
|
@ -152,27 +190,25 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getSpeechBubble() {
|
getSpeechBubble() {
|
||||||
const {state, props} = this;
|
const { state, props } = this;
|
||||||
return (
|
return (
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
style={{
|
style={styles.speechBubbleContainer}
|
||||||
marginLeft: '10%',
|
|
||||||
marginRight: '10%',
|
|
||||||
}}
|
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
||||||
duration={state.dialogVisible ? 1000 : 300}>
|
duration={state.dialogVisible ? 1000 : 300}
|
||||||
|
>
|
||||||
<SpeechArrow
|
<SpeechArrow
|
||||||
style={{marginLeft: this.mascotSize / 3}}
|
style={{ marginLeft: this.mascotSize / 3 }}
|
||||||
size={20}
|
size={20}
|
||||||
color={props.theme.colors.mascotMessageArrow}
|
color={props.theme.colors.mascotMessageArrow}
|
||||||
/>
|
/>
|
||||||
<Card
|
<Card
|
||||||
style={{
|
style={{
|
||||||
borderColor: props.theme.colors.mascotMessageArrow,
|
borderColor: props.theme.colors.mascotMessageArrow,
|
||||||
borderWidth: 4,
|
...styles.speechBubbleCard,
|
||||||
borderRadius: 10,
|
}}
|
||||||
}}>
|
>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={props.title}
|
title={props.title}
|
||||||
left={
|
left={
|
||||||
|
@ -180,7 +216,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
? () => (
|
? () => (
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
size={48}
|
size={48}
|
||||||
style={{backgroundColor: 'transparent'}}
|
style={styles.speechBubbleIcon}
|
||||||
color={props.theme.colors.primary}
|
color={props.theme.colors.primary}
|
||||||
icon={props.icon}
|
icon={props.icon}
|
||||||
/>
|
/>
|
||||||
|
@ -191,13 +227,16 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
<Card.Content
|
<Card.Content
|
||||||
style={{
|
style={{
|
||||||
maxHeight: this.windowHeight / 3,
|
maxHeight: this.windowHeight / 3,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<Paragraph style={{marginBottom: 10}}>{props.message}</Paragraph>
|
<Paragraph style={styles.speechBubbleText}>
|
||||||
|
{props.message}
|
||||||
|
</Paragraph>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
|
|
||||||
<Card.Actions style={{marginTop: 10, marginBottom: 10}}>
|
<Card.Actions style={styles.actionsContainer}>
|
||||||
{this.getButtons()}
|
{this.getButtons()}
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -206,14 +245,15 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getMascot() {
|
getMascot() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
||||||
duration={state.dialogVisible ? 1500 : 200}>
|
duration={state.dialogVisible ? 1500 : 200}
|
||||||
|
>
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{width: this.mascotSize}}
|
style={{ width: this.mascotSize }}
|
||||||
animated
|
animated
|
||||||
emotion={props.emotion}
|
emotion={props.emotion}
|
||||||
/>
|
/>
|
||||||
|
@ -222,45 +262,34 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getButtons() {
|
getButtons() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {action} = props.buttons;
|
const { action } = props.buttons;
|
||||||
const {cancel} = props.buttons;
|
const { cancel } = props.buttons;
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={GENERAL_STYLES.center}>
|
||||||
style={{
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}>
|
|
||||||
{action != null ? (
|
{action != null ? (
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={styles.button}
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon={action.icon}
|
icon={action.icon}
|
||||||
color={action.color}
|
color={action.color}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.onDismiss(action.onPress);
|
this.onDismiss(action.onPress);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{action.message}
|
{action.message}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
{cancel != null ? (
|
{cancel != null ? (
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={styles.button}
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon={cancel.icon}
|
icon={cancel.icon}
|
||||||
color={cancel.color}
|
color={cancel.color}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.onDismiss(cancel.onPress);
|
this.onDismiss(cancel.onPress);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{cancel.message}
|
{cancel.message}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -269,19 +298,15 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBackground() {
|
getBackground() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
<TouchableWithoutFeedback
|
<TouchableWithoutFeedback
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.onDismiss(props.buttons.cancel?.onPress);
|
this.onDismiss(props.buttons.cancel?.onPress);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
style={{
|
style={styles.background}
|
||||||
position: 'absolute',
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation={state.dialogVisible ? 'fadeIn' : 'fadeOut'}
|
animation={state.dialogVisible ? 'fadeIn' : 'fadeOut'}
|
||||||
duration={state.dialogVisible ? 300 : 300}
|
duration={state.dialogVisible ? 300 : 300}
|
||||||
|
@ -291,10 +316,10 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onDismiss = (callback?: () => void) => {
|
onDismiss = (callback?: () => void) => {
|
||||||
const {prefKey} = this.props;
|
const { prefKey } = this.props;
|
||||||
if (prefKey != null) {
|
if (prefKey != null) {
|
||||||
AsyncStorageManager.set(prefKey, false);
|
AsyncStorageManager.set(prefKey, false);
|
||||||
this.setState({dialogVisible: false});
|
this.setState({ dialogVisible: false });
|
||||||
}
|
}
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -302,21 +327,13 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {shouldRenderDialog} = this.state;
|
const { shouldRenderDialog } = this.state;
|
||||||
if (shouldRenderDialog) {
|
if (shouldRenderDialog) {
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
{this.getBackground()}
|
{this.getBackground()}
|
||||||
<View
|
<View style={GENERAL_STYLES.centerVertical}>
|
||||||
style={{
|
<View style={styles.container}>
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
marginTop: -80,
|
|
||||||
width: '100%',
|
|
||||||
}}>
|
|
||||||
{this.getMascot()}
|
{this.getMascot()}
|
||||||
{this.getSpeechBubble()}
|
{this.getSpeechBubble()}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View, ViewStyle} from 'react-native';
|
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
style?: ViewStyle;
|
style?: ViewStyle;
|
||||||
|
@ -26,20 +26,26 @@ type PropsType = {
|
||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
arrow: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderLeftWidth: 0,
|
||||||
|
borderStyle: 'solid',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default function SpeechArrow(props: PropsType) {
|
export default function SpeechArrow(props: PropsType) {
|
||||||
return (
|
return (
|
||||||
<View style={props.style}>
|
<View style={props.style}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: 0,
|
...styles.arrow,
|
||||||
height: 0,
|
|
||||||
borderLeftWidth: 0,
|
|
||||||
borderRightWidth: props.size,
|
borderRightWidth: props.size,
|
||||||
borderBottomWidth: props.size,
|
borderBottomWidth: props.size,
|
||||||
borderStyle: 'solid',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderLeftColor: 'transparent',
|
|
||||||
borderRightColor: 'transparent',
|
|
||||||
borderBottomColor: props.color,
|
borderBottomColor: props.color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,32 +18,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {TouchableRipple} from 'react-native-paper';
|
import { TouchableRipple } from 'react-native-paper';
|
||||||
import {Image} from 'react-native-animatable';
|
import { Image } from 'react-native-animatable';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import {ViewStyle} from 'react-native';
|
import { StyleSheet, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
images: Array<{url: string}>;
|
images: Array<{ url: string }>;
|
||||||
style: ViewStyle;
|
style: ViewStyle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function ImageGalleryButton(props: PropsType) {
|
function ImageGalleryButton(props: PropsType) {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
navigation.navigate('gallery', {images: props.images});
|
navigation.navigate('gallery', { images: props.images });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableRipple onPress={onPress} style={props.style}>
|
<TouchableRipple onPress={onPress} style={props.style}>
|
||||||
<Image
|
<Image
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
source={{uri: props.images[0].url}}
|
source={{ uri: props.images[0].url }}
|
||||||
style={{
|
style={styles.image}
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</TouchableRipple>
|
</TouchableRipple>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import { View } from 'react-native';
|
||||||
import {useTheme} from 'react-native-paper';
|
import { useTheme } from 'react-native-paper';
|
||||||
import {Agenda, AgendaProps} from 'react-native-calendars';
|
import { Agenda, AgendaProps } from 'react-native-calendars';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
onRef: (ref: Agenda<any>) => void;
|
onRef: (ref: Agenda<any>) => void;
|
||||||
|
@ -67,7 +68,7 @@ function CustomAgenda(props: PropsType) {
|
||||||
|
|
||||||
// Completely recreate the component on theme change to force theme reload
|
// Completely recreate the component on theme change to force theme reload
|
||||||
if (theme.dark) {
|
if (theme.dark) {
|
||||||
return <View style={{flex: 1}}>{getAgenda()}</View>;
|
return <View style={GENERAL_STYLES.flex}>{getAgenda()}</View>;
|
||||||
}
|
}
|
||||||
return getAgenda();
|
return getAgenda();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Text} from 'react-native-paper';
|
import { Text } from 'react-native-paper';
|
||||||
import HTML from 'react-native-render-html';
|
import HTML from 'react-native-render-html';
|
||||||
import {GestureResponderEvent, Linking} from 'react-native';
|
import { GestureResponderEvent, Linking } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
html: string;
|
html: string;
|
||||||
|
@ -38,7 +38,7 @@ function CustomHTML(props: PropsType) {
|
||||||
htmlAttribs: any,
|
htmlAttribs: any,
|
||||||
children: any,
|
children: any,
|
||||||
convertedCSSStyles: any,
|
convertedCSSStyles: any,
|
||||||
passProps: any,
|
passProps: any
|
||||||
) => {
|
) => {
|
||||||
return <Text {...passProps}>{children}</Text>;
|
return <Text {...passProps}>{children}</Text>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,7 +25,7 @@ import {
|
||||||
HeaderButtons,
|
HeaderButtons,
|
||||||
HeaderButtonsProps,
|
HeaderButtonsProps,
|
||||||
} from 'react-navigation-header-buttons';
|
} from 'react-navigation-header-buttons';
|
||||||
import {useTheme} from 'react-native-paper';
|
import { useTheme } from 'react-native-paper';
|
||||||
|
|
||||||
const MaterialHeaderButton = (props: HeaderButtonProps) => {
|
const MaterialHeaderButton = (props: HeaderButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -40,7 +40,7 @@ const MaterialHeaderButton = (props: HeaderButtonProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const MaterialHeaderButtons = (
|
const MaterialHeaderButtons = (
|
||||||
props: HeaderButtonsProps & {children?: React.ReactNode},
|
props: HeaderButtonsProps & { children?: React.ReactNode }
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<HeaderButtons {...props} HeaderButtonComponent={MaterialHeaderButton} />
|
<HeaderButtons {...props} HeaderButtonComponent={MaterialHeaderButton} />
|
||||||
|
@ -49,4 +49,4 @@ const MaterialHeaderButtons = (
|
||||||
|
|
||||||
export default MaterialHeaderButtons;
|
export default MaterialHeaderButtons;
|
||||||
|
|
||||||
export {Item} from 'react-navigation-header-buttons';
|
export { Item } from 'react-navigation-header-buttons';
|
||||||
|
|
|
@ -30,13 +30,14 @@ import i18n from 'i18n-js';
|
||||||
import AppIntroSlider from 'react-native-app-intro-slider';
|
import AppIntroSlider from 'react-native-app-intro-slider';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import {Card} from 'react-native-paper';
|
import { Card } from 'react-native-paper';
|
||||||
import Update from '../../constants/Update';
|
import Update from '../../constants/Update';
|
||||||
import ThemeManager from '../../managers/ThemeManager';
|
import ThemeManager from '../../managers/ThemeManager';
|
||||||
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
|
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
|
||||||
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
|
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
|
||||||
import IntroIcon from '../Intro/IconIntro';
|
import IntroIcon from '../Intro/IconIntro';
|
||||||
import MascotIntroEnd from '../Intro/MascotIntroEnd';
|
import MascotIntroEnd from '../Intro/MascotIntroEnd';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
onDone: () => void;
|
onDone: () => void;
|
||||||
|
@ -75,11 +76,42 @@ const styles = StyleSheet.create({
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
center: {
|
mascot: {
|
||||||
marginTop: 'auto',
|
marginLeft: 30,
|
||||||
marginBottom: 'auto',
|
marginBottom: 0,
|
||||||
marginRight: 'auto',
|
width: 100,
|
||||||
marginLeft: 'auto',
|
marginTop: -30,
|
||||||
|
},
|
||||||
|
speechArrow: {
|
||||||
|
marginLeft: 50,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderLeftWidth: 20,
|
||||||
|
borderRightWidth: 0,
|
||||||
|
borderBottomWidth: 20,
|
||||||
|
borderStyle: 'solid',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderBottomColor: 'rgba(0,0,0,0.60)',
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.38)',
|
||||||
|
marginHorizontal: 20,
|
||||||
|
borderColor: 'rgba(0,0,0,0.60)',
|
||||||
|
borderWidth: 4,
|
||||||
|
borderRadius: 10,
|
||||||
|
elevation: 0,
|
||||||
|
},
|
||||||
|
nextButtonContainer: {
|
||||||
|
borderRadius: 25,
|
||||||
|
padding: 5,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||||
|
},
|
||||||
|
doneButtonContainer: {
|
||||||
|
borderRadius: 25,
|
||||||
|
padding: 5,
|
||||||
|
backgroundColor: 'rgb(190,21,34)',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,7 +122,7 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
PropsType,
|
PropsType,
|
||||||
StateType
|
StateType
|
||||||
> {
|
> {
|
||||||
sliderRef: {current: null | AppIntroSlider};
|
sliderRef: { current: null | AppIntroSlider };
|
||||||
|
|
||||||
introSlides: Array<IntroSlideType>;
|
introSlides: Array<IntroSlideType>;
|
||||||
|
|
||||||
|
@ -173,31 +205,27 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
getIntroRenderItem = (
|
getIntroRenderItem = (
|
||||||
data:
|
data:
|
||||||
| (ListRenderItemInfo<IntroSlideType> & {
|
| (ListRenderItemInfo<IntroSlideType> & {
|
||||||
dimensions: {width: number; height: number};
|
dimensions: { width: number; height: number };
|
||||||
})
|
})
|
||||||
| ListRenderItemInfo<IntroSlideType>,
|
| ListRenderItemInfo<IntroSlideType>
|
||||||
) => {
|
) => {
|
||||||
const item = data.item;
|
const item = data.item;
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
const index = parseInt(item.key, 10);
|
const index = parseInt(item.key, 10);
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
style={[styles.mainContent]}
|
style={[styles.mainContent]}
|
||||||
colors={item.colors}
|
colors={item.colors}
|
||||||
start={{x: 0, y: 0.1}}
|
start={{ x: 0, y: 0.1 }}
|
||||||
end={{x: 0.1, y: 1}}>
|
end={{ x: 0.1, y: 1 }}
|
||||||
|
>
|
||||||
{state.currentSlide === index ? (
|
{state.currentSlide === index ? (
|
||||||
<View style={{height: '100%', flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<View style={{flex: 1}}>{item.view()}</View>
|
<View style={GENERAL_STYLES.flex}>{item.view()}</View>
|
||||||
<Animatable.View useNativeDriver animation="fadeIn">
|
<Animatable.View useNativeDriver animation="fadeIn">
|
||||||
{item.mascotStyle != null ? (
|
{item.mascotStyle != null ? (
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{
|
style={styles.mascot}
|
||||||
marginLeft: 30,
|
|
||||||
marginBottom: 0,
|
|
||||||
width: 100,
|
|
||||||
marginTop: -30,
|
|
||||||
}}
|
|
||||||
emotion={item.mascotStyle}
|
emotion={item.mascotStyle}
|
||||||
animated
|
animated
|
||||||
entryAnimation={{
|
entryAnimation={{
|
||||||
|
@ -211,43 +239,23 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<View
|
<View style={styles.speechArrow} />
|
||||||
style={{
|
<Card style={styles.card}>
|
||||||
marginLeft: 50,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
borderLeftWidth: 20,
|
|
||||||
borderRightWidth: 0,
|
|
||||||
borderBottomWidth: 20,
|
|
||||||
borderStyle: 'solid',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderLeftColor: 'transparent',
|
|
||||||
borderRightColor: 'transparent',
|
|
||||||
borderBottomColor: 'rgba(0,0,0,0.60)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.38)',
|
|
||||||
marginHorizontal: 20,
|
|
||||||
borderColor: 'rgba(0,0,0,0.60)',
|
|
||||||
borderWidth: 4,
|
|
||||||
borderRadius: 10,
|
|
||||||
elevation: 0,
|
|
||||||
}}>
|
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Animatable.Text
|
<Animatable.Text
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation="fadeIn"
|
animation="fadeIn"
|
||||||
delay={100}
|
delay={100}
|
||||||
style={styles.title}>
|
style={styles.title}
|
||||||
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Animatable.Text>
|
</Animatable.Text>
|
||||||
<Animatable.Text
|
<Animatable.Text
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation="fadeIn"
|
animation="fadeIn"
|
||||||
delay={200}
|
delay={200}
|
||||||
style={styles.text}>
|
style={styles.text}
|
||||||
|
>
|
||||||
{item.text}
|
{item.text}
|
||||||
</Animatable.Text>
|
</Animatable.Text>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
|
@ -267,12 +275,12 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
|
|
||||||
onSlideChange = (index: number) => {
|
onSlideChange = (index: number) => {
|
||||||
CustomIntroSlider.setStatusBarColor(this.currentSlides[index].colors[0]);
|
CustomIntroSlider.setStatusBarColor(this.currentSlides[index].colors[0]);
|
||||||
this.setState({currentSlide: index});
|
this.setState({ currentSlide: index });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSkip = () => {
|
onSkip = () => {
|
||||||
CustomIntroSlider.setStatusBarColor(
|
CustomIntroSlider.setStatusBarColor(
|
||||||
this.currentSlides[this.currentSlides.length - 1].colors[0],
|
this.currentSlides[this.currentSlides.length - 1].colors[0]
|
||||||
);
|
);
|
||||||
if (this.sliderRef.current != null) {
|
if (this.sliderRef.current != null) {
|
||||||
this.sliderRef.current.goToSlide(this.currentSlides.length - 1);
|
this.sliderRef.current.goToSlide(this.currentSlides.length - 1);
|
||||||
|
@ -280,9 +288,9 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
};
|
};
|
||||||
|
|
||||||
onDone = () => {
|
onDone = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
CustomIntroSlider.setStatusBarColor(
|
CustomIntroSlider.setStatusBarColor(
|
||||||
ThemeManager.getCurrentTheme().colors.surface,
|
ThemeManager.getCurrentTheme().colors.surface
|
||||||
);
|
);
|
||||||
props.onDone();
|
props.onDone();
|
||||||
};
|
};
|
||||||
|
@ -292,11 +300,8 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation="fadeIn"
|
animation="fadeIn"
|
||||||
style={{
|
style={styles.nextButtonContainer}
|
||||||
borderRadius: 25,
|
>
|
||||||
padding: 5,
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
|
||||||
}}>
|
|
||||||
<MaterialCommunityIcons name="arrow-right" color="#fff" size={40} />
|
<MaterialCommunityIcons name="arrow-right" color="#fff" size={40} />
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
);
|
);
|
||||||
|
@ -307,18 +312,15 @@ export default class CustomIntroSlider extends React.Component<
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
animation="bounceIn"
|
animation="bounceIn"
|
||||||
style={{
|
style={styles.doneButtonContainer}
|
||||||
borderRadius: 25,
|
>
|
||||||
padding: 5,
|
|
||||||
backgroundColor: 'rgb(190,21,34)',
|
|
||||||
}}>
|
|
||||||
<MaterialCommunityIcons name="check" color="#fff" size={40} />
|
<MaterialCommunityIcons name="check" color="#fff" size={40} />
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
this.currentSlides = this.introSlides;
|
this.currentSlides = this.introSlides;
|
||||||
if (props.isUpdate) {
|
if (props.isUpdate) {
|
||||||
this.currentSlides = this.updateSlides;
|
this.currentSlides = this.updateSlides;
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useTheme} from 'react-native-paper';
|
import { useTheme } from 'react-native-paper';
|
||||||
import {Modalize} from 'react-native-modalize';
|
import { Modalize } from 'react-native-modalize';
|
||||||
import {View} from 'react-native-animatable';
|
import { View } from 'react-native-animatable';
|
||||||
import CustomTabBar from '../Tabbar/CustomTabBar';
|
import CustomTabBar from '../Tabbar/CustomTabBar';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,18 +34,20 @@ function CustomModal(props: {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {onRef, children} = props;
|
const { onRef, children } = props;
|
||||||
return (
|
return (
|
||||||
<Modalize
|
<Modalize
|
||||||
ref={onRef}
|
ref={onRef}
|
||||||
adjustToContentHeight
|
adjustToContentHeight
|
||||||
handlePosition="inside"
|
handlePosition="inside"
|
||||||
modalStyle={{backgroundColor: theme.colors.card}}
|
modalStyle={{ backgroundColor: theme.colors.card }}
|
||||||
handleStyle={{backgroundColor: theme.colors.primary}}>
|
handleStyle={{ backgroundColor: theme.colors.primary }}
|
||||||
|
>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
|
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
</Modalize>
|
</Modalize>
|
||||||
|
|
|
@ -18,15 +18,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Text} from 'react-native-paper';
|
import { Text } from 'react-native-paper';
|
||||||
import {View} from 'react-native-animatable';
|
import { View } from 'react-native-animatable';
|
||||||
import Slider, {SliderProps} from '@react-native-community/slider';
|
import Slider, { SliderProps } from '@react-native-community/slider';
|
||||||
import {useState} from 'react';
|
import { useState } from 'react';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
valueSuffix?: string;
|
valueSuffix?: string;
|
||||||
} & SliderProps;
|
} & SliderProps;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
marginHorizontal: 10,
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction layer for Modalize component, using custom configuration
|
* Abstraction layer for Modalize component, using custom configuration
|
||||||
*
|
*
|
||||||
|
@ -44,15 +57,8 @@ function CustomSlider(props: PropsType) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
<View style={styles.container}>
|
||||||
<Text
|
<Text style={styles.text}>{currentValue}min</Text>
|
||||||
style={{
|
|
||||||
marginHorizontal: 10,
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}>
|
|
||||||
{currentValue}min
|
|
||||||
</Text>
|
|
||||||
<Slider {...props} ref={undefined} onValueChange={onValueChange} />
|
<Slider {...props} ref={undefined} onValueChange={onValueChange} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,16 +17,24 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {ActivityIndicator, useTheme} from 'react-native-paper';
|
import { ActivityIndicator, useTheme } from 'react-native-paper';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isAbsolute?: boolean;
|
isAbsolute?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to display a header button
|
* Component used to display a header button
|
||||||
*
|
*
|
||||||
|
@ -35,18 +43,16 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
export default function BasicLoadingScreen(props: Props) {
|
export default function BasicLoadingScreen(props: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const {isAbsolute} = props;
|
const { isAbsolute } = props;
|
||||||
|
const position = isAbsolute ? 'absolute' : 'relative';
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.colors.background,
|
backgroundColor: theme.colors.background,
|
||||||
position: isAbsolute ? 'absolute' : 'relative',
|
position: position,
|
||||||
top: 0,
|
...styles.container,
|
||||||
right: 0,
|
}}
|
||||||
width: '100%',
|
>
|
||||||
height: '100%',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}>
|
|
||||||
<ActivityIndicator animating size="large" color={theme.colors.primary} />
|
<ActivityIndicator animating size="large" color={theme.colors.primary} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,18 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Button, Subheading, withTheme} from 'react-native-paper';
|
import { Button, Subheading, withTheme } from 'react-native-paper';
|
||||||
import {StyleSheet, View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import {ERROR_TYPE} from '../../utils/WebData';
|
import { ERROR_TYPE } from '../../utils/WebData';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
navigation?: StackNavigationProp<any>;
|
navigation?: StackNavigationProp<any>;
|
||||||
theme: ReactNativePaper.Theme;
|
theme: ReactNativePaper.Theme;
|
||||||
route?: {name: string};
|
route?: { name: string };
|
||||||
onRefresh?: () => void;
|
onRefresh?: () => void;
|
||||||
errorCode?: number;
|
errorCode?: number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
@ -84,13 +84,14 @@ class ErrorView extends React.PureComponent<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getRetryButton() {
|
getRetryButton() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="refresh"
|
icon="refresh"
|
||||||
onPress={props.onRefresh}
|
onPress={props.onRefresh}
|
||||||
style={styles.button}>
|
style={styles.button}
|
||||||
|
>
|
||||||
{i18n.t('general.retry')}
|
{i18n.t('general.retry')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -102,24 +103,25 @@ class ErrorView extends React.PureComponent<PropsType> {
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="login"
|
icon="login"
|
||||||
onPress={this.goToLogin}
|
onPress={this.goToLogin}
|
||||||
style={styles.button}>
|
style={styles.button}
|
||||||
|
>
|
||||||
{i18n.t('screens.login.title')}
|
{i18n.t('screens.login.title')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
goToLogin = () => {
|
goToLogin = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
if (props.navigation) {
|
if (props.navigation) {
|
||||||
props.navigation.navigate('login', {
|
props.navigation.navigate('login', {
|
||||||
screen: 'login',
|
screen: 'login',
|
||||||
params: {nextScreen: props.route ? props.route.name : undefined},
|
params: { nextScreen: props.route ? props.route.name : undefined },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
generateMessage() {
|
generateMessage() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
this.showLoginButton = false;
|
this.showLoginButton = false;
|
||||||
if (props.errorCode !== 0) {
|
if (props.errorCode !== 0) {
|
||||||
switch (props.errorCode) {
|
switch (props.errorCode) {
|
||||||
|
@ -171,7 +173,7 @@ class ErrorView extends React.PureComponent<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
this.generateMessage();
|
this.generateMessage();
|
||||||
let button;
|
let button;
|
||||||
if (this.showLoginButton) {
|
if (this.showLoginButton) {
|
||||||
|
@ -190,7 +192,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
||||||
}}
|
}}
|
||||||
animation="zoomIn"
|
animation="zoomIn"
|
||||||
duration={200}
|
duration={200}
|
||||||
useNativeDriver>
|
useNativeDriver
|
||||||
|
>
|
||||||
<View style={styles.inner}>
|
<View style={styles.inner}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
|
@ -204,7 +207,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
||||||
style={{
|
style={{
|
||||||
...styles.subheading,
|
...styles.subheading,
|
||||||
color: props.theme.colors.textDisabled,
|
color: props.theme.colors.textDisabled,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{this.message}
|
{this.message}
|
||||||
</Subheading>
|
</Subheading>
|
||||||
{button}
|
{button}
|
||||||
|
|
|
@ -19,21 +19,22 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Snackbar} from 'react-native-paper';
|
import { Snackbar } from 'react-native-paper';
|
||||||
import {
|
import {
|
||||||
NativeSyntheticEvent,
|
NativeSyntheticEvent,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
SectionListData,
|
SectionListData,
|
||||||
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import {Collapsible} from 'react-navigation-collapsible';
|
import { Collapsible } from 'react-navigation-collapsible';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import ErrorView from './ErrorView';
|
import ErrorView from './ErrorView';
|
||||||
import BasicLoadingScreen from './BasicLoadingScreen';
|
import BasicLoadingScreen from './BasicLoadingScreen';
|
||||||
import withCollapsible from '../../utils/withCollapsible';
|
import withCollapsible from '../../utils/withCollapsible';
|
||||||
import CustomTabBar from '../Tabbar/CustomTabBar';
|
import CustomTabBar from '../Tabbar/CustomTabBar';
|
||||||
import {ERROR_TYPE, readData} from '../../utils/WebData';
|
import { ERROR_TYPE, readData } from '../../utils/WebData';
|
||||||
import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
|
import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
|
||||||
|
|
||||||
export type SectionListDataType<ItemT> = Array<{
|
export type SectionListDataType<ItemT> = Array<{
|
||||||
|
@ -48,10 +49,10 @@ type PropsType<ItemT, RawData> = {
|
||||||
fetchUrl: string;
|
fetchUrl: string;
|
||||||
autoRefreshTime: number;
|
autoRefreshTime: number;
|
||||||
refreshOnFocus: boolean;
|
refreshOnFocus: boolean;
|
||||||
renderItem: (data: {item: ItemT}) => React.ReactNode;
|
renderItem: (data: { item: ItemT }) => React.ReactNode;
|
||||||
createDataset: (
|
createDataset: (
|
||||||
data: RawData | null,
|
data: RawData | null,
|
||||||
isLoading?: boolean,
|
isLoading?: boolean
|
||||||
) => SectionListDataType<ItemT>;
|
) => SectionListDataType<ItemT>;
|
||||||
onScroll: (event: NativeSyntheticEvent<EventTarget>) => void;
|
onScroll: (event: NativeSyntheticEvent<EventTarget>) => void;
|
||||||
collapsibleStack: Collapsible;
|
collapsibleStack: Collapsible;
|
||||||
|
@ -60,11 +61,11 @@ type PropsType<ItemT, RawData> = {
|
||||||
itemHeight?: number | null;
|
itemHeight?: number | null;
|
||||||
updateData?: number;
|
updateData?: number;
|
||||||
renderListHeaderComponent?: (
|
renderListHeaderComponent?: (
|
||||||
data: RawData | null,
|
data: RawData | null
|
||||||
) => React.ComponentType<any> | React.ReactElement | null;
|
) => React.ComponentType<any> | React.ReactElement | null;
|
||||||
renderSectionHeader?: (
|
renderSectionHeader?: (
|
||||||
data: {section: SectionListData<ItemT>},
|
data: { section: SectionListData<ItemT> },
|
||||||
isLoading?: boolean,
|
isLoading?: boolean
|
||||||
) => React.ReactElement | null;
|
) => React.ReactElement | null;
|
||||||
stickyHeader?: boolean;
|
stickyHeader?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -77,6 +78,12 @@ type StateType<RawData> = {
|
||||||
|
|
||||||
const MIN_REFRESH_TIME = 5 * 1000;
|
const MIN_REFRESH_TIME = 5 * 1000;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
minHeight: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to render a SectionList with data fetched from the web
|
* Component used to render a SectionList with data fetched from the web
|
||||||
*
|
*
|
||||||
|
@ -114,7 +121,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
* Allows to detect when the screen is focused
|
* Allows to detect when the screen is focused
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.addListener('focus', this.onScreenFocus);
|
navigation.addListener('focus', this.onScreenFocus);
|
||||||
navigation.addListener('blur', this.onScreenBlur);
|
navigation.addListener('blur', this.onScreenBlur);
|
||||||
this.lastRefresh = undefined;
|
this.lastRefresh = undefined;
|
||||||
|
@ -125,7 +132,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
* Refreshes data when focusing the screen and setup a refresh interval if asked to
|
* Refreshes data when focusing the screen and setup a refresh interval if asked to
|
||||||
*/
|
*/
|
||||||
onScreenFocus = () => {
|
onScreenFocus = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
if (props.refreshOnFocus && this.lastRefresh) {
|
if (props.refreshOnFocus && this.lastRefresh) {
|
||||||
setTimeout(this.onRefresh, 200);
|
setTimeout(this.onRefresh, 200);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +180,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
* Refreshes data and shows an animations while doing it
|
* Refreshes data and shows an animations while doing it
|
||||||
*/
|
*/
|
||||||
onRefresh = () => {
|
onRefresh = () => {
|
||||||
const {fetchUrl} = this.props;
|
const { fetchUrl } = this.props;
|
||||||
let canRefresh;
|
let canRefresh;
|
||||||
if (this.lastRefresh != null) {
|
if (this.lastRefresh != null) {
|
||||||
const last = this.lastRefresh;
|
const last = this.lastRefresh;
|
||||||
|
@ -182,7 +189,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
canRefresh = true;
|
canRefresh = true;
|
||||||
}
|
}
|
||||||
if (canRefresh) {
|
if (canRefresh) {
|
||||||
this.setState({refreshing: true});
|
this.setState({ refreshing: true });
|
||||||
readData(fetchUrl).then(this.onFetchSuccess).catch(this.onFetchError);
|
readData(fetchUrl).then(this.onFetchSuccess).catch(this.onFetchError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -191,21 +198,21 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
* Shows the error popup
|
* Shows the error popup
|
||||||
*/
|
*/
|
||||||
showSnackBar = () => {
|
showSnackBar = () => {
|
||||||
this.setState({snackbarVisible: true});
|
this.setState({ snackbarVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the error popup
|
* Hides the error popup
|
||||||
*/
|
*/
|
||||||
hideSnackBar = () => {
|
hideSnackBar = () => {
|
||||||
this.setState({snackbarVisible: false});
|
this.setState({ snackbarVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
getItemLayout = (
|
getItemLayout = (
|
||||||
height: number,
|
height: number,
|
||||||
data: Array<SectionListData<ItemT>> | null,
|
data: Array<SectionListData<ItemT>> | null,
|
||||||
index: number,
|
index: number
|
||||||
): {length: number; offset: number; index: number} => {
|
): { length: number; offset: number; index: number } => {
|
||||||
return {
|
return {
|
||||||
length: height,
|
length: height,
|
||||||
offset: height * index,
|
offset: height * index,
|
||||||
|
@ -213,9 +220,9 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderSectionHeader = (data: {section: SectionListData<ItemT>}) => {
|
getRenderSectionHeader = (data: { section: SectionListData<ItemT> }) => {
|
||||||
const {renderSectionHeader} = this.props;
|
const { renderSectionHeader } = this.props;
|
||||||
const {refreshing} = this.state;
|
const { refreshing } = this.state;
|
||||||
if (renderSectionHeader != null) {
|
if (renderSectionHeader != null) {
|
||||||
return (
|
return (
|
||||||
<Animatable.View animation="fadeInUp" duration={500} useNativeDriver>
|
<Animatable.View animation="fadeInUp" duration={500} useNativeDriver>
|
||||||
|
@ -226,8 +233,8 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderItem = (data: {item: ItemT}) => {
|
getRenderItem = (data: { item: ItemT }) => {
|
||||||
const {renderItem} = this.props;
|
const { renderItem } = this.props;
|
||||||
return (
|
return (
|
||||||
<Animatable.View animation="fadeInUp" duration={500} useNativeDriver>
|
<Animatable.View animation="fadeInUp" duration={500} useNativeDriver>
|
||||||
{renderItem(data)}
|
{renderItem(data)}
|
||||||
|
@ -236,15 +243,15 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
};
|
};
|
||||||
|
|
||||||
onScroll = (event: NativeSyntheticEvent<EventTarget>) => {
|
onScroll = (event: NativeSyntheticEvent<EventTarget>) => {
|
||||||
const {onScroll} = this.props;
|
const { onScroll } = this.props;
|
||||||
if (onScroll != null) {
|
if (onScroll != null) {
|
||||||
onScroll(event);
|
onScroll(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
const {itemHeight} = props;
|
const { itemHeight } = props;
|
||||||
let dataset: SectionListDataType<ItemT> = [];
|
let dataset: SectionListDataType<ItemT> = [];
|
||||||
if (
|
if (
|
||||||
state.fetchedData != null ||
|
state.fetchedData != null ||
|
||||||
|
@ -253,7 +260,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
dataset = props.createDataset(state.fetchedData, state.refreshing);
|
dataset = props.createDataset(state.fetchedData, state.refreshing);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {containerPaddingTop} = props.collapsibleStack;
|
const { containerPaddingTop } = props.collapsibleStack;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<CollapsibleSectionList
|
<CollapsibleSectionList
|
||||||
|
@ -269,7 +276,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
renderSectionHeader={this.getRenderSectionHeader}
|
renderSectionHeader={this.getRenderSectionHeader}
|
||||||
renderItem={this.getRenderItem}
|
renderItem={this.getRenderItem}
|
||||||
stickySectionHeadersEnabled={props.stickyHeader}
|
stickySectionHeadersEnabled={props.stickyHeader}
|
||||||
style={{minHeight: '100%'}}
|
style={styles.container}
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
props.renderListHeaderComponent != null
|
props.renderListHeaderComponent != null
|
||||||
? props.renderListHeaderComponent(state.fetchedData)
|
? props.renderListHeaderComponent(state.fetchedData)
|
||||||
|
@ -304,7 +311,8 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
||||||
duration={4000}
|
duration={4000}
|
||||||
style={{
|
style={{
|
||||||
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{i18n.t('general.listUpdateFail')}
|
{i18n.t('general.listUpdateFail')}
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -31,14 +31,15 @@ import {
|
||||||
Linking,
|
Linking,
|
||||||
NativeScrollEvent,
|
NativeScrollEvent,
|
||||||
NativeSyntheticEvent,
|
NativeSyntheticEvent,
|
||||||
|
StyleSheet,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import {withTheme} from 'react-native-paper';
|
import { withTheme } from 'react-native-paper';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import {Collapsible} from 'react-navigation-collapsible';
|
import { Collapsible } from 'react-navigation-collapsible';
|
||||||
import withCollapsible from '../../utils/withCollapsible';
|
import withCollapsible from '../../utils/withCollapsible';
|
||||||
import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
|
import MaterialHeaderButtons, { Item } from '../Overrides/CustomHeaderButton';
|
||||||
import {ERROR_TYPE} from '../../utils/WebData';
|
import { ERROR_TYPE } from '../../utils/WebData';
|
||||||
import ErrorView from './ErrorView';
|
import ErrorView from './ErrorView';
|
||||||
import BasicLoadingScreen from './BasicLoadingScreen';
|
import BasicLoadingScreen from './BasicLoadingScreen';
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ type PropsType = {
|
||||||
theme: ReactNativePaper.Theme;
|
theme: ReactNativePaper.Theme;
|
||||||
url: string;
|
url: string;
|
||||||
collapsibleStack: Collapsible;
|
collapsibleStack: Collapsible;
|
||||||
onMessage: (event: {nativeEvent: {data: string}}) => void;
|
onMessage: (event: { nativeEvent: { data: string } }) => void;
|
||||||
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
||||||
customJS?: string;
|
customJS?: string;
|
||||||
customPaddingFunction?: null | ((padding: number) => string);
|
customPaddingFunction?: null | ((padding: number) => string);
|
||||||
|
@ -56,6 +57,12 @@ type PropsType = {
|
||||||
|
|
||||||
const AnimatedWebView = Animated.createAnimatedComponent(WebView);
|
const AnimatedWebView = Animated.createAnimatedComponent(WebView);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overflow: {
|
||||||
|
marginHorizontal: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a webview screen.
|
* Class defining a webview screen.
|
||||||
*/
|
*/
|
||||||
|
@ -68,7 +75,7 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
|
|
||||||
currentUrl: string;
|
currentUrl: string;
|
||||||
|
|
||||||
webviewRef: {current: null | WebView};
|
webviewRef: { current: null | WebView };
|
||||||
|
|
||||||
canGoBack: boolean;
|
canGoBack: boolean;
|
||||||
|
|
||||||
|
@ -83,7 +90,7 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
* Creates header buttons and listens to events after mounting
|
* Creates header buttons and listens to events after mounting
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.navigation.setOptions({
|
props.navigation.setOptions({
|
||||||
headerRight: props.showAdvancedControls
|
headerRight: props.showAdvancedControls
|
||||||
? this.getAdvancedButtons
|
? this.getAdvancedButtons
|
||||||
|
@ -92,13 +99,13 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
props.navigation.addListener('focus', () => {
|
props.navigation.addListener('focus', () => {
|
||||||
BackHandler.addEventListener(
|
BackHandler.addEventListener(
|
||||||
'hardwareBackPress',
|
'hardwareBackPress',
|
||||||
this.onBackButtonPressAndroid,
|
this.onBackButtonPressAndroid
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
props.navigation.addListener('blur', () => {
|
props.navigation.addListener('blur', () => {
|
||||||
BackHandler.removeEventListener(
|
BackHandler.removeEventListener(
|
||||||
'hardwareBackPress',
|
'hardwareBackPress',
|
||||||
this.onBackButtonPressAndroid,
|
this.onBackButtonPressAndroid
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -145,7 +152,7 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getAdvancedButtons = () => {
|
getAdvancedButtons = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<MaterialHeaderButtons>
|
<MaterialHeaderButtons>
|
||||||
<Item
|
<Item
|
||||||
|
@ -154,14 +161,15 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
onPress={this.onRefreshClicked}
|
onPress={this.onRefreshClicked}
|
||||||
/>
|
/>
|
||||||
<OverflowMenu
|
<OverflowMenu
|
||||||
style={{marginHorizontal: 10}}
|
style={styles.overflow}
|
||||||
OverflowIcon={
|
OverflowIcon={
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="dots-vertical"
|
name="dots-vertical"
|
||||||
size={26}
|
size={26}
|
||||||
color={props.theme.colors.text}
|
color={props.theme.colors.text}
|
||||||
/>
|
/>
|
||||||
}>
|
}
|
||||||
|
>
|
||||||
<HiddenItem
|
<HiddenItem
|
||||||
title={i18n.t('general.goBack')}
|
title={i18n.t('general.goBack')}
|
||||||
onPress={this.onGoBackClicked}
|
onPress={this.onGoBackClicked}
|
||||||
|
@ -195,7 +203,7 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getJavascriptPadding(padding: number): string {
|
getJavascriptPadding(padding: number): string {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const customPadding =
|
const customPadding =
|
||||||
props.customPaddingFunction != null
|
props.customPaddingFunction != null
|
||||||
? props.customPaddingFunction(padding)
|
? props.customPaddingFunction(padding)
|
||||||
|
@ -229,7 +237,7 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||||
const {onScroll} = this.props;
|
const { onScroll } = this.props;
|
||||||
if (onScroll) {
|
if (onScroll) {
|
||||||
onScroll(event);
|
onScroll(event);
|
||||||
}
|
}
|
||||||
|
@ -247,12 +255,15 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {containerPaddingTop, onScrollWithListener} = props.collapsibleStack;
|
const {
|
||||||
|
containerPaddingTop,
|
||||||
|
onScrollWithListener,
|
||||||
|
} = props.collapsibleStack;
|
||||||
return (
|
return (
|
||||||
<AnimatedWebView
|
<AnimatedWebView
|
||||||
ref={this.webviewRef}
|
ref={this.webviewRef}
|
||||||
source={{uri: props.url}}
|
source={{ uri: props.url }}
|
||||||
startInLoadingState
|
startInLoadingState
|
||||||
injectedJavaScript={props.customJS}
|
injectedJavaScript={props.customJS}
|
||||||
javaScriptEnabled
|
javaScriptEnabled
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Animated} from 'react-native';
|
import { Animated, StyleSheet } from 'react-native';
|
||||||
import {withTheme} from 'react-native-paper';
|
import { withTheme } from 'react-native-paper';
|
||||||
import {Collapsible} from 'react-navigation-collapsible';
|
import { Collapsible } from 'react-navigation-collapsible';
|
||||||
import TabIcon from './TabIcon';
|
import TabIcon from './TabIcon';
|
||||||
import TabHomeIcon from './TabHomeIcon';
|
import TabHomeIcon from './TabHomeIcon';
|
||||||
import {BottomTabBarProps} from '@react-navigation/bottom-tabs';
|
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
||||||
import {NavigationState} from '@react-navigation/native';
|
import { NavigationState } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
PartialState,
|
PartialState,
|
||||||
Route,
|
Route,
|
||||||
|
@ -51,6 +51,16 @@ const TAB_ICONS = {
|
||||||
planex: 'clock',
|
planex: 'clock',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
width: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class CustomTabBar extends React.Component<PropsType, StateType> {
|
class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
static TAB_BAR_HEIGHT = 48;
|
static TAB_BAR_HEIGHT = 48;
|
||||||
|
|
||||||
|
@ -71,7 +81,7 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
* @param destIndex The destination route index
|
* @param destIndex The destination route index
|
||||||
*/
|
*/
|
||||||
onItemPress(route: RouteType, currentIndex: number, destIndex: number) {
|
onItemPress(route: RouteType, currentIndex: number, destIndex: number) {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
if (currentIndex !== destIndex) {
|
if (currentIndex !== destIndex) {
|
||||||
navigation.navigate(route.name);
|
navigation.navigate(route.name);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +93,7 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
* @param route
|
* @param route
|
||||||
*/
|
*/
|
||||||
onItemLongPress(route: RouteType) {
|
onItemLongPress(route: RouteType) {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
if (route.name === 'home') {
|
if (route.name === 'home') {
|
||||||
navigation.navigate('game-start');
|
navigation.navigate('game-start');
|
||||||
}
|
}
|
||||||
|
@ -93,7 +103,7 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
* Finds the active route and syncs the tab bar animation with the header bar
|
* Finds the active route and syncs the tab bar animation with the header bar
|
||||||
*/
|
*/
|
||||||
onRouteChange = () => {
|
onRouteChange = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.state.routes.map(this.syncTabBar);
|
props.state.routes.map(this.syncTabBar);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,9 +132,9 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getRenderIcon = (route: RouteType, index: number) => {
|
getRenderIcon = (route: RouteType, index: number) => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const {state} = props;
|
const { state } = props;
|
||||||
const {options} = props.descriptors[route.key];
|
const { options } = props.descriptors[route.key];
|
||||||
let label;
|
let label;
|
||||||
if (options.tabBarLabel != null) {
|
if (options.tabBarLabel != null) {
|
||||||
label = options.tabBarLabel;
|
label = options.tabBarLabel;
|
||||||
|
@ -171,12 +181,12 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getIcons() {
|
getIcons() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return props.state.routes.map(this.getRenderIcon);
|
return props.state.routes.map(this.getRenderIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
syncTabBar = (route: RouteType, index: number) => {
|
syncTabBar = (route: RouteType, index: number) => {
|
||||||
const {state} = this.props;
|
const { state } = this.props;
|
||||||
const isFocused = state.index === index;
|
const isFocused = state.index === index;
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
const stackState = route.state;
|
const stackState = route.state;
|
||||||
|
@ -184,8 +194,8 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
stackState && stackState.index != null
|
stackState && stackState.index != null
|
||||||
? stackState.routes[stackState.index]
|
? stackState.routes[stackState.index]
|
||||||
: null;
|
: null;
|
||||||
const params: {collapsible: Collapsible} | null | undefined = stackRoute
|
const params: { collapsible: Collapsible } | null | undefined = stackRoute
|
||||||
? (stackRoute.params as {collapsible: Collapsible})
|
? (stackRoute.params as { collapsible: Collapsible })
|
||||||
: null;
|
: null;
|
||||||
const collapsible = params != null ? params.collapsible : null;
|
const collapsible = params != null ? params.collapsible : null;
|
||||||
if (collapsible != null) {
|
if (collapsible != null) {
|
||||||
|
@ -197,20 +207,17 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
const icons = this.getIcons();
|
const icons = this.getIcons();
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
|
||||||
height: CustomTabBar.TAB_BAR_HEIGHT,
|
height: CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
width: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
backgroundColor: props.theme.colors.surface,
|
backgroundColor: props.theme.colors.surface,
|
||||||
transform: [{translateY: state.translateY}],
|
transform: [{ translateY: state.translateY }],
|
||||||
}}>
|
...styles.container,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{icons}
|
{icons}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, View} from 'react-native';
|
import { Image, StyleSheet, View } from 'react-native';
|
||||||
import {FAB} from 'react-native-paper';
|
import { FAB } from 'react-native-paper';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
const FOCUSED_ICON = require('../../../assets/tab-icon.png');
|
const FOCUSED_ICON = require('../../../assets/tab-icon.png');
|
||||||
const UNFOCUSED_ICON = require('../../../assets/tab-icon-outline.png');
|
const UNFOCUSED_ICON = require('../../../assets/tab-icon-outline.png');
|
||||||
|
@ -33,6 +33,25 @@ type PropsType = {
|
||||||
|
|
||||||
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
subcontainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: -15,
|
||||||
|
},
|
||||||
|
fab: {
|
||||||
|
marginTop: 15,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction layer for Agenda component, using custom configuration
|
* Abstraction layer for Agenda component, using custom configuration
|
||||||
*/
|
*/
|
||||||
|
@ -70,12 +89,12 @@ class TabHomeIcon extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {focused} = this.props;
|
const { focused } = this.props;
|
||||||
return nextProps.focused !== focused;
|
return nextProps.focused !== focused;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIconRender = ({size, color}: {size: number; color: string}) => {
|
getIconRender = ({ size, color }: { size: number; color: string }) => {
|
||||||
const {focused} = this.props;
|
const { focused } = this.props;
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
source={focused ? FOCUSED_ICON : UNFOCUSED_ICON}
|
source={focused ? FOCUSED_ICON : UNFOCUSED_ICON}
|
||||||
|
@ -89,22 +108,15 @@ class TabHomeIcon extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={styles.container}>
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: props.tabBarHeight + 30,
|
height: props.tabBarHeight + 30,
|
||||||
marginBottom: -15,
|
...styles.subcontainer,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<AnimatedFAB
|
<AnimatedFAB
|
||||||
duration={200}
|
duration={200}
|
||||||
easing="ease-out"
|
easing="ease-out"
|
||||||
|
@ -112,11 +124,7 @@ class TabHomeIcon extends React.Component<PropsType> {
|
||||||
icon={this.getIconRender}
|
icon={this.getIconRender}
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
onLongPress={props.onLongPress}
|
onLongPress={props.onLongPress}
|
||||||
style={{
|
style={styles.fab}
|
||||||
marginTop: 15,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,10 +18,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {TouchableRipple, withTheme} from 'react-native-paper';
|
import { TouchableRipple, withTheme } from 'react-native-paper';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
focused: boolean;
|
focused: boolean;
|
||||||
|
@ -34,6 +35,19 @@ type PropsType = {
|
||||||
extraData: null | boolean | number | string;
|
extraData: null | boolean | number | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderRadius: 10,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
fontSize: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction layer for Agenda component, using custom configuration
|
* Abstraction layer for Agenda component, using custom configuration
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +92,7 @@ class TabIcon extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
nextProps.focused !== props.focused ||
|
nextProps.focused !== props.focused ||
|
||||||
nextProps.theme.dark !== props.theme.dark ||
|
nextProps.theme.dark !== props.theme.dark ||
|
||||||
|
@ -87,32 +101,27 @@ class TabIcon extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
onLongPress={props.onLongPress}
|
onLongPress={props.onLongPress}
|
||||||
rippleColor={props.theme.colors.primary}
|
rippleColor={props.theme.colors.primary}
|
||||||
borderless
|
borderless={true}
|
||||||
style={{
|
style={styles.container}
|
||||||
flex: 1,
|
>
|
||||||
justifyContent: 'center',
|
|
||||||
borderRadius: 10,
|
|
||||||
}}>
|
|
||||||
<View>
|
<View>
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
duration={200}
|
duration={200}
|
||||||
easing="ease-out"
|
easing="ease-out"
|
||||||
animation={props.focused ? 'focusIn' : 'focusOut'}
|
animation={props.focused ? 'focusIn' : 'focusOut'}
|
||||||
useNativeDriver>
|
useNativeDriver
|
||||||
|
>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={props.icon}
|
name={props.icon}
|
||||||
color={props.color}
|
color={props.color}
|
||||||
size={26}
|
size={26}
|
||||||
style={{
|
style={GENERAL_STYLES.centerHorizontal}
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
<Animatable.Text
|
<Animatable.Text
|
||||||
|
@ -120,10 +129,9 @@ class TabIcon extends React.Component<PropsType> {
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
style={{
|
style={{
|
||||||
color: props.color,
|
color: props.color,
|
||||||
marginLeft: 'auto',
|
...styles.text,
|
||||||
marginRight: 'auto',
|
}}
|
||||||
fontSize: 10,
|
>
|
||||||
}}>
|
|
||||||
{props.label}
|
{props.label}
|
||||||
</Animatable.Text>
|
</Animatable.Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @flow
|
|
||||||
|
|
||||||
const ICON_AMICALE = require('../../assets/amicale.png');
|
const ICON_AMICALE = require('../../assets/amicale.png');
|
||||||
const ICON_CAMPUS = require('../../assets/android.icon.png');
|
const ICON_CAMPUS = require('../../assets/android.icon.png');
|
||||||
|
|
||||||
|
|
23
src/constants/Styles.tsx
Normal file
23
src/constants/Styles.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
const GENERAL_STYLES = StyleSheet.create({
|
||||||
|
centerHorizontal: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
centerVertical: {
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
center: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
flex: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default GENERAL_STYLES;
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import type {IntroSlideType} from '../components/Overrides/CustomIntroSlider';
|
import type { IntroSlideType } from '../components/Overrides/CustomIntroSlider';
|
||||||
import MascotIntroWelcome from '../components/Intro/MascotIntroWelcome';
|
import MascotIntroWelcome from '../components/Intro/MascotIntroWelcome';
|
||||||
import IntroIcon from '../components/Intro/IconIntro';
|
import IntroIcon from '../components/Intro/IconIntro';
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {ProxiwashMachineType} from '../screens/Proxiwash/ProxiwashScreen';
|
import type { ProxiwashMachineType } from '../screens/Proxiwash/ProxiwashScreen';
|
||||||
import type {RuFoodCategoryType} from '../screens/Services/SelfMenuScreen';
|
import type { RuFoodCategoryType } from '../screens/Services/SelfMenuScreen';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton class used to manage april fools
|
* Singleton class used to manage april fools
|
||||||
|
@ -67,13 +67,13 @@ export default class AprilFoolsManager {
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
static getFakeMenuItem(
|
static getFakeMenuItem(
|
||||||
menu: Array<RuFoodCategoryType>,
|
menu: Array<RuFoodCategoryType>
|
||||||
): Array<RuFoodCategoryType> {
|
): Array<RuFoodCategoryType> {
|
||||||
menu[1].dishes.splice(4, 0, {name: 'Coq au vin'});
|
menu[1].dishes.splice(4, 0, { name: 'Coq au vin' });
|
||||||
menu[1].dishes.splice(2, 0, {name: "Bat'Soupe"});
|
menu[1].dishes.splice(2, 0, { name: "Bat'Soupe" });
|
||||||
menu[1].dishes.splice(1, 0, {name: 'Pave de loup'});
|
menu[1].dishes.splice(1, 0, { name: 'Pave de loup' });
|
||||||
menu[1].dishes.splice(0, 0, {name: 'Béranger à point'});
|
menu[1].dishes.splice(0, 0, { name: 'Béranger à point' });
|
||||||
menu[1].dishes.splice(0, 0, {name: "Pieds d'Arnaud"});
|
menu[1].dishes.splice(0, 0, { name: "Pieds d'Arnaud" });
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ export default class AprilFoolsManager {
|
||||||
* @param dryers
|
* @param dryers
|
||||||
*/
|
*/
|
||||||
static getNewProxiwashDryerOrderedList(
|
static getNewProxiwashDryerOrderedList(
|
||||||
dryers: Array<ProxiwashMachineType> | null,
|
dryers: Array<ProxiwashMachineType> | null
|
||||||
) {
|
) {
|
||||||
if (dryers != null) {
|
if (dryers != null) {
|
||||||
const second = dryers[1];
|
const second = dryers[1];
|
||||||
|
@ -98,7 +98,7 @@ export default class AprilFoolsManager {
|
||||||
* @param washers
|
* @param washers
|
||||||
*/
|
*/
|
||||||
static getNewProxiwashWasherOrderedList(
|
static getNewProxiwashWasherOrderedList(
|
||||||
washers: Array<ProxiwashMachineType> | null,
|
washers: Array<ProxiwashMachineType> | null
|
||||||
) {
|
) {
|
||||||
if (washers != null) {
|
if (washers != null) {
|
||||||
const first = washers[0];
|
const first = washers[0];
|
||||||
|
@ -129,7 +129,7 @@ export default class AprilFoolsManager {
|
||||||
* @returns {{colors: {textDisabled: string, agendaDayTextColor: string, surface: string, background: string, dividerBackground: string, accent: string, agendaBackgroundColor: string, tabIcon: string, card: string, primary: string}}}
|
* @returns {{colors: {textDisabled: string, agendaDayTextColor: string, surface: string, background: string, dividerBackground: string, accent: string, agendaBackgroundColor: string, tabIcon: string, card: string, primary: string}}}
|
||||||
*/
|
*/
|
||||||
static getAprilFoolsTheme(
|
static getAprilFoolsTheme(
|
||||||
currentTheme: ReactNativePaper.Theme,
|
currentTheme: ReactNativePaper.Theme
|
||||||
): ReactNativePaper.Theme {
|
): ReactNativePaper.Theme {
|
||||||
return {
|
return {
|
||||||
...currentTheme,
|
...currentTheme,
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import {SERVICES_KEY} from './ServicesManager';
|
import { SERVICES_KEY } from './ServicesManager';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton used to manage preferences.
|
* Singleton used to manage preferences.
|
||||||
|
@ -29,7 +29,7 @@ import {SERVICES_KEY} from './ServicesManager';
|
||||||
export default class AsyncStorageManager {
|
export default class AsyncStorageManager {
|
||||||
static instance: AsyncStorageManager | null = null;
|
static instance: AsyncStorageManager | null = null;
|
||||||
|
|
||||||
static PREFERENCES: {[key: string]: {key: string; default: string}} = {
|
static PREFERENCES: { [key: string]: { key: string; default: string } } = {
|
||||||
debugUnlocked: {
|
debugUnlocked: {
|
||||||
key: 'debugUnlocked',
|
key: 'debugUnlocked',
|
||||||
default: '0',
|
default: '0',
|
||||||
|
@ -130,7 +130,7 @@ export default class AsyncStorageManager {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
private currentPreferences: {[key: string]: string};
|
private currentPreferences: { [key: string]: string };
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.currentPreferences = {};
|
this.currentPreferences = {};
|
||||||
|
@ -155,7 +155,7 @@ export default class AsyncStorageManager {
|
||||||
*/
|
*/
|
||||||
static set(
|
static set(
|
||||||
key: string,
|
key: string,
|
||||||
value: number | string | boolean | object | Array<any>,
|
value: number | string | boolean | object | Array<any>
|
||||||
) {
|
) {
|
||||||
AsyncStorageManager.getInstance().setPreference(key, value);
|
AsyncStorageManager.getInstance().setPreference(key, value);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ export default class AsyncStorageManager {
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async loadPreferences() {
|
async loadPreferences() {
|
||||||
return new Promise((resolve: () => void) => {
|
return new Promise((resolve: (val: void) => void) => {
|
||||||
const prefKeys: Array<string> = [];
|
const prefKeys: Array<string> = [];
|
||||||
// Get all available keys
|
// Get all available keys
|
||||||
Object.keys(AsyncStorageManager.PREFERENCES).forEach((key: string) => {
|
Object.keys(AsyncStorageManager.PREFERENCES).forEach((key: string) => {
|
||||||
|
@ -240,7 +240,7 @@ export default class AsyncStorageManager {
|
||||||
*/
|
*/
|
||||||
setPreference(
|
setPreference(
|
||||||
key: string,
|
key: string,
|
||||||
value: number | string | boolean | object | Array<any>,
|
value: number | string | boolean | object | Array<any>
|
||||||
) {
|
) {
|
||||||
if (AsyncStorageManager.PREFERENCES[key] != null) {
|
if (AsyncStorageManager.PREFERENCES[key] != null) {
|
||||||
let convertedValue;
|
let convertedValue;
|
||||||
|
|
|
@ -17,11 +17,9 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as Keychain from 'react-native-keychain';
|
import * as Keychain from 'react-native-keychain';
|
||||||
import type {ApiDataLoginType} from '../utils/WebData';
|
import type { ApiDataLoginType } from '../utils/WebData';
|
||||||
import {apiRequest, ERROR_TYPE} from '../utils/WebData';
|
import { apiRequest, ERROR_TYPE } from '../utils/WebData';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* champ: error
|
* champ: error
|
||||||
|
@ -84,7 +82,7 @@ export default class ConnectionManager {
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch(resolve);
|
.catch(() => resolve());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -159,7 +157,7 @@ export default class ConnectionManager {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: number): void => reject(error));
|
.catch((error: number): void => reject(error));
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +170,7 @@ export default class ConnectionManager {
|
||||||
*/
|
*/
|
||||||
async authenticatedRequest<T>(
|
async authenticatedRequest<T>(
|
||||||
path: string,
|
path: string,
|
||||||
params: {[key: string]: any},
|
params: { [key: string]: any }
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
(resolve: (response: T) => void, reject: (error: number) => void) => {
|
(resolve: (response: T) => void, reject: (error: number) => void) => {
|
||||||
|
@ -187,7 +185,7 @@ export default class ConnectionManager {
|
||||||
} else {
|
} else {
|
||||||
reject(ERROR_TYPE.TOKEN_RETRIEVE);
|
reject(ERROR_TYPE.TOKEN_RETRIEVE);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,15 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @flow
|
import type { ServiceItemType } from './ServicesManager';
|
||||||
|
|
||||||
import type {ServiceItemType} from './ServicesManager';
|
|
||||||
import ServicesManager from './ServicesManager';
|
import ServicesManager from './ServicesManager';
|
||||||
import {getSublistWithIds} from '../utils/Services';
|
import { getSublistWithIds } from '../utils/Services';
|
||||||
import AsyncStorageManager from './AsyncStorageManager';
|
import AsyncStorageManager from './AsyncStorageManager';
|
||||||
|
|
||||||
export default class DashboardManager extends ServicesManager {
|
export default class DashboardManager extends ServicesManager {
|
||||||
getCurrentDashboard(): Array<ServiceItemType | null> {
|
getCurrentDashboard(): Array<ServiceItemType | null> {
|
||||||
const dashboardIdList = AsyncStorageManager.getObject<Array<string>>(
|
const dashboardIdList = AsyncStorageManager.getObject<Array<string>>(
|
||||||
AsyncStorageManager.PREFERENCES.dashboardItems.key,
|
AsyncStorageManager.PREFERENCES.dashboardItems.key
|
||||||
);
|
);
|
||||||
const allDatasets = [
|
const allDatasets = [
|
||||||
...this.amicaleDataset,
|
...this.amicaleDataset,
|
||||||
|
|
|
@ -84,7 +84,7 @@ export default class DateManager {
|
||||||
date.setFullYear(
|
date.setFullYear(
|
||||||
parseInt(dateArray[0], 10),
|
parseInt(dateArray[0], 10),
|
||||||
parseInt(dateArray[1], 10) - 1,
|
parseInt(dateArray[1], 10) - 1,
|
||||||
parseInt(dateArray[2], 10),
|
parseInt(dateArray[2], 10)
|
||||||
);
|
);
|
||||||
return `${this.daysOfWeek[date.getDay()]} ${date.getDate()} ${
|
return `${this.daysOfWeek[date.getDay()]} ${date.getDate()} ${
|
||||||
this.monthsOfYear[date.getMonth()]
|
this.monthsOfYear[date.getMonth()]
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import AvailableWebsites from '../constants/AvailableWebsites';
|
import AvailableWebsites from '../constants/AvailableWebsites';
|
||||||
import ConnectionManager from './ConnectionManager';
|
import ConnectionManager from './ConnectionManager';
|
||||||
import type {FullDashboardType} from '../screens/Home/HomeScreen';
|
import type { FullDashboardType } from '../screens/Home/HomeScreen';
|
||||||
import getStrippedServicesList from '../utils/Services';
|
import getStrippedServicesList from '../utils/Services';
|
||||||
|
|
||||||
// AMICALE
|
// AMICALE
|
||||||
|
@ -337,7 +337,7 @@ export default class ServicesManager {
|
||||||
if (ConnectionManager.getInstance().isLoggedIn()) {
|
if (ConnectionManager.getInstance().isLoggedIn()) {
|
||||||
this.navigation.navigate(route);
|
this.navigation.navigate(route);
|
||||||
} else {
|
} else {
|
||||||
this.navigation.navigate('login', {nextScreen: route});
|
this.navigation.navigate('login', { nextScreen: route });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DarkTheme, DefaultTheme} from 'react-native-paper';
|
import { DarkTheme, DefaultTheme } from 'react-native-paper';
|
||||||
import {Appearance} from 'react-native-appearance';
|
import { Appearance } from 'react-native-appearance';
|
||||||
import AsyncStorageManager from './AsyncStorageManager';
|
import AsyncStorageManager from './AsyncStorageManager';
|
||||||
import AprilFoolsManager from './AprilFoolsManager';
|
import AprilFoolsManager from './AprilFoolsManager';
|
||||||
|
|
||||||
|
@ -235,14 +235,14 @@ export default class ThemeManager {
|
||||||
static getNightMode(): boolean {
|
static getNightMode(): boolean {
|
||||||
return (
|
return (
|
||||||
(AsyncStorageManager.getBool(
|
(AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.nightMode.key,
|
AsyncStorageManager.PREFERENCES.nightMode.key
|
||||||
) &&
|
) &&
|
||||||
(!AsyncStorageManager.getBool(
|
(!AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
|
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
|
||||||
) ||
|
) ||
|
||||||
colorScheme === 'no-preference')) ||
|
colorScheme === 'no-preference')) ||
|
||||||
(AsyncStorageManager.getBool(
|
(AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
|
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
|
||||||
) &&
|
) &&
|
||||||
colorScheme === 'dark')
|
colorScheme === 'dark')
|
||||||
);
|
);
|
||||||
|
@ -289,7 +289,7 @@ export default class ThemeManager {
|
||||||
setNightMode(isNightMode: boolean) {
|
setNightMode(isNightMode: boolean) {
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.nightMode.key,
|
AsyncStorageManager.PREFERENCES.nightMode.key,
|
||||||
isNightMode,
|
isNightMode
|
||||||
);
|
);
|
||||||
if (this.updateThemeCallback != null) {
|
if (this.updateThemeCallback != null) {
|
||||||
this.updateThemeCallback();
|
this.updateThemeCallback();
|
||||||
|
|
|
@ -18,9 +18,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
|
import {
|
||||||
|
createStackNavigator,
|
||||||
|
TransitionPresets,
|
||||||
|
} from '@react-navigation/stack';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Platform} from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import SettingsScreen from '../screens/Other/Settings/SettingsScreen';
|
import SettingsScreen from '../screens/Other/Settings/SettingsScreen';
|
||||||
import AboutScreen from '../screens/About/AboutScreen';
|
import AboutScreen from '../screens/About/AboutScreen';
|
||||||
import AboutDependenciesScreen from '../screens/About/AboutDependenciesScreen';
|
import AboutDependenciesScreen from '../screens/About/AboutDependenciesScreen';
|
||||||
|
@ -78,16 +81,16 @@ export enum MainRoutes {
|
||||||
Feedback = 'feedback',
|
Feedback = 'feedback',
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultParams = {[key in MainRoutes]: object | undefined};
|
type DefaultParams = { [key in MainRoutes]: object | undefined };
|
||||||
|
|
||||||
export interface FullParamsList extends DefaultParams {
|
export interface FullParamsList extends DefaultParams {
|
||||||
login: {nextScreen: string};
|
'login': { nextScreen: string };
|
||||||
'equipment-confirm': {
|
'equipment-confirm': {
|
||||||
item?: DeviceType;
|
item?: DeviceType;
|
||||||
dates: [string, string];
|
dates: [string, string];
|
||||||
};
|
};
|
||||||
'equipment-rent': {item?: DeviceType};
|
'equipment-rent': { item?: DeviceType };
|
||||||
gallery: {images: Array<{url: string}>};
|
'gallery': { images: Array<{ url: string }> };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't know why but TS is complaining without this
|
// Don't know why but TS is complaining without this
|
||||||
|
@ -108,13 +111,14 @@ const defaultScreenOptions = {
|
||||||
|
|
||||||
const MainStack = createStackNavigator<MainStackParamsList>();
|
const MainStack = createStackNavigator<MainStackParamsList>();
|
||||||
|
|
||||||
function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
function MainStackComponent(props: { createTabNavigator: () => JSX.Element }) {
|
||||||
const {createTabNavigator} = props;
|
const { createTabNavigator } = props;
|
||||||
return (
|
return (
|
||||||
<MainStack.Navigator
|
<MainStack.Navigator
|
||||||
initialRouteName={MainRoutes.Main}
|
initialRouteName={MainRoutes.Main}
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
<MainStack.Screen
|
<MainStack.Screen
|
||||||
name={MainRoutes.Main}
|
name={MainRoutes.Main}
|
||||||
component={createTabNavigator}
|
component={createTabNavigator}
|
||||||
|
@ -135,31 +139,31 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
MainRoutes.Settings,
|
MainRoutes.Settings,
|
||||||
MainStack,
|
MainStack,
|
||||||
SettingsScreen,
|
SettingsScreen,
|
||||||
i18n.t('screens.settings.title'),
|
i18n.t('screens.settings.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.DashboardEdit,
|
MainRoutes.DashboardEdit,
|
||||||
MainStack,
|
MainStack,
|
||||||
DashboardEditScreen,
|
DashboardEditScreen,
|
||||||
i18n.t('screens.settings.dashboardEdit.title'),
|
i18n.t('screens.settings.dashboardEdit.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.About,
|
MainRoutes.About,
|
||||||
MainStack,
|
MainStack,
|
||||||
AboutScreen,
|
AboutScreen,
|
||||||
i18n.t('screens.about.title'),
|
i18n.t('screens.about.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Dependencies,
|
MainRoutes.Dependencies,
|
||||||
MainStack,
|
MainStack,
|
||||||
AboutDependenciesScreen,
|
AboutDependenciesScreen,
|
||||||
i18n.t('screens.about.libs'),
|
i18n.t('screens.about.libs')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Debug,
|
MainRoutes.Debug,
|
||||||
MainStack,
|
MainStack,
|
||||||
DebugScreen,
|
DebugScreen,
|
||||||
i18n.t('screens.about.debug'),
|
i18n.t('screens.about.debug')
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
|
@ -169,7 +173,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
i18n.t('screens.game.title'),
|
i18n.t('screens.game.title'),
|
||||||
true,
|
true,
|
||||||
undefined,
|
undefined,
|
||||||
'transparent',
|
'transparent'
|
||||||
)}
|
)}
|
||||||
<MainStack.Screen
|
<MainStack.Screen
|
||||||
name={MainRoutes.GameMain}
|
name={MainRoutes.GameMain}
|
||||||
|
@ -184,8 +188,8 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
LoginScreen,
|
LoginScreen,
|
||||||
i18n.t('screens.login.title'),
|
i18n.t('screens.login.title'),
|
||||||
true,
|
true,
|
||||||
{headerTintColor: '#fff'},
|
{ headerTintColor: '#fff' },
|
||||||
'transparent',
|
'transparent'
|
||||||
)}
|
)}
|
||||||
{getWebsiteStack('website', MainStack, WebsiteScreen, '')}
|
{getWebsiteStack('website', MainStack, WebsiteScreen, '')}
|
||||||
|
|
||||||
|
@ -193,19 +197,19 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
MainRoutes.SelfMenu,
|
MainRoutes.SelfMenu,
|
||||||
MainStack,
|
MainStack,
|
||||||
SelfMenuScreen,
|
SelfMenuScreen,
|
||||||
i18n.t('screens.menu.title'),
|
i18n.t('screens.menu.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Proximo,
|
MainRoutes.Proximo,
|
||||||
MainStack,
|
MainStack,
|
||||||
ProximoMainScreen,
|
ProximoMainScreen,
|
||||||
i18n.t('screens.proximo.title'),
|
i18n.t('screens.proximo.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.ProximoList,
|
MainRoutes.ProximoList,
|
||||||
MainStack,
|
MainStack,
|
||||||
ProximoListScreen,
|
ProximoListScreen,
|
||||||
i18n.t('screens.proximo.articleList'),
|
i18n.t('screens.proximo.articleList')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.ProximoAbout,
|
MainRoutes.ProximoAbout,
|
||||||
|
@ -213,20 +217,20 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
ProximoAboutScreen,
|
ProximoAboutScreen,
|
||||||
i18n.t('screens.proximo.title'),
|
i18n.t('screens.proximo.title'),
|
||||||
true,
|
true,
|
||||||
{...modalTransition},
|
{ ...modalTransition }
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Profile,
|
MainRoutes.Profile,
|
||||||
MainStack,
|
MainStack,
|
||||||
ProfileScreen,
|
ProfileScreen,
|
||||||
i18n.t('screens.profile.title'),
|
i18n.t('screens.profile.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.ClubList,
|
MainRoutes.ClubList,
|
||||||
MainStack,
|
MainStack,
|
||||||
ClubListScreen,
|
ClubListScreen,
|
||||||
i18n.t('screens.clubs.title'),
|
i18n.t('screens.clubs.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.ClubInformation,
|
MainRoutes.ClubInformation,
|
||||||
|
@ -234,7 +238,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
ClubDisplayScreen,
|
ClubDisplayScreen,
|
||||||
i18n.t('screens.clubs.details'),
|
i18n.t('screens.clubs.details'),
|
||||||
true,
|
true,
|
||||||
{...modalTransition},
|
{ ...modalTransition }
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.ClubAbout,
|
MainRoutes.ClubAbout,
|
||||||
|
@ -242,37 +246,37 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
ClubAboutScreen,
|
ClubAboutScreen,
|
||||||
i18n.t('screens.clubs.title'),
|
i18n.t('screens.clubs.title'),
|
||||||
true,
|
true,
|
||||||
{...modalTransition},
|
{ ...modalTransition }
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.EquipmentList,
|
MainRoutes.EquipmentList,
|
||||||
MainStack,
|
MainStack,
|
||||||
EquipmentScreen,
|
EquipmentScreen,
|
||||||
i18n.t('screens.equipment.title'),
|
i18n.t('screens.equipment.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.EquipmentRent,
|
MainRoutes.EquipmentRent,
|
||||||
MainStack,
|
MainStack,
|
||||||
EquipmentLendScreen,
|
EquipmentLendScreen,
|
||||||
i18n.t('screens.equipment.book'),
|
i18n.t('screens.equipment.book')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.EquipmentConfirm,
|
MainRoutes.EquipmentConfirm,
|
||||||
MainStack,
|
MainStack,
|
||||||
EquipmentConfirmScreen,
|
EquipmentConfirmScreen,
|
||||||
i18n.t('screens.equipment.confirm'),
|
i18n.t('screens.equipment.confirm')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Vote,
|
MainRoutes.Vote,
|
||||||
MainStack,
|
MainStack,
|
||||||
VoteScreen,
|
VoteScreen,
|
||||||
i18n.t('screens.vote.title'),
|
i18n.t('screens.vote.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
MainRoutes.Feedback,
|
MainRoutes.Feedback,
|
||||||
MainStack,
|
MainStack,
|
||||||
BugReportScreen,
|
BugReportScreen,
|
||||||
i18n.t('screens.feedback.title'),
|
i18n.t('screens.feedback.title')
|
||||||
)}
|
)}
|
||||||
</MainStack.Navigator>
|
</MainStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -280,7 +284,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
defaultHomeRoute: string | null;
|
defaultHomeRoute: string | null;
|
||||||
defaultHomeData: {[key: string]: string};
|
defaultHomeData: { [key: string]: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function MainNavigator(props: PropsType) {
|
export default function MainNavigator(props: PropsType) {
|
||||||
|
|
|
@ -18,14 +18,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
|
import {
|
||||||
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
createStackNavigator,
|
||||||
|
TransitionPresets,
|
||||||
|
} from '@react-navigation/stack';
|
||||||
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
|
|
||||||
import {Title, useTheme} from 'react-native-paper';
|
import { Title, useTheme } from 'react-native-paper';
|
||||||
import {Platform} from 'react-native';
|
import { Platform, StyleSheet } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {createCollapsibleStack} from 'react-navigation-collapsible';
|
import { createCollapsibleStack } from 'react-navigation-collapsible';
|
||||||
import {View} from 'react-native-animatable';
|
import { View } from 'react-native-animatable';
|
||||||
import HomeScreen from '../screens/Home/HomeScreen';
|
import HomeScreen from '../screens/Home/HomeScreen';
|
||||||
import PlanningScreen from '../screens/Planning/PlanningScreen';
|
import PlanningScreen from '../screens/Planning/PlanningScreen';
|
||||||
import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
|
import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
|
||||||
|
@ -45,7 +48,7 @@ import {
|
||||||
CreateScreenCollapsibleStack,
|
CreateScreenCollapsibleStack,
|
||||||
getWebsiteStack,
|
getWebsiteStack,
|
||||||
} from '../utils/CollapsibleUtils';
|
} from '../utils/CollapsibleUtils';
|
||||||
import Mascot, {MASCOT_STYLE} from '../components/Mascot/Mascot';
|
import Mascot, { MASCOT_STYLE } from '../components/Mascot/Mascot';
|
||||||
|
|
||||||
const modalTransition =
|
const modalTransition =
|
||||||
Platform.OS === 'ios'
|
Platform.OS === 'ios'
|
||||||
|
@ -58,6 +61,20 @@ const defaultScreenOptions = {
|
||||||
...modalTransition,
|
...modalTransition,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
mascot: {
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginLeft: 10,
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const ServicesStack = createStackNavigator();
|
const ServicesStack = createStackNavigator();
|
||||||
|
|
||||||
function ServicesStackComponent() {
|
function ServicesStackComponent() {
|
||||||
|
@ -65,24 +82,25 @@ function ServicesStackComponent() {
|
||||||
<ServicesStack.Navigator
|
<ServicesStack.Navigator
|
||||||
initialRouteName="index"
|
initialRouteName="index"
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'index',
|
'index',
|
||||||
ServicesStack,
|
ServicesStack,
|
||||||
WebsitesHomeScreen,
|
WebsitesHomeScreen,
|
||||||
i18n.t('screens.services.title'),
|
i18n.t('screens.services.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'services-section',
|
'services-section',
|
||||||
ServicesStack,
|
ServicesStack,
|
||||||
ServicesSectionScreen,
|
ServicesSectionScreen,
|
||||||
'SECTION',
|
'SECTION'
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'amicale-contact',
|
'amicale-contact',
|
||||||
ServicesStack,
|
ServicesStack,
|
||||||
AmicaleContactScreen,
|
AmicaleContactScreen,
|
||||||
i18n.t('screens.amicaleAbout.title'),
|
i18n.t('screens.amicaleAbout.title')
|
||||||
)}
|
)}
|
||||||
</ServicesStack.Navigator>
|
</ServicesStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -95,18 +113,19 @@ function ProxiwashStackComponent() {
|
||||||
<ProxiwashStack.Navigator
|
<ProxiwashStack.Navigator
|
||||||
initialRouteName="index"
|
initialRouteName="index"
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'index',
|
'index',
|
||||||
ProxiwashStack,
|
ProxiwashStack,
|
||||||
ProxiwashScreen,
|
ProxiwashScreen,
|
||||||
i18n.t('screens.proxiwash.title'),
|
i18n.t('screens.proxiwash.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'proxiwash-about',
|
'proxiwash-about',
|
||||||
ProxiwashStack,
|
ProxiwashStack,
|
||||||
ProxiwashAboutScreen,
|
ProxiwashAboutScreen,
|
||||||
i18n.t('screens.proxiwash.title'),
|
i18n.t('screens.proxiwash.title')
|
||||||
)}
|
)}
|
||||||
</ProxiwashStack.Navigator>
|
</ProxiwashStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -119,17 +138,18 @@ function PlanningStackComponent() {
|
||||||
<PlanningStack.Navigator
|
<PlanningStack.Navigator
|
||||||
initialRouteName="index"
|
initialRouteName="index"
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
<PlanningStack.Screen
|
<PlanningStack.Screen
|
||||||
name="index"
|
name="index"
|
||||||
component={PlanningScreen}
|
component={PlanningScreen}
|
||||||
options={{title: i18n.t('screens.planning.title')}}
|
options={{ title: i18n.t('screens.planning.title') }}
|
||||||
/>
|
/>
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'planning-information',
|
'planning-information',
|
||||||
PlanningStack,
|
PlanningStack,
|
||||||
PlanningDisplayScreen,
|
PlanningDisplayScreen,
|
||||||
i18n.t('screens.planning.eventDetails'),
|
i18n.t('screens.planning.eventDetails')
|
||||||
)}
|
)}
|
||||||
</PlanningStack.Navigator>
|
</PlanningStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -139,18 +159,19 @@ const HomeStack = createStackNavigator();
|
||||||
|
|
||||||
function HomeStackComponent(
|
function HomeStackComponent(
|
||||||
initialRoute: string | null,
|
initialRoute: string | null,
|
||||||
defaultData: {[key: string]: string},
|
defaultData: { [key: string]: string }
|
||||||
) {
|
) {
|
||||||
let params;
|
let params;
|
||||||
if (initialRoute) {
|
if (initialRoute) {
|
||||||
params = {data: defaultData, nextScreen: initialRoute, shouldOpen: true};
|
params = { data: defaultData, nextScreen: initialRoute, shouldOpen: true };
|
||||||
}
|
}
|
||||||
const {colors} = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<HomeStack.Navigator
|
<HomeStack.Navigator
|
||||||
initialRouteName="index"
|
initialRouteName="index"
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
{createCollapsibleStack(
|
{createCollapsibleStack(
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name="index"
|
name="index"
|
||||||
|
@ -161,11 +182,9 @@ function HomeStackComponent(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
},
|
},
|
||||||
headerTitle: () => (
|
headerTitle: () => (
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={styles.header}>
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{
|
style={styles.mascot}
|
||||||
width: 50,
|
|
||||||
}}
|
|
||||||
emotion={MASCOT_STYLE.RANDOM}
|
emotion={MASCOT_STYLE.RANDOM}
|
||||||
animated
|
animated
|
||||||
entryAnimation={{
|
entryAnimation={{
|
||||||
|
@ -178,12 +197,7 @@ function HomeStackComponent(
|
||||||
iterationCount: 'infinite',
|
iterationCount: 'infinite',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Title
|
<Title style={styles.title}>
|
||||||
style={{
|
|
||||||
marginLeft: 10,
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}>
|
|
||||||
{i18n.t('screens.home.title')}
|
{i18n.t('screens.home.title')}
|
||||||
</Title>
|
</Title>
|
||||||
</View>
|
</View>
|
||||||
|
@ -194,31 +208,31 @@ function HomeStackComponent(
|
||||||
{
|
{
|
||||||
collapsedColor: colors.surface,
|
collapsedColor: colors.surface,
|
||||||
useNativeDriver: true,
|
useNativeDriver: true,
|
||||||
},
|
}
|
||||||
)}
|
)}
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name="scanner"
|
name="scanner"
|
||||||
component={ScannerScreen}
|
component={ScannerScreen}
|
||||||
options={{title: i18n.t('screens.scanner.title')}}
|
options={{ title: i18n.t('screens.scanner.title') }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'club-information',
|
'club-information',
|
||||||
HomeStack,
|
HomeStack,
|
||||||
ClubDisplayScreen,
|
ClubDisplayScreen,
|
||||||
i18n.t('screens.clubs.details'),
|
i18n.t('screens.clubs.details')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'feed-information',
|
'feed-information',
|
||||||
HomeStack,
|
HomeStack,
|
||||||
FeedItemScreen,
|
FeedItemScreen,
|
||||||
i18n.t('screens.home.feed'),
|
i18n.t('screens.home.feed')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'planning-information',
|
'planning-information',
|
||||||
HomeStack,
|
HomeStack,
|
||||||
PlanningDisplayScreen,
|
PlanningDisplayScreen,
|
||||||
i18n.t('screens.planning.eventDetails'),
|
i18n.t('screens.planning.eventDetails')
|
||||||
)}
|
)}
|
||||||
</HomeStack.Navigator>
|
</HomeStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -231,18 +245,19 @@ function PlanexStackComponent() {
|
||||||
<PlanexStack.Navigator
|
<PlanexStack.Navigator
|
||||||
initialRouteName="index"
|
initialRouteName="index"
|
||||||
headerMode="screen"
|
headerMode="screen"
|
||||||
screenOptions={defaultScreenOptions}>
|
screenOptions={defaultScreenOptions}
|
||||||
|
>
|
||||||
{getWebsiteStack(
|
{getWebsiteStack(
|
||||||
'index',
|
'index',
|
||||||
PlanexStack,
|
PlanexStack,
|
||||||
PlanexScreen,
|
PlanexScreen,
|
||||||
i18n.t('screens.planex.title'),
|
i18n.t('screens.planex.title')
|
||||||
)}
|
)}
|
||||||
{CreateScreenCollapsibleStack(
|
{CreateScreenCollapsibleStack(
|
||||||
'group-select',
|
'group-select',
|
||||||
PlanexStack,
|
PlanexStack,
|
||||||
GroupSelectionScreen,
|
GroupSelectionScreen,
|
||||||
'',
|
''
|
||||||
)}
|
)}
|
||||||
</PlanexStack.Navigator>
|
</PlanexStack.Navigator>
|
||||||
);
|
);
|
||||||
|
@ -252,7 +267,7 @@ const Tab = createBottomTabNavigator();
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
defaultHomeRoute: string | null;
|
defaultHomeRoute: string | null;
|
||||||
defaultHomeData: {[key: string]: string};
|
defaultHomeData: { [key: string]: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class TabNavigator extends React.Component<PropsType> {
|
export default class TabNavigator extends React.Component<PropsType> {
|
||||||
|
@ -264,7 +279,7 @@ export default class TabNavigator extends React.Component<PropsType> {
|
||||||
this.defaultRoute = 'home';
|
this.defaultRoute = 'home';
|
||||||
if (!props.defaultHomeRoute) {
|
if (!props.defaultHomeRoute) {
|
||||||
this.defaultRoute = AsyncStorageManager.getString(
|
this.defaultRoute = AsyncStorageManager.getString(
|
||||||
AsyncStorageManager.PREFERENCES.defaultStartScreen.key,
|
AsyncStorageManager.PREFERENCES.defaultStartScreen.key
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
}
|
}
|
||||||
this.createHomeStackComponent = () =>
|
this.createHomeStackComponent = () =>
|
||||||
|
@ -275,31 +290,32 @@ export default class TabNavigator extends React.Component<PropsType> {
|
||||||
return (
|
return (
|
||||||
<Tab.Navigator
|
<Tab.Navigator
|
||||||
initialRouteName={this.defaultRoute}
|
initialRouteName={this.defaultRoute}
|
||||||
tabBar={(tabProps) => <CustomTabBar {...tabProps} />}>
|
tabBar={(tabProps) => <CustomTabBar {...tabProps} />}
|
||||||
|
>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="services"
|
name="services"
|
||||||
component={ServicesStackComponent}
|
component={ServicesStackComponent}
|
||||||
options={{title: i18n.t('screens.services.title')}}
|
options={{ title: i18n.t('screens.services.title') }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="proxiwash"
|
name="proxiwash"
|
||||||
component={ProxiwashStackComponent}
|
component={ProxiwashStackComponent}
|
||||||
options={{title: i18n.t('screens.proxiwash.title')}}
|
options={{ title: i18n.t('screens.proxiwash.title') }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="home"
|
name="home"
|
||||||
component={this.createHomeStackComponent}
|
component={this.createHomeStackComponent}
|
||||||
options={{title: i18n.t('screens.home.title')}}
|
options={{ title: i18n.t('screens.home.title') }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="planning"
|
name="planning"
|
||||||
component={PlanningStackComponent}
|
component={PlanningStackComponent}
|
||||||
options={{title: i18n.t('screens.planning.title')}}
|
options={{ title: i18n.t('screens.planning.title') }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="planex"
|
name="planex"
|
||||||
component={PlanexStackComponent}
|
component={PlanexStackComponent}
|
||||||
options={{title: i18n.t('screens.planex.title')}}
|
options={{ title: i18n.t('screens.planex.title') }}
|
||||||
/>
|
/>
|
||||||
</Tab.Navigator>
|
</Tab.Navigator>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {List} from 'react-native-paper';
|
import { List } from 'react-native-paper';
|
||||||
import {View} from 'react-native-animatable';
|
import { View } from 'react-native-animatable';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
import packageJson from '../../../package.json';
|
import packageJson from '../../../package.json';
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ function generateListFromObject(object: {
|
||||||
const list: Array<ListItemType> = [];
|
const list: Array<ListItemType> = [];
|
||||||
const keys = Object.keys(object);
|
const keys = Object.keys(object);
|
||||||
keys.forEach((key: string) => {
|
keys.forEach((key: string) => {
|
||||||
list.push({name: key, version: object[key]});
|
list.push({ name: key, version: object[key] });
|
||||||
});
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -60,18 +60,18 @@ export default class AboutDependenciesScreen extends React.Component<{}> {
|
||||||
|
|
||||||
keyExtractor = (item: ListItemType): string => item.name;
|
keyExtractor = (item: ListItemType): string => item.name;
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: ListItemType}) => (
|
getRenderItem = ({ item }: { item: ListItemType }) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={item.name}
|
title={item.name}
|
||||||
description={item.version.replace('^', '').replace('~', '')}
|
description={item.version.replace('^', '').replace('~', '')}
|
||||||
style={{height: LIST_ITEM_HEIGHT}}
|
style={{ height: LIST_ITEM_HEIGHT }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
getItemLayout = (
|
getItemLayout = (
|
||||||
data: Array<ListItemType> | null | undefined,
|
data: Array<ListItemType> | null | undefined,
|
||||||
index: number,
|
index: number
|
||||||
): {length: number; offset: number; index: number} => ({
|
): { length: number; offset: number; index: number } => ({
|
||||||
length: LIST_ITEM_HEIGHT,
|
length: LIST_ITEM_HEIGHT,
|
||||||
offset: LIST_ITEM_HEIGHT * index,
|
offset: LIST_ITEM_HEIGHT * index,
|
||||||
index,
|
index,
|
||||||
|
|
|
@ -18,14 +18,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList, Linking, Platform, Image, View} from 'react-native';
|
import {
|
||||||
|
FlatList,
|
||||||
|
Linking,
|
||||||
|
Platform,
|
||||||
|
Image,
|
||||||
|
View,
|
||||||
|
StyleSheet,
|
||||||
|
} from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Avatar, Card, List} from 'react-native-paper';
|
import { Avatar, Card, List } from 'react-native-paper';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import packageJson from '../../../package.json';
|
import packageJson from '../../../package.json';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
import OptionsDialog from '../../components/Dialogs/OptionsDialog';
|
import OptionsDialog from '../../components/Dialogs/OptionsDialog';
|
||||||
import type {OptionsDialogButtonType} from '../../components/Dialogs/OptionsDialog';
|
import type { OptionsDialogButtonType } from '../../components/Dialogs/OptionsDialog';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
const APP_LOGO = require('../../../assets/android.icon.round.png');
|
const APP_LOGO = require('../../../assets/android.icon.round.png');
|
||||||
|
|
||||||
|
@ -69,6 +77,15 @@ type StateType = {
|
||||||
dialogButtons: Array<OptionsDialogButtonType>;
|
dialogButtons: Array<OptionsDialogButtonType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
padding: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a link in the device's browser
|
* Opens a link in the device's browser
|
||||||
* @param link The link to open
|
* @param link The link to open
|
||||||
|
@ -171,7 +188,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onPressCallback: () => {
|
onPressCallback: () => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('feedback');
|
navigation.navigate('feedback');
|
||||||
},
|
},
|
||||||
icon: 'bug',
|
icon: 'bug',
|
||||||
|
@ -228,7 +245,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onPressCallback: () => {
|
onPressCallback: () => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('dependencies');
|
navigation.navigate('dependencies');
|
||||||
},
|
},
|
||||||
icon: 'developer-board',
|
icon: 'developer-board',
|
||||||
|
@ -267,7 +284,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
...this.getMemberData(this.majorContributors),
|
...this.getMemberData(this.majorContributors),
|
||||||
{
|
{
|
||||||
onPressCallback: () => {
|
onPressCallback: () => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('feedback');
|
navigation.navigate('feedback');
|
||||||
},
|
},
|
||||||
icon: 'hand-pointing-right',
|
icon: 'hand-pointing-right',
|
||||||
|
@ -306,7 +323,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
onPress: this.onDialogDismiss,
|
onPress: this.onDialogDismiss,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const {linkedin, trollLink, mail} = user;
|
const { linkedin, trollLink, mail } = user;
|
||||||
if (linkedin != null) {
|
if (linkedin != null) {
|
||||||
dialogBtn.push({
|
dialogBtn.push({
|
||||||
title: '',
|
title: '',
|
||||||
|
@ -348,14 +365,14 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getAppCard() {
|
getAppCard() {
|
||||||
return (
|
return (
|
||||||
<Card style={{marginBottom: 10}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title="Campus"
|
title="Campus"
|
||||||
subtitle={packageJson.version}
|
subtitle={packageJson.version}
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
<Image
|
<Image
|
||||||
source={APP_LOGO}
|
source={APP_LOGO}
|
||||||
style={{width: iconProps.size, height: iconProps.size}}
|
style={{ width: iconProps.size, height: iconProps.size }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -377,7 +394,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getTeamCard() {
|
getTeamCard() {
|
||||||
return (
|
return (
|
||||||
<Card style={{marginBottom: 10}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.about.team')}
|
title={i18n.t('screens.about.team')}
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
|
@ -402,7 +419,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getThanksCard() {
|
getThanksCard() {
|
||||||
return (
|
return (
|
||||||
<Card style={{marginBottom: 10}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.about.thanks')}
|
title={i18n.t('screens.about.thanks')}
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
|
@ -427,7 +444,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getTechnoCard() {
|
getTechnoCard() {
|
||||||
return (
|
return (
|
||||||
<Card style={{marginBottom: 10}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.about.technologies')}
|
title={i18n.t('screens.about.technologies')}
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
|
@ -478,7 +495,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
marginRight: number;
|
marginRight: number;
|
||||||
marginVertical?: number;
|
marginVertical?: number;
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<List.Icon color={props.color} style={props.style} icon={item.icon} />
|
<List.Icon color={props.color} style={props.style} icon={item.icon} />
|
||||||
|
@ -490,7 +507,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
*
|
*
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getCardItem = ({item}: {item: ListItemType}) => {
|
getCardItem = ({ item }: { item: ListItemType }) => {
|
||||||
const getItemIcon = (props: {
|
const getItemIcon = (props: {
|
||||||
color: string;
|
color: string;
|
||||||
style?: {
|
style?: {
|
||||||
|
@ -523,7 +540,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
* @param item The item to show
|
* @param item The item to show
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getMainCard = ({item}: {item: {id: string}}) => {
|
getMainCard = ({ item }: { item: { id: string } }) => {
|
||||||
switch (item.id) {
|
switch (item.id) {
|
||||||
case 'app':
|
case 'app':
|
||||||
return this.getAppCard();
|
return this.getAppCard();
|
||||||
|
@ -539,7 +556,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onDialogDismiss = () => {
|
onDialogDismiss = () => {
|
||||||
this.setState({dialogVisible: false});
|
this.setState({ dialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -551,14 +568,11 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
||||||
keyExtractor = (item: ListItemType): string => item.icon;
|
keyExtractor = (item: ListItemType): string => item.icon;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={GENERAL_STYLES.flex}>
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
}}>
|
|
||||||
<CollapsibleFlatList
|
<CollapsibleFlatList
|
||||||
style={{padding: 5}}
|
style={styles.list}
|
||||||
data={this.dataOrder}
|
data={this.dataOrder}
|
||||||
renderItem={this.getMainCard}
|
renderItem={this.getMainCard}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
List,
|
List,
|
||||||
|
@ -27,7 +27,7 @@ import {
|
||||||
Title,
|
Title,
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {Modalize} from 'react-native-modalize';
|
import { Modalize } from 'react-native-modalize';
|
||||||
import CustomModal from '../../components/Overrides/CustomModal';
|
import CustomModal from '../../components/Overrides/CustomModal';
|
||||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
@ -47,6 +47,17 @@ type StateType = {
|
||||||
currentPreferences: Array<PreferenceItemType>;
|
currentPreferences: Array<PreferenceItemType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining the Debug screen.
|
* Class defining the Debug screen.
|
||||||
* This screen allows the user to get and modify information on the app/device.
|
* This screen allows the user to get and modify information on the app/device.
|
||||||
|
@ -67,7 +78,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
this.modalInputValue = '';
|
this.modalInputValue = '';
|
||||||
const currentPreferences: Array<PreferenceItemType> = [];
|
const currentPreferences: Array<PreferenceItemType> = [];
|
||||||
Object.values(AsyncStorageManager.PREFERENCES).forEach((object: any) => {
|
Object.values(AsyncStorageManager.PREFERENCES).forEach((object: any) => {
|
||||||
const newObject: PreferenceItemType = {...object};
|
const newObject: PreferenceItemType = { ...object };
|
||||||
newObject.current = AsyncStorageManager.getString(newObject.key);
|
newObject.current = AsyncStorageManager.getString(newObject.key);
|
||||||
currentPreferences.push(newObject);
|
currentPreferences.push(newObject);
|
||||||
});
|
});
|
||||||
|
@ -83,7 +94,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getModalContent() {
|
getModalContent() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
let key = '';
|
let key = '';
|
||||||
let defaultValue = '';
|
let defaultValue = '';
|
||||||
let current = '';
|
let current = '';
|
||||||
|
@ -95,11 +106,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={styles.container}>
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
padding: 20,
|
|
||||||
}}>
|
|
||||||
<Title>{key}</Title>
|
<Title>{key}</Title>
|
||||||
<Subheading>Default: {defaultValue}</Subheading>
|
<Subheading>Default: {defaultValue}</Subheading>
|
||||||
<Subheading>Current: {current}</Subheading>
|
<Subheading>Current: {current}</Subheading>
|
||||||
|
@ -109,18 +116,15 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
this.modalInputValue = text;
|
this.modalInputValue = text;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<View
|
<View style={styles.buttonContainer}>
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginTop: 10,
|
|
||||||
}}>
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
dark
|
dark
|
||||||
color={props.theme.colors.success}
|
color={props.theme.colors.success}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.saveNewPrefs(key, this.modalInputValue);
|
this.saveNewPrefs(key, this.modalInputValue);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Save new value
|
Save new value
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
@ -129,7 +133,8 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
color={props.theme.colors.danger}
|
color={props.theme.colors.danger}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
this.saveNewPrefs(key, defaultValue);
|
this.saveNewPrefs(key, defaultValue);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Reset to default
|
Reset to default
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
@ -137,7 +142,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: PreferenceItemType}) => {
|
getRenderItem = ({ item }: { item: PreferenceItemType }) => {
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={item.key}
|
title={item.key}
|
||||||
|
@ -179,7 +184,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
findIndexOfKey(key: string): number {
|
findIndexOfKey(key: string): number {
|
||||||
const {currentPreferences} = this.state;
|
const { currentPreferences } = this.state;
|
||||||
let index = -1;
|
let index = -1;
|
||||||
for (let i = 0; i < currentPreferences.length; i += 1) {
|
for (let i = 0; i < currentPreferences.length; i += 1) {
|
||||||
if (currentPreferences[i].key === key) {
|
if (currentPreferences[i].key === key) {
|
||||||
|
@ -202,7 +207,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
} => {
|
} => {
|
||||||
const currentPreferences = [...prevState.currentPreferences];
|
const currentPreferences = [...prevState.currentPreferences];
|
||||||
currentPreferences[this.findIndexOfKey(key)].current = value;
|
currentPreferences[this.findIndexOfKey(key)].current = value;
|
||||||
return {currentPreferences};
|
return { currentPreferences };
|
||||||
});
|
});
|
||||||
AsyncStorageManager.set(key, value);
|
AsyncStorageManager.set(key, value);
|
||||||
if (this.modalRef) {
|
if (this.modalRef) {
|
||||||
|
@ -211,7 +216,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<CustomModal onRef={this.onModalRef}>
|
<CustomModal onRef={this.onModalRef}>
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList, Image, Linking, View} from 'react-native';
|
import { FlatList, Image, Linking, StyleSheet, View } from 'react-native';
|
||||||
import {Avatar, Card, List, Text} from 'react-native-paper';
|
import { Avatar, Card, List, Text } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
|
||||||
|
@ -31,6 +31,24 @@ type DatasetItemType = {
|
||||||
icon: string;
|
icon: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
imageContainer: {
|
||||||
|
width: '100%',
|
||||||
|
height: 100,
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
flex: 1,
|
||||||
|
resizeMode: 'contain',
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a planning event information page.
|
* Class defining a planning event information page.
|
||||||
*/
|
*/
|
||||||
|
@ -105,7 +123,7 @@ class AmicaleContactScreen extends React.Component<{}> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: DatasetItemType}) => {
|
getRenderItem = ({ item }: { item: DatasetItemType }) => {
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
Linking.openURL(`mailto:${item.email}`);
|
Linking.openURL(`mailto:${item.email}`);
|
||||||
};
|
};
|
||||||
|
@ -129,22 +147,14 @@ class AmicaleContactScreen extends React.Component<{}> {
|
||||||
getScreen = () => {
|
getScreen = () => {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View
|
<View style={styles.imageContainer}>
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: 100,
|
|
||||||
marginTop: 20,
|
|
||||||
marginBottom: 20,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
<Image
|
<Image
|
||||||
source={AMICALE_LOGO}
|
source={AMICALE_LOGO}
|
||||||
style={{flex: 1, resizeMode: 'contain'}}
|
style={styles.image}
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Card style={{margin: 5}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.amicaleAbout.title')}
|
title={i18n.t('screens.amicaleAbout.title')}
|
||||||
subtitle={i18n.t('screens.amicaleAbout.subtitle')}
|
subtitle={i18n.t('screens.amicaleAbout.subtitle')}
|
||||||
|
@ -168,7 +178,7 @@ class AmicaleContactScreen extends React.Component<{}> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<CollapsibleFlatList
|
<CollapsibleFlatList
|
||||||
data={[{key: '1'}]}
|
data={[{ key: '1' }]}
|
||||||
renderItem={this.getScreen}
|
renderItem={this.getScreen}
|
||||||
hasTab
|
hasTab
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, View} from 'react-native';
|
import { Image, StyleSheet, View } from 'react-native';
|
||||||
import {Card, Avatar, Text} from 'react-native-paper';
|
import { Card, Avatar, Text } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import Autolink from 'react-native-autolink';
|
import Autolink from 'react-native-autolink';
|
||||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||||
|
@ -27,26 +27,39 @@ const AMICALE_ICON = require('../../../../assets/amicale.png');
|
||||||
|
|
||||||
const CONTACT_LINK = 'clubs@amicale-insat.fr';
|
const CONTACT_LINK = 'clubs@amicale-insat.fr';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
padding: 5,
|
||||||
|
},
|
||||||
|
imageContainer: {
|
||||||
|
width: '100%',
|
||||||
|
height: 100,
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
flex: 1,
|
||||||
|
resizeMode: 'contain',
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function ClubAboutScreen() {
|
function ClubAboutScreen() {
|
||||||
return (
|
return (
|
||||||
<CollapsibleScrollView style={{padding: 5}}>
|
<CollapsibleScrollView style={styles.container}>
|
||||||
<View
|
<View style={styles.imageContainer}>
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: 100,
|
|
||||||
marginTop: 20,
|
|
||||||
marginBottom: 20,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
<Image
|
<Image
|
||||||
source={AMICALE_ICON}
|
source={AMICALE_ICON}
|
||||||
style={{flex: 1, resizeMode: 'contain'}}
|
style={styles.image}
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text>{i18n.t('screens.clubs.about.text')}</Text>
|
<Text>{i18n.t('screens.clubs.about.text')}</Text>
|
||||||
<Card style={{margin: 5}}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.clubs.about.title')}
|
title={i18n.t('screens.clubs.about.title')}
|
||||||
subtitle={i18n.t('screens.clubs.about.subtitle')}
|
subtitle={i18n.t('screens.clubs.about.subtitle')}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Linking, View} from 'react-native';
|
import { Linking, StyleSheet, View } from 'react-native';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
|
@ -28,12 +28,12 @@ import {
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
||||||
import CustomHTML from '../../../components/Overrides/CustomHTML';
|
import CustomHTML from '../../../components/Overrides/CustomHTML';
|
||||||
import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
|
import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
|
||||||
import type {ClubCategoryType, ClubType} from './ClubListScreen';
|
import type { ClubCategoryType, ClubType } from './ClubListScreen';
|
||||||
import {ERROR_TYPE} from '../../../utils/WebData';
|
import { ERROR_TYPE } from '../../../utils/WebData';
|
||||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||||
import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
|
import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
|
||||||
|
|
||||||
|
@ -51,6 +51,37 @@ type PropsType = {
|
||||||
|
|
||||||
const AMICALE_MAIL = 'clubs@amicale-insat.fr';
|
const AMICALE_MAIL = 'clubs@amicale-insat.fr';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
category: {
|
||||||
|
marginRight: 5,
|
||||||
|
},
|
||||||
|
categoryContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginTop: 5,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
emailButton: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
},
|
||||||
|
scroll: {
|
||||||
|
paddingLeft: 5,
|
||||||
|
paddingRight: 5,
|
||||||
|
},
|
||||||
|
imageButton: {
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a club event information page.
|
* Class defining a club event information page.
|
||||||
* If called with data and categories navigation parameters, will use those to display the data.
|
* If called with data and categories navigation parameters, will use those to display the data.
|
||||||
|
@ -117,13 +148,13 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
categories.forEach((cat: number | null) => {
|
categories.forEach((cat: number | null) => {
|
||||||
if (cat != null) {
|
if (cat != null) {
|
||||||
final.push(
|
final.push(
|
||||||
<Chip style={{marginRight: 5}} key={cat}>
|
<Chip style={styles.category} key={cat}>
|
||||||
{this.getCategoryName(cat)}
|
{this.getCategoryName(cat)}
|
||||||
</Chip>,
|
</Chip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
|
return <View style={styles.categoryContainer}>{final}</View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +165,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getManagersRender(managers: Array<string>, email: string | null) {
|
getManagersRender(managers: Array<string>, email: string | null) {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
const managersListView: Array<React.ReactNode> = [];
|
const managersListView: Array<React.ReactNode> = [];
|
||||||
managers.forEach((item: string) => {
|
managers.forEach((item: string) => {
|
||||||
managersListView.push(<Paragraph key={item}>{item}</Paragraph>);
|
managersListView.push(<Paragraph key={item}>{item}</Paragraph>);
|
||||||
|
@ -142,7 +173,11 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
const hasManagers = managers.length > 0;
|
const hasManagers = managers.length > 0;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
style={{
|
||||||
|
marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20,
|
||||||
|
...styles.card,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.clubs.managers')}
|
title={i18n.t('screens.clubs.managers')}
|
||||||
subtitle={
|
subtitle={
|
||||||
|
@ -153,7 +188,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
left={(iconProps) => (
|
left={(iconProps) => (
|
||||||
<Avatar.Icon
|
<Avatar.Icon
|
||||||
size={iconProps.size}
|
size={iconProps.size}
|
||||||
style={{backgroundColor: 'transparent'}}
|
style={styles.icon}
|
||||||
color={
|
color={
|
||||||
hasManagers
|
hasManagers
|
||||||
? props.theme.colors.success
|
? props.theme.colors.success
|
||||||
|
@ -193,7 +228,8 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
Linking.openURL(`mailto:${destinationEmail}`);
|
Linking.openURL(`mailto:${destinationEmail}`);
|
||||||
}}
|
}}
|
||||||
style={{marginLeft: 'auto'}}>
|
style={styles.emailButton}
|
||||||
|
>
|
||||||
{text}
|
{text}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
@ -205,19 +241,12 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
this.updateHeaderTitle(data);
|
this.updateHeaderTitle(data);
|
||||||
return (
|
return (
|
||||||
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
|
<CollapsibleScrollView style={styles.scroll} hasTab>
|
||||||
{this.getCategoriesRender(data.category)}
|
{this.getCategoriesRender(data.category)}
|
||||||
{data.logo !== null ? (
|
{data.logo !== null ? (
|
||||||
<ImageGalleryButton
|
<ImageGalleryButton
|
||||||
images={[{url: data.logo}]}
|
images={[{ url: data.logo }]}
|
||||||
style={{
|
style={styles.imageButton}
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View />
|
<View />
|
||||||
|
@ -244,12 +273,12 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
* @param data The club data
|
* @param data The club data
|
||||||
*/
|
*/
|
||||||
updateHeaderTitle(data: ClubType) {
|
updateHeaderTitle(data: ClubType) {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.navigation.setOptions({title: data.name});
|
props.navigation.setOptions({ title: data.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
if (this.shouldFetchData) {
|
if (this.shouldFetchData) {
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
|
@ -257,7 +286,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
||||||
requests={[
|
requests={[
|
||||||
{
|
{
|
||||||
link: 'clubs/info',
|
link: 'clubs/info',
|
||||||
params: {id: this.clubId},
|
params: { id: this.clubId },
|
||||||
mandatory: true,
|
mandatory: true,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -18,13 +18,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Platform} from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import {Searchbar} from 'react-native-paper';
|
import { Searchbar } from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
||||||
import ClubListItem from '../../../components/Lists/Clubs/ClubListItem';
|
import ClubListItem from '../../../components/Lists/Clubs/ClubListItem';
|
||||||
import {isItemInCategoryFilter, stringMatchQuery} from '../../../utils/Search';
|
import {
|
||||||
|
isItemInCategoryFilter,
|
||||||
|
stringMatchQuery,
|
||||||
|
} from '../../../utils/Search';
|
||||||
import ClubListHeader from '../../../components/Lists/Clubs/ClubListHeader';
|
import ClubListHeader from '../../../components/Lists/Clubs/ClubListHeader';
|
||||||
import MaterialHeaderButtons, {
|
import MaterialHeaderButtons, {
|
||||||
Item,
|
Item,
|
||||||
|
@ -73,15 +76,15 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
* Creates the header content
|
* Creates the header content
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.navigation.setOptions({
|
props.navigation.setOptions({
|
||||||
headerTitle: this.getSearchBar,
|
headerTitle: this.getSearchBar,
|
||||||
headerRight: this.getHeaderButtons,
|
headerRight: this.getHeaderButtons,
|
||||||
headerBackTitleVisible: false,
|
headerBackTitleVisible: false,
|
||||||
headerTitleContainerStyle:
|
headerTitleContainerStyle:
|
||||||
Platform.OS === 'ios'
|
Platform.OS === 'ios'
|
||||||
? {marginHorizontal: 0, width: '70%'}
|
? { marginHorizontal: 0, width: '70%' }
|
||||||
: {marginHorizontal: 0, right: 50, left: 50},
|
: { marginHorizontal: 0, right: 50, left: 50 },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +95,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
* @param item The article pressed
|
* @param item The article pressed
|
||||||
*/
|
*/
|
||||||
onListItemPress(item: ClubType) {
|
onListItemPress(item: ClubType) {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.navigation.navigate('club-information', {
|
props.navigation.navigate('club-information', {
|
||||||
data: item,
|
data: item,
|
||||||
categories: this.categories,
|
categories: this.categories,
|
||||||
|
@ -133,7 +136,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getHeaderButtons = () => {
|
getHeaderButtons = () => {
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
props.navigation.navigate('club-about');
|
props.navigation.navigate('club-about');
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
@ -147,7 +150,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
data: Array<{
|
data: Array<{
|
||||||
categories: Array<ClubCategoryType>;
|
categories: Array<ClubCategoryType>;
|
||||||
clubs: Array<ClubType>;
|
clubs: Array<ClubType>;
|
||||||
} | null>,
|
} | null>
|
||||||
) => {
|
) => {
|
||||||
let categoryList: Array<ClubCategoryType> = [];
|
let categoryList: Array<ClubCategoryType> = [];
|
||||||
let clubList: Array<ClubType> = [];
|
let clubList: Array<ClubType> = [];
|
||||||
|
@ -175,7 +178,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getListHeader() {
|
getListHeader() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
return (
|
return (
|
||||||
<ClubListHeader
|
<ClubListHeader
|
||||||
categories={this.categories}
|
categories={this.categories}
|
||||||
|
@ -201,7 +204,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
return cat;
|
return cat;
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: ClubType}) => {
|
getRenderItem = ({ item }: { item: ClubType }) => {
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
this.onListItemPress(item);
|
this.onListItemPress(item);
|
||||||
};
|
};
|
||||||
|
@ -222,8 +225,8 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
|
|
||||||
itemLayout = (
|
itemLayout = (
|
||||||
data: Array<ClubType> | null | undefined,
|
data: Array<ClubType> | null | undefined,
|
||||||
index: number,
|
index: number
|
||||||
): {length: number; offset: number; index: number} => ({
|
): { length: number; offset: number; index: number } => ({
|
||||||
length: LIST_ITEM_HEIGHT,
|
length: LIST_ITEM_HEIGHT,
|
||||||
offset: LIST_ITEM_HEIGHT * index,
|
offset: LIST_ITEM_HEIGHT * index,
|
||||||
index,
|
index,
|
||||||
|
@ -239,7 +242,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
* @param categoryId The category to add/remove from the filter
|
* @param categoryId The category to add/remove from the filter
|
||||||
*/
|
*/
|
||||||
updateFilteredData(filterStr: string | null, categoryId: number | null) {
|
updateFilteredData(filterStr: string | null, categoryId: number | null) {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
const newCategoriesState = [...state.currentlySelectedCategories];
|
const newCategoriesState = [...state.currentlySelectedCategories];
|
||||||
let newStrState = state.currentSearchString;
|
let newStrState = state.currentSearchString;
|
||||||
if (filterStr !== null) {
|
if (filterStr !== null) {
|
||||||
|
@ -268,7 +271,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
shouldRenderItem(item: ClubType): boolean {
|
shouldRenderItem(item: ClubType): boolean {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
let shouldRender =
|
let shouldRender =
|
||||||
state.currentlySelectedCategories.length === 0 ||
|
state.currentlySelectedCategories.length === 0 ||
|
||||||
isItemInCategoryFilter(state.currentlySelectedCategories, item.category);
|
isItemInCategoryFilter(state.currentlySelectedCategories, item.category);
|
||||||
|
@ -279,7 +282,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props} = this;
|
const { props } = this;
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
navigation={props.navigation}
|
navigation={props.navigation}
|
||||||
|
|
|
@ -26,12 +26,13 @@ import {
|
||||||
Title,
|
Title,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {getRelativeDateString} from '../../../utils/EquipmentBooking';
|
import { getRelativeDateString } from '../../../utils/EquipmentBooking';
|
||||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||||
import {StackScreenProps} from '@react-navigation/stack';
|
import { StackScreenProps } from '@react-navigation/stack';
|
||||||
import {MainStackParamsList} from '../../../navigation/MainNavigator';
|
import { MainStackParamsList } from '../../../navigation/MainNavigator';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type EquipmentConfirmScreenNavigationProp = StackScreenProps<
|
type EquipmentConfirmScreenNavigationProp = StackScreenProps<
|
||||||
MainStackParamsList,
|
MainStackParamsList,
|
||||||
|
@ -40,6 +41,32 @@ type EquipmentConfirmScreenNavigationProp = StackScreenProps<
|
||||||
|
|
||||||
type Props = EquipmentConfirmScreenNavigationProp;
|
type Props = EquipmentConfirmScreenNavigationProp;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
titleContainer: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: 35,
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function EquipmentConfirmScreen(props: Props) {
|
function EquipmentConfirmScreen(props: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const item = props.route.params?.item;
|
const item = props.route.params?.item;
|
||||||
|
@ -63,31 +90,20 @@ function EquipmentConfirmScreen(props: Props) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<CollapsibleScrollView>
|
<CollapsibleScrollView>
|
||||||
<Card style={{margin: 5}}>
|
<Card style={styles.card}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<View
|
<View style={styles.titleContainer}>
|
||||||
style={{
|
<Headline style={styles.title}>{item.name}</Headline>
|
||||||
marginLeft: 'auto',
|
<Caption style={styles.caption}>
|
||||||
marginRight: 'auto',
|
({i18n.t('screens.equipment.bail', { cost: item.caution })})
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}>
|
|
||||||
<Headline style={{textAlign: 'center'}}>{item.name}</Headline>
|
|
||||||
<Caption
|
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
lineHeight: 35,
|
|
||||||
marginLeft: 10,
|
|
||||||
}}>
|
|
||||||
({i18n.t('screens.equipment.bail', {cost: item.caution})})
|
|
||||||
</Caption>
|
</Caption>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Title style={{color: theme.colors.success, textAlign: 'center'}}>
|
<Title style={{ color: theme.colors.success, ...styles.subtitle }}>
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Title>
|
</Title>
|
||||||
<Paragraph style={{textAlign: 'center'}}>
|
<Paragraph style={styles.text}>
|
||||||
{i18n.t('screens.equipment.bookingConfirmedMessage')}
|
{i18n.t('screens.equipment.bookingConfirmedMessage')}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
|
|
|
@ -18,16 +18,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import {Button} from 'react-native-paper';
|
import { Button } from 'react-native-paper';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
||||||
import EquipmentListItem from '../../../components/Lists/Equipment/EquipmentListItem';
|
import EquipmentListItem from '../../../components/Lists/Equipment/EquipmentListItem';
|
||||||
import MascotPopup from '../../../components/Mascot/MascotPopup';
|
import MascotPopup from '../../../components/Mascot/MascotPopup';
|
||||||
import {MASCOT_STYLE} from '../../../components/Mascot/Mascot';
|
import { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
||||||
import AsyncStorageManager from '../../../managers/AsyncStorageManager';
|
import AsyncStorageManager from '../../../managers/AsyncStorageManager';
|
||||||
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
navigation: StackNavigationProp<any>;
|
navigation: StackNavigationProp<any>;
|
||||||
|
@ -41,7 +42,7 @@ export type DeviceType = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
caution: number;
|
caution: number;
|
||||||
booked_at: Array<{begin: string; end: string}>;
|
booked_at: Array<{ begin: string; end: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RentedDeviceType = {
|
export type RentedDeviceType = {
|
||||||
|
@ -53,10 +54,18 @@ export type RentedDeviceType = {
|
||||||
|
|
||||||
const LIST_ITEM_HEIGHT = 64;
|
const LIST_ITEM_HEIGHT = 64;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
headerContainer: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
userRents: null | Array<RentedDeviceType>;
|
userRents: null | Array<RentedDeviceType>;
|
||||||
|
|
||||||
authRef: {current: null | AuthenticatedScreen<any>};
|
authRef: { current: null | AuthenticatedScreen<any> };
|
||||||
|
|
||||||
canRefresh: boolean;
|
canRefresh: boolean;
|
||||||
|
|
||||||
|
@ -65,7 +74,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
this.userRents = null;
|
this.userRents = null;
|
||||||
this.state = {
|
this.state = {
|
||||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
|
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
this.canRefresh = false;
|
this.canRefresh = false;
|
||||||
|
@ -84,8 +93,8 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
this.canRefresh = true;
|
this.canRefresh = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: DeviceType}) => {
|
getRenderItem = ({ item }: { item: DeviceType }) => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<EquipmentListItem
|
<EquipmentListItem
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
@ -115,20 +124,13 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getListHeader() {
|
getListHeader() {
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={styles.headerContainer}>
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
}}>
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="help-circle"
|
icon="help-circle"
|
||||||
onPress={this.showMascotDialog}
|
onPress={this.showMascotDialog}
|
||||||
style={{
|
style={GENERAL_STYLES.centerHorizontal}
|
||||||
marginRight: 'auto',
|
>
|
||||||
marginLeft: 'auto',
|
|
||||||
}}>
|
|
||||||
{i18n.t('screens.equipment.mascotDialog.title')}
|
{i18n.t('screens.equipment.mascotDialog.title')}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
@ -145,8 +147,10 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
getScreen = (
|
getScreen = (
|
||||||
data: Array<
|
data: Array<
|
||||||
{devices: Array<DeviceType>} | {locations: Array<RentedDeviceType>} | null
|
| { devices: Array<DeviceType> }
|
||||||
>,
|
| { locations: Array<RentedDeviceType> }
|
||||||
|
| null
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
const [allDevices, userRents] = data;
|
const [allDevices, userRents] = data;
|
||||||
if (userRents) {
|
if (userRents) {
|
||||||
|
@ -161,7 +165,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
ListHeaderComponent={this.getListHeader()}
|
ListHeaderComponent={this.getListHeader()}
|
||||||
data={
|
data={
|
||||||
allDevices
|
allDevices
|
||||||
? (allDevices as {devices: Array<DeviceType>}).devices
|
? (allDevices as { devices: Array<DeviceType> }).devices
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -169,21 +173,21 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
showMascotDialog = () => {
|
showMascotDialog = () => {
|
||||||
this.setState({mascotDialogVisible: true});
|
this.setState({ mascotDialogVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
hideMascotDialog = () => {
|
hideMascotDialog = () => {
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
|
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
this.setState({mascotDialogVisible: false});
|
this.setState({ mascotDialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
navigation={props.navigation}
|
navigation={props.navigation}
|
||||||
ref={this.authRef}
|
ref={this.authRef}
|
||||||
|
|
|
@ -26,12 +26,12 @@ import {
|
||||||
Subheading,
|
Subheading,
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import {StackNavigationProp, StackScreenProps} from '@react-navigation/stack';
|
import { StackNavigationProp, StackScreenProps } from '@react-navigation/stack';
|
||||||
import {BackHandler, View} from 'react-native';
|
import { BackHandler, StyleSheet, View } from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {CalendarList, PeriodMarking} from 'react-native-calendars';
|
import { CalendarList, PeriodMarking } from 'react-native-calendars';
|
||||||
import type {DeviceType} from './EquipmentListScreen';
|
import type { DeviceType } from './EquipmentListScreen';
|
||||||
import LoadingConfirmDialog from '../../../components/Dialogs/LoadingConfirmDialog';
|
import LoadingConfirmDialog from '../../../components/Dialogs/LoadingConfirmDialog';
|
||||||
import ErrorDialog from '../../../components/Dialogs/ErrorDialog';
|
import ErrorDialog from '../../../components/Dialogs/ErrorDialog';
|
||||||
import {
|
import {
|
||||||
|
@ -44,7 +44,8 @@ import {
|
||||||
} from '../../../utils/EquipmentBooking';
|
} from '../../../utils/EquipmentBooking';
|
||||||
import ConnectionManager from '../../../managers/ConnectionManager';
|
import ConnectionManager from '../../../managers/ConnectionManager';
|
||||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||||
import {MainStackParamsList} from '../../../navigation/MainNavigator';
|
import { MainStackParamsList } from '../../../navigation/MainNavigator';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
type EquipmentRentScreenNavigationProp = StackScreenProps<
|
type EquipmentRentScreenNavigationProp = StackScreenProps<
|
||||||
MainStackParamsList,
|
MainStackParamsList,
|
||||||
|
@ -67,12 +68,56 @@ type StateType = {
|
||||||
currentError: number;
|
currentError: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
titleContainer: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
textAlign: 'center',
|
||||||
|
lineHeight: 35,
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 10,
|
||||||
|
minHeight: 50,
|
||||||
|
},
|
||||||
|
calendar: {
|
||||||
|
marginBottom: 50,
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
flex: 1,
|
||||||
|
transform: [{ translateY: 100 }],
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
width: '80%',
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginBottom: 20,
|
||||||
|
borderRadius: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
class EquipmentRentScreen extends React.Component<Props, StateType> {
|
class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
item: DeviceType | null;
|
item: DeviceType | null;
|
||||||
|
|
||||||
bookedDates: Array<string>;
|
bookedDates: Array<string>;
|
||||||
|
|
||||||
bookRef: {current: null | (Animatable.View & View)};
|
bookRef: { current: null | (Animatable.View & View) };
|
||||||
|
|
||||||
canBookEquipment: boolean;
|
canBookEquipment: boolean;
|
||||||
|
|
||||||
|
@ -101,14 +146,14 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
this.item = null;
|
this.item = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const {item} = this;
|
const { item } = this;
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
this.lockedDates = {};
|
this.lockedDates = {};
|
||||||
item.booked_at.forEach((date: {begin: string; end: string}) => {
|
item.booked_at.forEach((date: { begin: string; end: string }) => {
|
||||||
const range = getValidRange(
|
const range = getValidRange(
|
||||||
new Date(date.begin),
|
new Date(date.begin),
|
||||||
new Date(date.end),
|
new Date(date.end),
|
||||||
null,
|
null
|
||||||
);
|
);
|
||||||
this.lockedDates = {
|
this.lockedDates = {
|
||||||
...this.lockedDates,
|
...this.lockedDates,
|
||||||
|
@ -122,17 +167,17 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
* Captures focus and blur events to hook on android back button
|
* Captures focus and blur events to hook on android back button
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.addListener('focus', () => {
|
navigation.addListener('focus', () => {
|
||||||
BackHandler.addEventListener(
|
BackHandler.addEventListener(
|
||||||
'hardwareBackPress',
|
'hardwareBackPress',
|
||||||
this.onBackButtonPressAndroid,
|
this.onBackButtonPressAndroid
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
navigation.addListener('blur', () => {
|
navigation.addListener('blur', () => {
|
||||||
BackHandler.removeEventListener(
|
BackHandler.removeEventListener(
|
||||||
'hardwareBackPress',
|
'hardwareBackPress',
|
||||||
this.onBackButtonPressAndroid,
|
this.onBackButtonPressAndroid
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -152,11 +197,11 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onDialogDismiss = () => {
|
onDialogDismiss = () => {
|
||||||
this.setState({dialogVisible: false});
|
this.setState({ dialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onErrorDialogDismiss = () => {
|
onErrorDialogDismiss = () => {
|
||||||
this.setState({errorDialogVisible: false});
|
this.setState({ errorDialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,7 +213,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
*/
|
*/
|
||||||
onDialogAccept = (): Promise<void> => {
|
onDialogAccept = (): Promise<void> => {
|
||||||
return new Promise((resolve: () => void) => {
|
return new Promise((resolve: () => void) => {
|
||||||
const {item, props} = this;
|
const { item, props } = this;
|
||||||
const start = this.getBookStartDate();
|
const start = this.getBookStartDate();
|
||||||
const end = this.getBookEndDate();
|
const end = this.getBookEndDate();
|
||||||
if (item != null && start != null && end != null) {
|
if (item != null && start != null && end != null) {
|
||||||
|
@ -203,7 +248,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBookEndDate(): Date | null {
|
getBookEndDate(): Date | null {
|
||||||
const {length} = this.bookedDates;
|
const { length } = this.bookedDates;
|
||||||
return length > 0 ? new Date(this.bookedDates[length - 1]) : null;
|
return length > 0 ? new Date(this.bookedDates[length - 1]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +292,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
showDialog = () => {
|
showDialog = () => {
|
||||||
this.setState({dialogVisible: true});
|
this.setState({ dialogVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,14 +333,14 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMarkedSelection() {
|
updateMarkedSelection() {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
markedDates: generateMarkedDates(true, theme, this.bookedDates),
|
markedDates: generateMarkedDates(true, theme, this.bookedDates),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {item, props, state} = this;
|
const { item, props, state } = this;
|
||||||
const start = this.getBookStartDate();
|
const start = this.getBookStartDate();
|
||||||
const end = this.getBookEndDate();
|
const end = this.getBookEndDate();
|
||||||
let subHeadingText;
|
let subHeadingText;
|
||||||
|
@ -315,28 +360,17 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
const isAvailable = isEquipmentAvailable(item);
|
const isAvailable = isEquipmentAvailable(item);
|
||||||
const firstAvailability = getFirstEquipmentAvailability(item);
|
const firstAvailability = getFirstEquipmentAvailability(item);
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<CollapsibleScrollView>
|
<CollapsibleScrollView>
|
||||||
<Card style={{margin: 5}}>
|
<Card style={styles.card}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<View
|
<View style={styles.titleContainer}>
|
||||||
style={{
|
<Headline style={styles.title}>{item.name}</Headline>
|
||||||
marginLeft: 'auto',
|
<Caption style={styles.caption}>
|
||||||
marginRight: 'auto',
|
(
|
||||||
flexDirection: 'row',
|
{i18n.t('screens.equipment.bail', { cost: item.caution })}
|
||||||
flexWrap: 'wrap',
|
)
|
||||||
}}>
|
|
||||||
<Headline style={{textAlign: 'center'}}>
|
|
||||||
{item.name}
|
|
||||||
</Headline>
|
|
||||||
<Caption
|
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
lineHeight: 35,
|
|
||||||
marginLeft: 10,
|
|
||||||
}}>
|
|
||||||
({i18n.t('screens.equipment.bail', {cost: item.caution})})
|
|
||||||
</Caption>
|
</Caption>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -348,17 +382,13 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
? props.theme.colors.success
|
? props.theme.colors.success
|
||||||
: props.theme.colors.primary
|
: props.theme.colors.primary
|
||||||
}
|
}
|
||||||
mode="text">
|
mode="text"
|
||||||
|
>
|
||||||
{i18n.t('screens.equipment.available', {
|
{i18n.t('screens.equipment.available', {
|
||||||
date: getRelativeDateString(firstAvailability),
|
date: getRelativeDateString(firstAvailability),
|
||||||
})}
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<Subheading
|
<Subheading style={styles.subtitle}>
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
marginBottom: 10,
|
|
||||||
minHeight: 50,
|
|
||||||
}}>
|
|
||||||
{subHeadingText}
|
{subHeadingText}
|
||||||
</Subheading>
|
</Subheading>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
|
@ -382,30 +412,30 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
hideArrows={false}
|
hideArrows={false}
|
||||||
// Date marking style [simple/period/multi-dot/custom]. Default = 'simple'
|
// Date marking style [simple/period/multi-dot/custom]. Default = 'simple'
|
||||||
markingType={'period'}
|
markingType={'period'}
|
||||||
markedDates={{...this.lockedDates, ...state.markedDates}}
|
markedDates={{ ...this.lockedDates, ...state.markedDates }}
|
||||||
theme={{
|
theme={{
|
||||||
backgroundColor: props.theme.colors.agendaBackgroundColor,
|
'backgroundColor': props.theme.colors.agendaBackgroundColor,
|
||||||
calendarBackground: props.theme.colors.background,
|
'calendarBackground': props.theme.colors.background,
|
||||||
textSectionTitleColor: props.theme.colors.agendaDayTextColor,
|
'textSectionTitleColor': props.theme.colors.agendaDayTextColor,
|
||||||
selectedDayBackgroundColor: props.theme.colors.primary,
|
'selectedDayBackgroundColor': props.theme.colors.primary,
|
||||||
selectedDayTextColor: '#ffffff',
|
'selectedDayTextColor': '#ffffff',
|
||||||
todayTextColor: props.theme.colors.text,
|
'todayTextColor': props.theme.colors.text,
|
||||||
dayTextColor: props.theme.colors.text,
|
'dayTextColor': props.theme.colors.text,
|
||||||
textDisabledColor: props.theme.colors.agendaDayTextColor,
|
'textDisabledColor': props.theme.colors.agendaDayTextColor,
|
||||||
dotColor: props.theme.colors.primary,
|
'dotColor': props.theme.colors.primary,
|
||||||
selectedDotColor: '#ffffff',
|
'selectedDotColor': '#ffffff',
|
||||||
arrowColor: props.theme.colors.primary,
|
'arrowColor': props.theme.colors.primary,
|
||||||
monthTextColor: props.theme.colors.text,
|
'monthTextColor': props.theme.colors.text,
|
||||||
indicatorColor: props.theme.colors.primary,
|
'indicatorColor': props.theme.colors.primary,
|
||||||
textDayFontFamily: 'monospace',
|
'textDayFontFamily': 'monospace',
|
||||||
textMonthFontFamily: 'monospace',
|
'textMonthFontFamily': 'monospace',
|
||||||
textDayHeaderFontFamily: 'monospace',
|
'textDayHeaderFontFamily': 'monospace',
|
||||||
textDayFontWeight: '300',
|
'textDayFontWeight': '300',
|
||||||
textMonthFontWeight: 'bold',
|
'textMonthFontWeight': 'bold',
|
||||||
textDayHeaderFontWeight: '300',
|
'textDayHeaderFontWeight': '300',
|
||||||
textDayFontSize: 16,
|
'textDayFontSize': 16,
|
||||||
textMonthFontSize: 16,
|
'textMonthFontSize': 16,
|
||||||
textDayHeaderFontSize: 16,
|
'textDayHeaderFontSize': 16,
|
||||||
'stylesheet.day.period': {
|
'stylesheet.day.period': {
|
||||||
base: {
|
base: {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
@ -415,7 +445,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
style={{marginBottom: 50}}
|
style={styles.calendar}
|
||||||
/>
|
/>
|
||||||
</CollapsibleScrollView>
|
</CollapsibleScrollView>
|
||||||
<LoadingConfirmDialog
|
<LoadingConfirmDialog
|
||||||
|
@ -435,26 +465,14 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
||||||
<Animatable.View
|
<Animatable.View
|
||||||
ref={this.bookRef}
|
ref={this.bookRef}
|
||||||
useNativeDriver
|
useNativeDriver
|
||||||
style={{
|
style={styles.buttonContainer}
|
||||||
position: 'absolute',
|
>
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
flex: 1,
|
|
||||||
transform: [{translateY: 100}],
|
|
||||||
}}>
|
|
||||||
<Button
|
<Button
|
||||||
icon="bookmark-check"
|
icon="bookmark-check"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={this.showDialog}
|
onPress={this.showDialog}
|
||||||
style={{
|
style={styles.button}
|
||||||
width: '80%',
|
>
|
||||||
flex: 1,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginBottom: 20,
|
|
||||||
borderRadius: 10,
|
|
||||||
}}>
|
|
||||||
{i18n.t('screens.equipment.bookButton')}
|
{i18n.t('screens.equipment.bookButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, KeyboardAvoidingView, StyleSheet, View} from 'react-native';
|
import { Image, KeyboardAvoidingView, StyleSheet, View } from 'react-native';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
@ -27,16 +27,17 @@ import {
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp, StackScreenProps} from '@react-navigation/stack';
|
import { StackNavigationProp, StackScreenProps } from '@react-navigation/stack';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import ConnectionManager from '../../managers/ConnectionManager';
|
import ConnectionManager from '../../managers/ConnectionManager';
|
||||||
import ErrorDialog from '../../components/Dialogs/ErrorDialog';
|
import ErrorDialog from '../../components/Dialogs/ErrorDialog';
|
||||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
import AvailableWebsites from '../../constants/AvailableWebsites';
|
import AvailableWebsites from '../../constants/AvailableWebsites';
|
||||||
import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
|
||||||
import MascotPopup from '../../components/Mascot/MascotPopup';
|
import MascotPopup from '../../components/Mascot/MascotPopup';
|
||||||
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
||||||
import {MainStackParamsList} from '../../navigation/MainNavigator';
|
import { MainStackParamsList } from '../../navigation/MainNavigator';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type LoginScreenNavigationProp = StackScreenProps<MainStackParamsList, 'login'>;
|
type LoginScreenNavigationProp = StackScreenProps<MainStackParamsList, 'login'>;
|
||||||
|
|
||||||
|
@ -63,9 +64,6 @@ const RESET_PASSWORD_PATH = 'https://www.amicale-insat.fr/password/reset';
|
||||||
const emailRegex = /^.+@.+\..+$/;
|
const emailRegex = /^.+@.+\..+$/;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
card: {
|
card: {
|
||||||
marginTop: 'auto',
|
marginTop: 'auto',
|
||||||
marginBottom: 'auto',
|
marginBottom: 'auto',
|
||||||
|
@ -74,10 +72,18 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 36,
|
fontSize: 36,
|
||||||
marginBottom: 48,
|
marginBottom: 48,
|
||||||
},
|
},
|
||||||
textInput: {},
|
text: {
|
||||||
btnContainer: {
|
color: '#ffffff',
|
||||||
marginTop: 5,
|
},
|
||||||
marginBottom: 10,
|
buttonContainer: {
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
lockButton: {
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
sendButton: {
|
||||||
|
marginLeft: 'auto',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -113,7 +119,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
dialogError: 0,
|
dialogError: 0,
|
||||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.loginShowMascot.key,
|
AsyncStorageManager.PREFERENCES.loginShowMascot.key
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -126,7 +132,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* Navigates to the Amicale website screen with the reset password link as navigation parameters
|
* Navigates to the Amicale website screen with the reset password link as navigation parameters
|
||||||
*/
|
*/
|
||||||
onResetPasswordClick = () => {
|
onResetPasswordClick = () => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('website', {
|
navigation.navigate('website', {
|
||||||
host: AvailableWebsites.websites.AMICALE,
|
host: AvailableWebsites.websites.AMICALE,
|
||||||
path: RESET_PASSWORD_PATH,
|
path: RESET_PASSWORD_PATH,
|
||||||
|
@ -174,15 +180,15 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
const {email, password} = this.state;
|
const { email, password } = this.state;
|
||||||
if (this.shouldEnableLogin()) {
|
if (this.shouldEnableLogin()) {
|
||||||
this.setState({loading: true});
|
this.setState({ loading: true });
|
||||||
ConnectionManager.getInstance()
|
ConnectionManager.getInstance()
|
||||||
.connect(email, password)
|
.connect(email, password)
|
||||||
.then(this.handleSuccess)
|
.then(this.handleSuccess)
|
||||||
.catch(this.showErrorDialog)
|
.catch(this.showErrorDialog)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setState({loading: false});
|
this.setState({ loading: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -193,7 +199,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getFormInput() {
|
getFormInput() {
|
||||||
const {email, password} = this.state;
|
const { email, password } = this.state;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
@ -244,15 +250,15 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getMainCard() {
|
getMainCard() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
title={i18n.t('screens.login.title')}
|
title={i18n.t('screens.login.title')}
|
||||||
titleStyle={{color: '#fff'}}
|
titleStyle={styles.text}
|
||||||
subtitle={i18n.t('screens.login.subtitle')}
|
subtitle={i18n.t('screens.login.subtitle')}
|
||||||
subtitleStyle={{color: '#fff'}}
|
subtitleStyle={styles.text}
|
||||||
left={({size}) => (
|
left={({ size }) => (
|
||||||
<Image
|
<Image
|
||||||
source={ICON_AMICALE}
|
source={ICON_AMICALE}
|
||||||
style={{
|
style={{
|
||||||
|
@ -264,13 +270,14 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
/>
|
/>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
{this.getFormInput()}
|
{this.getFormInput()}
|
||||||
<Card.Actions style={{flexWrap: 'wrap'}}>
|
<Card.Actions style={styles.buttonContainer}>
|
||||||
<Button
|
<Button
|
||||||
icon="lock-question"
|
icon="lock-question"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={this.onResetPasswordClick}
|
onPress={this.onResetPasswordClick}
|
||||||
color={props.theme.colors.warning}
|
color={props.theme.colors.warning}
|
||||||
style={{marginRight: 'auto', marginBottom: 20}}>
|
style={styles.lockButton}
|
||||||
|
>
|
||||||
{i18n.t('screens.login.resetPassword')}
|
{i18n.t('screens.login.resetPassword')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
@ -279,7 +286,8 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
disabled={!this.shouldEnableLogin()}
|
disabled={!this.shouldEnableLogin()}
|
||||||
loading={state.loading}
|
loading={state.loading}
|
||||||
onPress={this.onSubmit}
|
onPress={this.onSubmit}
|
||||||
style={{marginLeft: 'auto'}}>
|
style={styles.sendButton}
|
||||||
|
>
|
||||||
{i18n.t('screens.login.title')}
|
{i18n.t('screens.login.title')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
@ -288,10 +296,8 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
icon="help-circle"
|
icon="help-circle"
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={this.showMascotDialog}
|
onPress={this.showMascotDialog}
|
||||||
style={{
|
style={GENERAL_STYLES.centerHorizontal}
|
||||||
marginLeft: 'auto',
|
>
|
||||||
marginRight: 'auto',
|
|
||||||
}}>
|
|
||||||
{i18n.t('screens.login.mascotDialog.title')}
|
{i18n.t('screens.login.mascotDialog.title')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
@ -304,26 +310,26 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* The user has unfocused the input, his email is ready to be validated
|
* The user has unfocused the input, his email is ready to be validated
|
||||||
*/
|
*/
|
||||||
validateEmail = () => {
|
validateEmail = () => {
|
||||||
this.setState({isEmailValidated: true});
|
this.setState({ isEmailValidated: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user has unfocused the input, his password is ready to be validated
|
* The user has unfocused the input, his password is ready to be validated
|
||||||
*/
|
*/
|
||||||
validatePassword = () => {
|
validatePassword = () => {
|
||||||
this.setState({isPasswordValidated: true});
|
this.setState({ isPasswordValidated: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
hideMascotDialog = () => {
|
hideMascotDialog = () => {
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.loginShowMascot.key,
|
AsyncStorageManager.PREFERENCES.loginShowMascot.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
this.setState({mascotDialogVisible: false});
|
this.setState({ mascotDialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
showMascotDialog = () => {
|
showMascotDialog = () => {
|
||||||
this.setState({mascotDialogVisible: true});
|
this.setState({ mascotDialogVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,7 +345,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
hideErrorDialog = () => {
|
hideErrorDialog = () => {
|
||||||
this.setState({dialogVisible: false});
|
this.setState({ dialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -347,11 +353,11 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* Saves in user preferences to not show the login banner again.
|
* Saves in user preferences to not show the login banner again.
|
||||||
*/
|
*/
|
||||||
handleSuccess = () => {
|
handleSuccess = () => {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
// Do not show the home login banner again
|
// Do not show the home login banner again
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.homeShowMascot.key,
|
AsyncStorageManager.PREFERENCES.homeShowMascot.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
if (this.nextScreen == null) {
|
if (this.nextScreen == null) {
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
|
@ -373,7 +379,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isEmailValid(): boolean {
|
isEmailValid(): boolean {
|
||||||
const {email} = this.state;
|
const { email } = this.state;
|
||||||
return emailRegex.test(email);
|
return emailRegex.test(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +390,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {boolean|boolean}
|
* @returns {boolean|boolean}
|
||||||
*/
|
*/
|
||||||
shouldShowEmailError(): boolean {
|
shouldShowEmailError(): boolean {
|
||||||
const {isEmailValidated} = this.state;
|
const { isEmailValidated } = this.state;
|
||||||
return isEmailValidated && !this.isEmailValid();
|
return isEmailValidated && !this.isEmailValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +400,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isPasswordValid(): boolean {
|
isPasswordValid(): boolean {
|
||||||
const {password} = this.state;
|
const { password } = this.state;
|
||||||
return password !== '';
|
return password !== '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +411,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {boolean|boolean}
|
* @returns {boolean|boolean}
|
||||||
*/
|
*/
|
||||||
shouldShowPasswordError(): boolean {
|
shouldShowPasswordError(): boolean {
|
||||||
const {isPasswordValidated} = this.state;
|
const { isPasswordValidated } = this.state;
|
||||||
return isPasswordValidated && !this.isPasswordValid();
|
return isPasswordValidated && !this.isPasswordValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,28 +421,28 @@ class LoginScreen extends React.Component<Props, StateType> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
shouldEnableLogin(): boolean {
|
shouldEnableLogin(): boolean {
|
||||||
const {loading} = this.state;
|
const { loading } = this.state;
|
||||||
return this.isEmailValid() && this.isPasswordValid() && !loading;
|
return this.isEmailValid() && this.isPasswordValid() && !loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {mascotDialogVisible, dialogVisible, dialogError} = this.state;
|
const { mascotDialogVisible, dialogVisible, dialogError } = this.state;
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
style={{
|
style={GENERAL_STYLES.flex}
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
colors={['#9e0d18', '#530209']}
|
colors={['#9e0d18', '#530209']}
|
||||||
start={{x: 0, y: 0.1}}
|
start={{ x: 0, y: 0.1 }}
|
||||||
end={{x: 0.1, y: 1}}>
|
end={{ x: 0.1, y: 1 }}
|
||||||
|
>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
behavior="height"
|
behavior="height"
|
||||||
contentContainerStyle={styles.container}
|
contentContainerStyle={GENERAL_STYLES.flex}
|
||||||
style={styles.container}
|
style={GENERAL_STYLES.flex}
|
||||||
enabled
|
enabled
|
||||||
keyboardVerticalOffset={100}>
|
keyboardVerticalOffset={100}
|
||||||
|
>
|
||||||
<CollapsibleScrollView>
|
<CollapsibleScrollView>
|
||||||
<View style={{height: '100%'}}>{this.getMainCard()}</View>
|
<View style={GENERAL_STYLES.flex}>{this.getMainCard()}</View>
|
||||||
<MascotPopup
|
<MascotPopup
|
||||||
visible={mascotDialogVisible}
|
visible={mascotDialogVisible}
|
||||||
title={i18n.t('screens.login.mascotDialog.title')}
|
title={i18n.t('screens.login.mascotDialog.title')}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList, StyleSheet, View} from 'react-native';
|
import { FlatList, StyleSheet, View } from 'react-native';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
|
@ -29,7 +29,7 @@ import {
|
||||||
withTheme,
|
withTheme,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen';
|
import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen';
|
||||||
import LogoutDialog from '../../components/Amicale/LogoutDialog';
|
import LogoutDialog from '../../components/Amicale/LogoutDialog';
|
||||||
import MaterialHeaderButtons, {
|
import MaterialHeaderButtons, {
|
||||||
|
@ -37,10 +37,11 @@ import MaterialHeaderButtons, {
|
||||||
} from '../../components/Overrides/CustomHeaderButton';
|
} from '../../components/Overrides/CustomHeaderButton';
|
||||||
import CardList from '../../components/Lists/CardList/CardList';
|
import CardList from '../../components/Lists/CardList/CardList';
|
||||||
import AvailableWebsites from '../../constants/AvailableWebsites';
|
import AvailableWebsites from '../../constants/AvailableWebsites';
|
||||||
import Mascot, {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
import Mascot, { MASCOT_STYLE } from '../../components/Mascot/Mascot';
|
||||||
import ServicesManager, {SERVICES_KEY} from '../../managers/ServicesManager';
|
import ServicesManager, { SERVICES_KEY } from '../../managers/ServicesManager';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
import type {ServiceItemType} from '../../managers/ServicesManager';
|
import type { ServiceItemType } from '../../managers/ServicesManager';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
navigation: StackNavigationProp<any>;
|
navigation: StackNavigationProp<any>;
|
||||||
|
@ -79,19 +80,25 @@ const styles = StyleSheet.create({
|
||||||
editButton: {
|
editButton: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
},
|
},
|
||||||
|
mascot: {
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class ProfileScreen extends React.Component<PropsType, StateType> {
|
class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
data: ProfileDataType | null;
|
data: ProfileDataType | null;
|
||||||
|
|
||||||
flatListData: Array<{id: string}>;
|
flatListData: Array<{ id: string }>;
|
||||||
|
|
||||||
amicaleDataset: Array<ServiceItemType>;
|
amicaleDataset: Array<ServiceItemType>;
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
constructor(props: PropsType) {
|
||||||
super(props);
|
super(props);
|
||||||
this.data = null;
|
this.data = null;
|
||||||
this.flatListData = [{id: '0'}, {id: '1'}, {id: '2'}, {id: '3'}];
|
this.flatListData = [{ id: '0' }, { id: '1' }, { id: '2' }, { id: '3' }];
|
||||||
const services = new ServicesManager(props.navigation);
|
const services = new ServicesManager(props.navigation);
|
||||||
this.amicaleDataset = services.getAmicaleServices([SERVICES_KEY.PROFILE]);
|
this.amicaleDataset = services.getAmicaleServices([SERVICES_KEY.PROFILE]);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -100,7 +107,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerRight: this.getHeaderButton,
|
headerRight: this.getHeaderButton,
|
||||||
});
|
});
|
||||||
|
@ -128,10 +135,10 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getScreen = (data: Array<ProfileDataType | null>) => {
|
getScreen = (data: Array<ProfileDataType | null>) => {
|
||||||
const {dialogVisible} = this.state;
|
const { dialogVisible } = this.state;
|
||||||
this.data = data[0];
|
this.data = data[0];
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<CollapsibleFlatList
|
<CollapsibleFlatList
|
||||||
renderItem={this.getRenderItem}
|
renderItem={this.getRenderItem}
|
||||||
data={this.flatListData}
|
data={this.flatListData}
|
||||||
|
@ -144,7 +151,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderItem = ({item}: {item: {id: string}}) => {
|
getRenderItem = ({ item }: { item: { id: string } }) => {
|
||||||
switch (item.id) {
|
switch (item.id) {
|
||||||
case '0':
|
case '0':
|
||||||
return this.getWelcomeCard();
|
return this.getWelcomeCard();
|
||||||
|
@ -172,7 +179,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getWelcomeCard() {
|
getWelcomeCard() {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -181,9 +188,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
})}
|
})}
|
||||||
left={() => (
|
left={() => (
|
||||||
<Mascot
|
<Mascot
|
||||||
style={{
|
style={styles.mascot}
|
||||||
width: 60,
|
|
||||||
}}
|
|
||||||
emotion={MASCOT_STYLE.COOL}
|
emotion={MASCOT_STYLE.COOL}
|
||||||
animated
|
animated
|
||||||
entryAnimation={{
|
entryAnimation={{
|
||||||
|
@ -192,7 +197,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
titleStyle={{marginLeft: 10}}
|
titleStyle={styles.title}
|
||||||
/>
|
/>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -207,7 +212,8 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.navigate('feedback');
|
navigation.navigate('feedback');
|
||||||
}}
|
}}
|
||||||
style={styles.editButton}>
|
style={styles.editButton}
|
||||||
|
>
|
||||||
{i18n.t('screens.feedback.homeButtonTitle')}
|
{i18n.t('screens.feedback.homeButtonTitle')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
@ -235,7 +241,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getPersonalListItem(field: string | undefined, icon: string) {
|
getPersonalListItem(field: string | undefined, icon: string) {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
const title = field != null ? ProfileScreen.getFieldValue(field) : ':(';
|
const title = field != null ? ProfileScreen.getFieldValue(field) : ':(';
|
||||||
const subtitle = field != null ? '' : ProfileScreen.getFieldValue(field);
|
const subtitle = field != null ? '' : ProfileScreen.getFieldValue(field);
|
||||||
return (
|
return (
|
||||||
|
@ -259,7 +265,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getPersonalCard() {
|
getPersonalCard() {
|
||||||
const {theme, navigation} = this.props;
|
const { theme, navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -297,7 +303,8 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
title: i18n.t('screens.websites.amicale'),
|
title: i18n.t('screens.websites.amicale'),
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
style={styles.editButton}>
|
style={styles.editButton}
|
||||||
|
>
|
||||||
{i18n.t('screens.profile.editInformation')}
|
{i18n.t('screens.profile.editInformation')}
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Actions>
|
</Card.Actions>
|
||||||
|
@ -312,7 +319,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getClubCard() {
|
getClubCard() {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -341,7 +348,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getMembershipCar() {
|
getMembershipCar() {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -371,7 +378,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getMembershipItem(state: boolean) {
|
getMembershipItem(state: boolean) {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={
|
title={
|
||||||
|
@ -396,8 +403,8 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @param item The club to render
|
* @param item The club to render
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getClubListItem = ({item}: {item: ClubType}) => {
|
getClubListItem = ({ item }: { item: ClubType }) => {
|
||||||
const {theme} = this.props;
|
const { theme } = this.props;
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
this.openClubDetailsScreen(item.id);
|
this.openClubDetailsScreen(item.id);
|
||||||
};
|
};
|
||||||
|
@ -458,11 +465,11 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
sortClubList = (a: ClubType): number => (a.is_manager ? -1 : 1);
|
sortClubList = (a: ClubType): number => (a.is_manager ? -1 : 1);
|
||||||
|
|
||||||
showDisconnectDialog = () => {
|
showDisconnectDialog = () => {
|
||||||
this.setState({dialogVisible: true});
|
this.setState({ dialogVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
hideDisconnectDialog = () => {
|
hideDisconnectDialog = () => {
|
||||||
this.setState({dialogVisible: false});
|
this.setState({ dialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -470,12 +477,12 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||||
* @param id The club's id to open
|
* @param id The club's id to open
|
||||||
*/
|
*/
|
||||||
openClubDetailsScreen(id: number) {
|
openClubDetailsScreen(id: number) {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('club-information', {clubId: id});
|
navigation.navigate('club-information', { clubId: id });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {navigation} = this.props;
|
const { navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
|
|
@ -18,21 +18,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {RefreshControl, View} from 'react-native';
|
import { RefreshControl, StyleSheet, View } from 'react-native';
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Button} from 'react-native-paper';
|
import { Button } from 'react-native-paper';
|
||||||
import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen';
|
import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen';
|
||||||
import {getTimeOnlyString, stringToDate} from '../../utils/Planning';
|
import { getTimeOnlyString, stringToDate } from '../../utils/Planning';
|
||||||
import VoteTease from '../../components/Amicale/Vote/VoteTease';
|
import VoteTease from '../../components/Amicale/Vote/VoteTease';
|
||||||
import VoteSelect from '../../components/Amicale/Vote/VoteSelect';
|
import VoteSelect from '../../components/Amicale/Vote/VoteSelect';
|
||||||
import VoteResults from '../../components/Amicale/Vote/VoteResults';
|
import VoteResults from '../../components/Amicale/Vote/VoteResults';
|
||||||
import VoteWait from '../../components/Amicale/Vote/VoteWait';
|
import VoteWait from '../../components/Amicale/Vote/VoteWait';
|
||||||
import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
|
||||||
import MascotPopup from '../../components/Mascot/MascotPopup';
|
import MascotPopup from '../../components/Mascot/MascotPopup';
|
||||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
|
import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
|
||||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
import GENERAL_STYLES from '../../constants/Styles';
|
||||||
|
|
||||||
export type VoteTeamType = {
|
export type VoteTeamType = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -118,6 +119,14 @@ type StateType = {
|
||||||
mascotDialogVisible: boolean;
|
mascotDialogVisible: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Screen displaying vote information and controls
|
* Screen displaying vote information and controls
|
||||||
*/
|
*/
|
||||||
|
@ -132,11 +141,11 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
|
|
||||||
today: Date;
|
today: Date;
|
||||||
|
|
||||||
mainFlatListData: Array<{key: string}>;
|
mainFlatListData: Array<{ key: string }>;
|
||||||
|
|
||||||
lastRefresh: Date | null;
|
lastRefresh: Date | null;
|
||||||
|
|
||||||
authRef: {current: null | AuthenticatedScreen<any>};
|
authRef: { current: null | AuthenticatedScreen<any> };
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
constructor(props: PropsType) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -146,14 +155,14 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
this.state = {
|
this.state = {
|
||||||
hasVoted: false,
|
hasVoted: false,
|
||||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||||
AsyncStorageManager.PREFERENCES.voteShowMascot.key,
|
AsyncStorageManager.PREFERENCES.voteShowMascot.key
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
this.hasVoted = false;
|
this.hasVoted = false;
|
||||||
this.today = new Date();
|
this.today = new Date();
|
||||||
this.authRef = React.createRef();
|
this.authRef = React.createRef();
|
||||||
this.lastRefresh = null;
|
this.lastRefresh = null;
|
||||||
this.mainFlatListData = [{key: 'main'}, {key: 'info'}];
|
this.mainFlatListData = [{ key: 'main' }, { key: 'info' }];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,7 +183,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
return dateString;
|
return dateString;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMainRenderItem = ({item}: {item: {key: string}}) => {
|
getMainRenderItem = ({ item }: { item: { key: string } }) => {
|
||||||
if (item.key === 'info') {
|
if (item.key === 'info') {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -182,11 +191,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="help-circle"
|
icon="help-circle"
|
||||||
onPress={this.showMascotDialog}
|
onPress={this.showMascotDialog}
|
||||||
style={{
|
style={styles.button}
|
||||||
marginLeft: 'auto',
|
>
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 20,
|
|
||||||
}}>
|
|
||||||
{i18n.t('screens.vote.mascotDialog.title')}
|
{i18n.t('screens.vote.mascotDialog.title')}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
@ -196,7 +202,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getScreen = (data: Array<TeamResponseType | VoteDatesStringType | null>) => {
|
getScreen = (data: Array<TeamResponseType | VoteDatesStringType | null>) => {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
// data[0] = FAKE_TEAMS2;
|
// data[0] = FAKE_TEAMS2;
|
||||||
// data[1] = FAKE_DATE;
|
// data[1] = FAKE_DATE;
|
||||||
this.lastRefresh = new Date();
|
this.lastRefresh = new Date();
|
||||||
|
@ -229,7 +235,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getContent() {
|
getContent() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
if (!this.isVoteStarted()) {
|
if (!this.isVoteStarted()) {
|
||||||
return this.getTeaseVoteCard();
|
return this.getTeaseVoteCard();
|
||||||
}
|
}
|
||||||
|
@ -245,7 +251,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
return <VoteNotAvailable />;
|
return <VoteNotAvailable />;
|
||||||
}
|
}
|
||||||
|
|
||||||
onVoteSuccess = (): void => this.setState({hasVoted: true});
|
onVoteSuccess = (): void => this.setState({ hasVoted: true });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user has not voted yet, and the votes are open
|
* The user has not voted yet, and the votes are open
|
||||||
|
@ -270,7 +276,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
teams={this.teams}
|
teams={this.teams}
|
||||||
dateEnd={this.getDateString(
|
dateEnd={this.getDateString(
|
||||||
this.dates.date_result_end,
|
this.dates.date_result_end,
|
||||||
this.datesString.date_result_end,
|
this.datesString.date_result_end
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -287,7 +293,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
<VoteTease
|
<VoteTease
|
||||||
startDate={this.getDateString(
|
startDate={this.getDateString(
|
||||||
this.dates.date_begin,
|
this.dates.date_begin,
|
||||||
this.datesString.date_begin,
|
this.datesString.date_begin
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -299,7 +305,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
* Votes have ended, or user has voted waiting for results
|
* Votes have ended, or user has voted waiting for results
|
||||||
*/
|
*/
|
||||||
getWaitVoteCard() {
|
getWaitVoteCard() {
|
||||||
const {state} = this;
|
const { state } = this;
|
||||||
let startDate = null;
|
let startDate = null;
|
||||||
if (
|
if (
|
||||||
this.dates != null &&
|
this.dates != null &&
|
||||||
|
@ -308,7 +314,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
) {
|
) {
|
||||||
startDate = this.getDateString(
|
startDate = this.getDateString(
|
||||||
this.dates.date_result_begin,
|
this.dates.date_result_begin,
|
||||||
this.datesString.date_result_begin,
|
this.datesString.date_result_begin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -326,7 +332,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
*/
|
*/
|
||||||
reloadData = () => {
|
reloadData = () => {
|
||||||
let canRefresh;
|
let canRefresh;
|
||||||
const {lastRefresh} = this;
|
const { lastRefresh } = this;
|
||||||
if (lastRefresh != null) {
|
if (lastRefresh != null) {
|
||||||
canRefresh =
|
canRefresh =
|
||||||
new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME;
|
new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME;
|
||||||
|
@ -339,15 +345,15 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
showMascotDialog = () => {
|
showMascotDialog = () => {
|
||||||
this.setState({mascotDialogVisible: true});
|
this.setState({ mascotDialogVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
hideMascotDialog = () => {
|
hideMascotDialog = () => {
|
||||||
AsyncStorageManager.set(
|
AsyncStorageManager.set(
|
||||||
AsyncStorageManager.PREFERENCES.voteShowMascot.key,
|
AsyncStorageManager.PREFERENCES.voteShowMascot.key,
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
this.setState({mascotDialogVisible: false});
|
this.setState({ mascotDialogVisible: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
isVoteStarted(): boolean {
|
isVoteStarted(): boolean {
|
||||||
|
@ -412,9 +418,9 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const {props, state} = this;
|
const { props, state } = this;
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<AuthenticatedScreen<TeamResponseType | VoteDatesStringType>
|
<AuthenticatedScreen<TeamResponseType | VoteDatesStringType>
|
||||||
navigation={props.navigation}
|
navigation={props.navigation}
|
||||||
ref={this.authRef}
|
ref={this.authRef}
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @flow
|
|
||||||
|
|
||||||
export type CoordinatesType = {
|
export type CoordinatesType = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -49,7 +47,7 @@ export default class BaseShape {
|
||||||
}
|
}
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
this.#rotation = 0;
|
this.#rotation = 0;
|
||||||
this.position = {x: 0, y: 0};
|
this.position = { x: 0, y: 0 };
|
||||||
this.#currentShape = this.getShapes()[this.#rotation];
|
this.#currentShape = this.getShapes()[this.#rotation];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +94,7 @@ export default class BaseShape {
|
||||||
y: this.position.y + row,
|
y: this.position.y + row,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
coordinates.push({x: col, y: row});
|
coordinates.push({ x: col, y: row });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseShape from './BaseShape';
|
import BaseShape from './BaseShape';
|
||||||
import type {ShapeType} from './BaseShape';
|
import type { ShapeType } from './BaseShape';
|
||||||
|
|
||||||
export default class ShapeI extends BaseShape {
|
export default class ShapeI extends BaseShape {
|
||||||
constructor(theme: ReactNativePaper.Theme) {
|
constructor(theme: ReactNativePaper.Theme) {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseShape from './BaseShape';
|
import BaseShape from './BaseShape';
|
||||||
import type {ShapeType} from './BaseShape';
|
import type { ShapeType } from './BaseShape';
|
||||||
|
|
||||||
export default class ShapeJ extends BaseShape {
|
export default class ShapeJ extends BaseShape {
|
||||||
constructor(theme: ReactNativePaper.Theme) {
|
constructor(theme: ReactNativePaper.Theme) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue