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"
|
||||
}
|
25
App.tsx
25
App.tsx
|
@ -17,7 +17,7 @@
|
|||
* 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 { NavigationContainer } from '@react-navigation/native';
|
||||
import { Provider as PaperProvider } from 'react-native-paper';
|
||||
|
@ -36,6 +36,7 @@ import URLHandler from './src/utils/URLHandler';
|
|||
import { setupStatusBar } from './src/utils/Utils';
|
||||
import initLocales from './src/utils/Locales';
|
||||
import { NavigationContainerRef } from '@react-navigation/core';
|
||||
import GENERAL_STYLES from './src/constants/Styles';
|
||||
|
||||
// Native optimizations https://reactnavigation.org/docs/react-native-screens
|
||||
// Crashes app when navigating away from webview on android 9+
|
||||
|
@ -132,15 +133,15 @@ export default class App extends React.Component<{}, StateType> {
|
|||
});
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.showIntro.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.updateNumber.key,
|
||||
Update.number,
|
||||
Update.number
|
||||
);
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -161,16 +162,16 @@ export default class App extends React.Component<{}, StateType> {
|
|||
isLoading: false,
|
||||
currentTheme: ThemeManager.getCurrentTheme(),
|
||||
showIntro: AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.showIntro.key,
|
||||
AsyncStorageManager.PREFERENCES.showIntro.key
|
||||
),
|
||||
showUpdate:
|
||||
AsyncStorageManager.getNumber(
|
||||
AsyncStorageManager.PREFERENCES.updateNumber.key,
|
||||
AsyncStorageManager.PREFERENCES.updateNumber.key
|
||||
) !== Update.number,
|
||||
showAprilFools:
|
||||
AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
|
||||
AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
|
||||
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key
|
||||
),
|
||||
});
|
||||
SplashScreen.hide();
|
||||
|
@ -213,12 +214,14 @@ export default class App extends React.Component<{}, StateType> {
|
|||
<View
|
||||
style={{
|
||||
backgroundColor: ThemeManager.getCurrentTheme().colors.background,
|
||||
flex: 1,
|
||||
}}>
|
||||
<SafeAreaView style={{flex: 1}}>
|
||||
...GENERAL_STYLES.flex,
|
||||
}}
|
||||
>
|
||||
<SafeAreaView style={GENERAL_STYLES.flex}>
|
||||
<NavigationContainer
|
||||
theme={state.currentTheme}
|
||||
ref={this.navigatorRef}>
|
||||
ref={this.navigatorRef}
|
||||
>
|
||||
<MainNavigator
|
||||
defaultHomeRoute={this.defaultHomeRoute}
|
||||
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 = {
|
||||
SECURITY_LEVEL_ANY: "MOCK_SECURITY_LEVEL_ANY",
|
||||
SECURITY_LEVEL_SECURE_SOFTWARE: "MOCK_SECURITY_LEVEL_SECURE_SOFTWARE",
|
||||
SECURITY_LEVEL_SECURE_HARDWARE: "MOCK_SECURITY_LEVEL_SECURE_HARDWARE",
|
||||
}
|
||||
SECURITY_LEVEL_ANY: 'MOCK_SECURITY_LEVEL_ANY',
|
||||
SECURITY_LEVEL_SECURE_SOFTWARE: 'MOCK_SECURITY_LEVEL_SECURE_SOFTWARE',
|
||||
SECURITY_LEVEL_SECURE_HARDWARE: 'MOCK_SECURITY_LEVEL_SECURE_HARDWARE',
|
||||
};
|
||||
|
||||
export default keychainMock;
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import ConnectionManager from '../../src/managers/ConnectionManager';
|
||||
import { ERROR_TYPE } from '../../src/utils/WebData';
|
||||
|
||||
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 c = ConnectionManager.getInstance();
|
||||
|
@ -44,7 +42,7 @@ test('connect bad credentials', () => {
|
|||
});
|
||||
});
|
||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||
ERROR_TYPE.BAD_CREDENTIALS,
|
||||
ERROR_TYPE.BAD_CREDENTIALS
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -79,7 +77,7 @@ test('connect good credentials no consent', () => {
|
|||
});
|
||||
});
|
||||
return expect(c.connect('email', 'password')).rejects.toBe(
|
||||
ERROR_TYPE.NO_CONSENT,
|
||||
ERROR_TYPE.NO_CONSENT
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -100,7 +98,7 @@ test('connect good credentials, fail save token', () => {
|
|||
return Promise.reject(false);
|
||||
});
|
||||
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 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(
|
||||
ERROR_TYPE.SERVER_ERROR,
|
||||
ERROR_TYPE.SERVER_ERROR
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -146,7 +144,7 @@ test('authenticatedRequest success', () => {
|
|||
});
|
||||
});
|
||||
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' });
|
||||
});
|
||||
|
||||
|
@ -167,7 +165,7 @@ test('authenticatedRequest error wrong token', () => {
|
|||
});
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -187,7 +185,7 @@ test('authenticatedRequest error bogus response', () => {
|
|||
});
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -201,7 +199,7 @@ test('authenticatedRequest connection error', () => {
|
|||
return Promise.reject();
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -212,6 +210,6 @@ test('authenticatedRequest error no token', () => {
|
|||
return null;
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import * as EquipmentBooking from '../../src/utils/EquipmentBooking';
|
||||
import i18n from 'i18n-js';
|
||||
|
||||
|
@ -18,7 +15,7 @@ test('getCurrentDay', () => {
|
|||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date('2020-01-14 14:50:35').getTime());
|
||||
expect(EquipmentBooking.getCurrentDay().getTime()).toBe(
|
||||
new Date('2020-01-14').getTime(),
|
||||
new Date('2020-01-14').getTime()
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -58,18 +55,18 @@ test('getFirstEquipmentAvailability', () => {
|
|||
booked_at: [{ begin: '2020-07-07', end: '2020-07-10' }],
|
||||
};
|
||||
expect(
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||
).toBe(new Date('2020-07-11').getTime());
|
||||
testDevice.booked_at = [{ begin: '2020-07-07', end: '2020-07-09' }];
|
||||
expect(
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||
).toBe(new Date('2020-07-10').getTime());
|
||||
testDevice.booked_at = [
|
||||
{ begin: '2020-07-07', end: '2020-07-09' },
|
||||
{ begin: '2020-07-10', end: '2020-07-16' },
|
||||
];
|
||||
expect(
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||
).toBe(new Date('2020-07-17').getTime());
|
||||
testDevice.booked_at = [
|
||||
{ begin: '2020-07-07', end: '2020-07-09' },
|
||||
|
@ -77,7 +74,7 @@ test('getFirstEquipmentAvailability', () => {
|
|||
{ begin: '2020-07-14', end: '2020-07-16' },
|
||||
];
|
||||
expect(
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime(),
|
||||
EquipmentBooking.getFirstEquipmentAvailability(testDevice).getTime()
|
||||
).toBe(new Date('2020-07-13').getTime());
|
||||
});
|
||||
|
||||
|
@ -85,7 +82,7 @@ test('getRelativeDateString', () => {
|
|||
jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date('2020-07-09').getTime());
|
||||
jest.spyOn(i18n, 't').mockImplementation((translationString: string) => {
|
||||
jest.spyOn(i18n, 't').mockImplementation((translationString) => {
|
||||
const prefix = 'screens.equipment.';
|
||||
if (translationString === prefix + 'otherYear') return '0';
|
||||
else if (translationString === prefix + 'otherMonth') return '1';
|
||||
|
@ -95,25 +92,25 @@ test('getRelativeDateString', () => {
|
|||
else return null;
|
||||
});
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-09'))).toBe(
|
||||
'4',
|
||||
'4'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-10'))).toBe(
|
||||
'3',
|
||||
'3'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-11'))).toBe(
|
||||
'2',
|
||||
'2'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-07-30'))).toBe(
|
||||
'2',
|
||||
'2'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-08-30'))).toBe(
|
||||
'1',
|
||||
'1'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2020-11-10'))).toBe(
|
||||
'1',
|
||||
'1'
|
||||
);
|
||||
expect(EquipmentBooking.getRelativeDateString(new Date('2021-11-10'))).toBe(
|
||||
'0',
|
||||
'0'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -134,7 +131,7 @@ test('getValidRange', () => {
|
|||
'2020-07-15',
|
||||
];
|
||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
testDevice.booked_at = [
|
||||
{ begin: '2020-07-07', end: '2020-07-10' },
|
||||
|
@ -142,43 +139,43 @@ test('getValidRange', () => {
|
|||
];
|
||||
result = ['2020-07-11', '2020-07-12'];
|
||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
|
||||
testDevice.booked_at = [{ begin: '2020-07-12', end: '2020-07-13' }];
|
||||
result = ['2020-07-11'];
|
||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
testDevice.booked_at = [{ begin: '2020-07-07', end: '2020-07-12' }];
|
||||
result = ['2020-07-13', '2020-07-14', '2020-07-15'];
|
||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
start = new Date('2020-07-14');
|
||||
end = new Date('2020-07-14');
|
||||
result = ['2020-07-14'];
|
||||
expect(
|
||||
EquipmentBooking.getValidRange(start, start, testDevice),
|
||||
EquipmentBooking.getValidRange(start, start, testDevice)
|
||||
).toStrictEqual(result);
|
||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
|
||||
start = new Date('2020-07-14');
|
||||
end = new Date('2020-07-17');
|
||||
result = ['2020-07-14', '2020-07-15', '2020-07-16', '2020-07-17'];
|
||||
expect(EquipmentBooking.getValidRange(start, end, null)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
|
||||
testDevice.booked_at = [{ begin: '2020-07-17', end: '2020-07-17' }];
|
||||
result = ['2020-07-14', '2020-07-15', '2020-07-16'];
|
||||
expect(EquipmentBooking.getValidRange(start, end, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
|
||||
testDevice.booked_at = [
|
||||
|
@ -189,7 +186,7 @@ test('getValidRange', () => {
|
|||
end = new Date('2020-07-23');
|
||||
result = ['2020-07-21', '2020-07-22', '2020-07-23'];
|
||||
expect(EquipmentBooking.getValidRange(end, start, testDevice)).toStrictEqual(
|
||||
result,
|
||||
result
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -228,7 +225,7 @@ test('generateMarkedDates', () => {
|
|||
},
|
||||
};
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||
).toStrictEqual(result);
|
||||
result = {
|
||||
'2020-07-11': {
|
||||
|
@ -248,7 +245,7 @@ test('generateMarkedDates', () => {
|
|||
},
|
||||
};
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(false, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(false, theme, range)
|
||||
).toStrictEqual(result);
|
||||
result = {
|
||||
'2020-07-11': {
|
||||
|
@ -269,7 +266,7 @@ test('generateMarkedDates', () => {
|
|||
};
|
||||
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(false, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(false, theme, range)
|
||||
).toStrictEqual(result);
|
||||
|
||||
testDevice.booked_at = [{ begin: '2020-07-13', end: '2020-07-15' }];
|
||||
|
@ -287,7 +284,7 @@ test('generateMarkedDates', () => {
|
|||
};
|
||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||
).toStrictEqual(result);
|
||||
|
||||
testDevice.booked_at = [{ begin: '2020-07-12', end: '2020-07-13' }];
|
||||
|
@ -300,7 +297,7 @@ test('generateMarkedDates', () => {
|
|||
};
|
||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||
).toStrictEqual(result);
|
||||
|
||||
testDevice.booked_at = [
|
||||
|
@ -318,7 +315,7 @@ test('generateMarkedDates', () => {
|
|||
};
|
||||
range = EquipmentBooking.getValidRange(start, end, testDevice);
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||
).toStrictEqual(result);
|
||||
|
||||
result = {
|
||||
|
@ -340,6 +337,6 @@ test('generateMarkedDates', () => {
|
|||
};
|
||||
range = EquipmentBooking.getValidRange(end, start, testDevice);
|
||||
expect(
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range),
|
||||
EquipmentBooking.generateMarkedDates(true, theme, range)
|
||||
).toStrictEqual(result);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import * as Planning from '../../src/utils/Planning';
|
||||
|
||||
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:00'),
|
||||
Planning.isEventDateStringFormatValid('3214-64-12 01:16:00')
|
||||
).toBeFalse();
|
||||
expect(Planning.isEventDateStringFormatValid('3214-64-12 1: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 truc')).toBeFalse();
|
||||
expect(
|
||||
Planning.isEventDateStringFormatValid('3214-64-12 1:16:65'),
|
||||
Planning.isEventDateStringFormatValid('3214-64-12 1:16:65')
|
||||
).toBeFalse();
|
||||
expect(Planning.isEventDateStringFormatValid('garbage')).toBeFalse();
|
||||
expect(Planning.isEventDateStringFormatValid('')).toBeFalse();
|
||||
|
@ -65,17 +62,17 @@ test('getFormattedEventTime', () => {
|
|||
expect(Planning.getFormattedEventTime(undefined, undefined)).toBe('/ - /');
|
||||
expect(Planning.getFormattedEventTime('20:30', '23:00')).toBe('/ - /');
|
||||
expect(Planning.getFormattedEventTime('2020-03-30', '2020-03-31')).toBe(
|
||||
'/ - /',
|
||||
'/ - /'
|
||||
);
|
||||
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
});
|
||||
|
||||
|
@ -90,38 +87,38 @@ test('getDateOnlyString', () => {
|
|||
|
||||
test('isEventBefore', () => {
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
|
||||
expect(Planning.isEventBefore('garbage', '2020-03-21 10:15')).toBeFalse();
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
getCleanedMachineWatched,
|
||||
getMachineEndDate,
|
||||
|
@ -16,13 +13,13 @@ test('getMachineEndDate', () => {
|
|||
expectDate.setHours(23);
|
||||
expectDate.setMinutes(10);
|
||||
expect(getMachineEndDate({ endTime: '23:10' }).getTime()).toBe(
|
||||
expectDate.getTime(),
|
||||
expectDate.getTime()
|
||||
);
|
||||
|
||||
expectDate.setHours(16);
|
||||
expectDate.setMinutes(30);
|
||||
expect(getMachineEndDate({ endTime: '16:30' }).getTime()).toBe(
|
||||
expectDate.getTime(),
|
||||
expectDate.getTime()
|
||||
);
|
||||
|
||||
expect(getMachineEndDate({ endTime: '15:30' })).toBeNull();
|
||||
|
@ -36,7 +33,7 @@ test('getMachineEndDate', () => {
|
|||
expectDate.setHours(0);
|
||||
expectDate.setMinutes(30);
|
||||
expect(getMachineEndDate({ endTime: '00:30' }).getTime()).toBe(
|
||||
expectDate.getTime(),
|
||||
expectDate.getTime()
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -52,16 +49,16 @@ test('isMachineWatched', () => {
|
|||
},
|
||||
];
|
||||
expect(
|
||||
isMachineWatched({number: '0', endTime: '23:30'}, machineList),
|
||||
isMachineWatched({ number: '0', endTime: '23:30' }, machineList)
|
||||
).toBeTrue();
|
||||
expect(
|
||||
isMachineWatched({number: '1', endTime: '20:30'}, machineList),
|
||||
isMachineWatched({ number: '1', endTime: '20:30' }, machineList)
|
||||
).toBeTrue();
|
||||
expect(
|
||||
isMachineWatched({number: '3', endTime: '20:30'}, machineList),
|
||||
isMachineWatched({ number: '3', endTime: '20:30' }, machineList)
|
||||
).toBeFalse();
|
||||
expect(
|
||||
isMachineWatched({number: '1', endTime: '23:30'}, machineList),
|
||||
isMachineWatched({ number: '1', endTime: '23:30' }, machineList)
|
||||
).toBeFalse();
|
||||
});
|
||||
|
||||
|
@ -110,7 +107,7 @@ test('getCleanedMachineWatched', () => {
|
|||
];
|
||||
let cleanedList = watchList;
|
||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||
cleanedList,
|
||||
cleanedList
|
||||
);
|
||||
|
||||
watchList = [
|
||||
|
@ -138,7 +135,7 @@ test('getCleanedMachineWatched', () => {
|
|||
},
|
||||
];
|
||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||
cleanedList,
|
||||
cleanedList
|
||||
);
|
||||
|
||||
watchList = [
|
||||
|
@ -162,6 +159,6 @@ test('getCleanedMachineWatched', () => {
|
|||
},
|
||||
];
|
||||
expect(getCleanedMachineWatched(watchList, machineList)).toStrictEqual(
|
||||
cleanedList,
|
||||
cleanedList
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/* eslint-disable */
|
||||
|
||||
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
|
||||
|
||||
test('isRequestResponseValid', () => {
|
||||
|
|
|
@ -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"
|
||||
|
1
index.js
1
index.js
|
@ -25,5 +25,4 @@ import {AppRegistry} from 'react-native';
|
|||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
|
||||
// eslint-disable-next-line flowtype/require-return-type
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
|
||||
module.exports = {
|
||||
transformer: {
|
||||
// eslint-disable-next-line flowtype/require-return-type
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
|
4670
package-lock.json
generated
4670
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",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "react-native start",
|
||||
"android": "react-native run-android",
|
||||
"android-release": "react-native run-android --variant=release",
|
||||
"ios": "react-native run-ios",
|
||||
"start": "react-native start",
|
||||
"start-no-cache": "react-native start --reset-cache",
|
||||
"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": {
|
||||
"preset": "react-native",
|
||||
|
@ -23,71 +124,5 @@
|
|||
"setupFilesAfterEnv": [
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,11 +176,11 @@ class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
|
|||
this.connectionManager
|
||||
.authenticatedRequest<T>(
|
||||
props.requests[i].link,
|
||||
props.requests[i].params,
|
||||
props.requests[i].params
|
||||
)
|
||||
.then((response: T): void => this.onRequestFinished(response, i))
|
||||
.catch((error: number): void =>
|
||||
this.onRequestFinished(null, i, error),
|
||||
this.onRequestFinished(null, i, error)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -18,24 +18,31 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { Headline, useTheme } from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
headline: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
function VoteNotAvailable() {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<View style={styles.container}>
|
||||
<Headline
|
||||
style={{
|
||||
color: theme.colors.textDisabled,
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
...styles.headline,
|
||||
}}
|
||||
>
|
||||
{i18n.t('screens.vote.noVote')}
|
||||
</Headline>
|
||||
</View>
|
||||
|
|
|
@ -40,8 +40,11 @@ const styles = StyleSheet.create({
|
|||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
itemCard: {
|
||||
marginTop: 10,
|
||||
},
|
||||
item: {
|
||||
padding: 0,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -90,12 +93,14 @@ class VoteResults extends React.Component<PropsType> {
|
|||
const isWinner = this.winnerIds.indexOf(item.id) !== -1;
|
||||
const isDraw = this.winnerIds.length > 1;
|
||||
const { props } = this;
|
||||
const elevation = isWinner ? 5 : 3;
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
marginTop: 10,
|
||||
elevation: isWinner ? 5 : 3,
|
||||
}}>
|
||||
...styles.itemCard,
|
||||
elevation: elevation,
|
||||
}}
|
||||
>
|
||||
<List.Item
|
||||
title={item.name}
|
||||
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.text,
|
||||
}}
|
||||
style={{padding: 0}}
|
||||
style={styles.item}
|
||||
/>
|
||||
<ProgressBar
|
||||
progress={item.votes / this.totalVotes}
|
||||
|
|
|
@ -43,8 +43,8 @@ const styles = StyleSheet.create({
|
|||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
button: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -123,7 +123,8 @@ export default class VoteSelect extends React.PureComponent<
|
|||
<Card.Content>
|
||||
<RadioButton.Group
|
||||
onValueChange={this.onVoteSelectionChange}
|
||||
value={state.selectedTeam}>
|
||||
value={state.selectedTeam}
|
||||
>
|
||||
<FlatList
|
||||
data={props.teams}
|
||||
keyExtractor={this.voteKeyExtractor}
|
||||
|
@ -137,8 +138,9 @@ export default class VoteSelect extends React.PureComponent<
|
|||
icon="send"
|
||||
mode="contained"
|
||||
onPress={this.showVoteDialog}
|
||||
style={{marginLeft: 'auto'}}
|
||||
disabled={state.selectedTeam === 'none'}>
|
||||
style={styles.button}
|
||||
disabled={state.selectedTeam === 'none'}
|
||||
>
|
||||
{i18n.t('screens.vote.select.sendButton')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
|
|
@ -30,9 +30,6 @@ const styles = StyleSheet.create({
|
|||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
export default function VoteTease(props: PropsType) {
|
||||
|
|
|
@ -33,9 +33,6 @@ const styles = StyleSheet.create({
|
|||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
export default function VoteWait(props: PropsType) {
|
||||
|
|
|
@ -76,6 +76,12 @@ const styles = StyleSheet.create({
|
|||
alignSelf: 'center',
|
||||
top: '-25%',
|
||||
},
|
||||
side: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
icon: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
});
|
||||
|
||||
class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
||||
|
@ -154,7 +160,8 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
|||
style={{
|
||||
...styles.container,
|
||||
bottom: 10 + CustomTabBar.TAB_BAR_HEIGHT,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Surface style={styles.surface}>
|
||||
<View style={styles.fabContainer}>
|
||||
<AnimatedFAB
|
||||
|
@ -165,10 +172,10 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
|||
useNativeDriver
|
||||
style={styles.fab}
|
||||
icon="account-clock"
|
||||
onPress={(): void => props.navigation.navigate('group-select')}
|
||||
onPress={() => props.navigation.navigate('group-select')}
|
||||
/>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={styles.side}>
|
||||
<IconButton
|
||||
icon={this.displayModeIcons[state.currentMode]}
|
||||
color={buttonColor}
|
||||
|
@ -177,21 +184,21 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
|
|||
<IconButton
|
||||
icon="clock-in"
|
||||
color={buttonColor}
|
||||
style={{marginLeft: 5}}
|
||||
onPress={(): void => props.onPress('today')}
|
||||
style={styles.icon}
|
||||
onPress={() => props.onPress('today')}
|
||||
/>
|
||||
</View>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={styles.side}>
|
||||
<IconButton
|
||||
icon="chevron-left"
|
||||
color={buttonColor}
|
||||
onPress={(): void => props.onPress('prev')}
|
||||
onPress={() => props.onPress('prev')}
|
||||
/>
|
||||
<IconButton
|
||||
icon="chevron-right"
|
||||
color={buttonColor}
|
||||
style={{marginLeft: 5}}
|
||||
onPress={(): void => props.onPress('next')}
|
||||
style={styles.icon}
|
||||
onPress={() => props.onPress('next')}
|
||||
/>
|
||||
</View>
|
||||
</Surface>
|
||||
|
|
|
@ -83,7 +83,8 @@ export default class AnimatedFAB extends React.Component<PropsType> {
|
|||
style={{
|
||||
...styles.fab,
|
||||
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<FAB icon={props.icon} onPress={props.onPress} />
|
||||
</Animatable.View>
|
||||
);
|
||||
|
|
|
@ -20,17 +20,27 @@
|
|||
import * as React from 'react';
|
||||
import { useCollapsibleStack } from 'react-navigation-collapsible';
|
||||
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;
|
||||
hasTab?: boolean;
|
||||
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
||||
}
|
||||
};
|
||||
|
||||
interface PropsType extends CollapsibleComponentPropsType {
|
||||
type PropsType = CollapsibleComponentPropsType & {
|
||||
component: React.ComponentType<any>;
|
||||
}
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
main: {
|
||||
minHeight: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
function CollapsibleComponent(props: PropsType) {
|
||||
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
|
@ -44,17 +54,18 @@ function CollapsibleComponent(props: PropsType) {
|
|||
scrollIndicatorInsetTop,
|
||||
onScrollWithListener,
|
||||
} = useCollapsibleStack();
|
||||
|
||||
const paddingBottom = props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0;
|
||||
return (
|
||||
<Comp
|
||||
{...props}
|
||||
onScroll={onScrollWithListener(onScroll)}
|
||||
contentContainerStyle={{
|
||||
paddingTop: containerPaddingTop,
|
||||
paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0,
|
||||
minHeight: '100%',
|
||||
paddingBottom: paddingBottom,
|
||||
...styles.main,
|
||||
}}
|
||||
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
|
||||
scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
|
||||
>
|
||||
{props.children}
|
||||
</Comp>
|
||||
);
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
Portal,
|
||||
} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
visible: boolean;
|
||||
|
@ -41,6 +42,12 @@ type StateType = {
|
|||
loading: boolean;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
marginRight: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default class LoadingConfirmDialog extends React.PureComponent<
|
||||
PropsType,
|
||||
StateType
|
||||
|
@ -113,7 +120,7 @@ export default class LoadingConfirmDialog extends React.PureComponent<
|
|||
</Dialog.Content>
|
||||
{state.loading ? null : (
|
||||
<Dialog.Actions>
|
||||
<Button onPress={this.onDismiss} style={{marginRight: 10}}>
|
||||
<Button onPress={this.onDismiss} style={styles.button}>
|
||||
{i18n.t('dialog.cancel')}
|
||||
</Button>
|
||||
<Button onPress={this.onClickAccept}>
|
||||
|
|
|
@ -19,10 +19,19 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import { List } from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
item: {
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
});
|
||||
|
||||
function ActionsDashBoardItem() {
|
||||
const navigation = useNavigation();
|
||||
return (
|
||||
|
@ -45,12 +54,7 @@ function ActionsDashBoardItem() {
|
|||
/>
|
||||
)}
|
||||
onPress={(): void => navigation.navigate('feedback')}
|
||||
style={{
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
}}
|
||||
style={styles.item}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from 'react-native-paper';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
eventNumber: number;
|
||||
|
@ -45,6 +46,9 @@ const styles = StyleSheet.create({
|
|||
avatar: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
text: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -61,7 +65,7 @@ function EventDashBoardItem(props: PropsType) {
|
|||
if (isAvailable) {
|
||||
subtitle = (
|
||||
<Text>
|
||||
<Text style={{fontWeight: 'bold'}}>{props.eventNumber}</Text>
|
||||
<Text style={styles.text}>{props.eventNumber}</Text>
|
||||
<Text>
|
||||
{props.eventNumber > 1
|
||||
? i18n.t('screens.home.dashboard.todayEventsSubtitlePlural')
|
||||
|
@ -74,7 +78,7 @@ function EventDashBoardItem(props: PropsType) {
|
|||
}
|
||||
return (
|
||||
<Card style={styles.card}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
||||
<TouchableRipple style={GENERAL_STYLES.flex} onPress={props.clickAction}>
|
||||
<View>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.home.dashboard.todayEventsTitle')}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 i18n from 'i18n-js';
|
||||
import type { FeedItemType } from '../../screens/Home/HomeScreen';
|
||||
|
@ -29,6 +29,7 @@ import NewsSourcesConstants, {
|
|||
import type { NewsSourceType } from '../../constants/NewsSourcesConstants';
|
||||
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
item: FeedItemType;
|
||||
|
@ -46,6 +47,20 @@ function getFormattedDate(dateString: number): string {
|
|||
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
|
||||
*/
|
||||
|
@ -76,31 +91,23 @@ function FeedItem(props: PropsType) {
|
|||
style={{
|
||||
margin: cardMargin,
|
||||
height: cardHeight,
|
||||
}}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={onPress}>
|
||||
}}
|
||||
>
|
||||
<TouchableRipple style={GENERAL_STYLES.flex} onPress={onPress}>
|
||||
<View>
|
||||
<Card.Title
|
||||
title={pageSource.name}
|
||||
subtitle={getFormattedDate(item.time)}
|
||||
left={() => (
|
||||
<Image
|
||||
source={pageSource.icon}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
left={() => <Image source={pageSource.icon} style={styles.image} />}
|
||||
style={{ height: titleHeight }}
|
||||
/>
|
||||
{image != null ? (
|
||||
<ImageGalleryButton
|
||||
images={[{ url: image }]}
|
||||
style={{
|
||||
...styles.button,
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -115,7 +122,7 @@ function FeedItem(props: PropsType) {
|
|||
) : null}
|
||||
</Card.Content>
|
||||
<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')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
|
|
@ -24,6 +24,7 @@ import {Avatar, Button, Card, TouchableRipple} from 'react-native-paper';
|
|||
import { getTimeOnlyString, isDescriptionEmpty } from '../../utils/Planning';
|
||||
import CustomHTML from '../Overrides/CustomHTML';
|
||||
import type { PlanningEventType } from '../../utils/Planning';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
event?: PlanningEventType | null;
|
||||
|
@ -59,12 +60,19 @@ function PreviewEventDashboardItem(props: PropsType) {
|
|||
const logo = event.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;
|
||||
return (
|
||||
<Card style={styles.card} elevation={3}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
||||
<TouchableRipple
|
||||
style={GENERAL_STYLES.flex}
|
||||
onPress={props.clickAction}
|
||||
>
|
||||
<View>
|
||||
<Card.Title
|
||||
title={event.title}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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';
|
||||
|
||||
type PropsType = {
|
||||
|
@ -28,6 +28,25 @@ type PropsType = {
|
|||
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
|
||||
*/
|
||||
|
@ -42,23 +61,18 @@ function SmallDashboardItem(props: PropsType) {
|
|||
style={{
|
||||
marginLeft: itemSize / 6,
|
||||
marginRight: itemSize / 6,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: itemSize,
|
||||
height: itemSize,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{image ? (
|
||||
<Image
|
||||
source={typeof image === 'string' ? { uri: image } : image}
|
||||
style={{
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
) : null}
|
||||
{props.badgeCount != null && props.badgeCount > 0 ? (
|
||||
|
@ -66,18 +80,16 @@ function SmallDashboardItem(props: PropsType) {
|
|||
animation="zoomIn"
|
||||
duration={300}
|
||||
useNativeDriver
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
}}>
|
||||
style={styles.badgeContainer}
|
||||
>
|
||||
<Badge
|
||||
visible={true}
|
||||
style={{
|
||||
backgroundColor: theme.colors.primary,
|
||||
borderColor: theme.colors.background,
|
||||
borderWidth: 2,
|
||||
}}>
|
||||
...styles.badge,
|
||||
}}
|
||||
>
|
||||
{props.badgeCount}
|
||||
</Badge>
|
||||
</Animatable.View>
|
||||
|
|
|
@ -21,6 +21,7 @@ import * as React from 'react';
|
|||
import { StyleSheet, View } from 'react-native';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
icon: string;
|
||||
|
@ -37,7 +38,7 @@ const styles = StyleSheet.create({
|
|||
|
||||
function IntroIcon(props: PropsType) {
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<Animatable.View useNativeDriver style={styles.center} animation="fadeIn">
|
||||
<MaterialCommunityIcons name={props.icon} color="#fff" size={200} />
|
||||
</Animatable.View>
|
||||
|
|
|
@ -19,24 +19,22 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
center: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginLeft: 'auto',
|
||||
...GENERAL_STYLES.center,
|
||||
width: '80%',
|
||||
},
|
||||
});
|
||||
|
||||
function MascotIntroEnd() {
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<Mascot
|
||||
style={{
|
||||
...styles.center,
|
||||
width: '80%',
|
||||
}}
|
||||
emotion={MASCOT_STYLE.COOL}
|
||||
animated
|
||||
|
|
|
@ -21,25 +21,37 @@ import * as React from 'react';
|
|||
import { StyleSheet, View } from 'react-native';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
center: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginLeft: 'auto',
|
||||
mascot: {
|
||||
...GENERAL_STYLES.center,
|
||||
width: '80%',
|
||||
},
|
||||
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() {
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<Mascot
|
||||
style={{
|
||||
...styles.center,
|
||||
width: '80%',
|
||||
}}
|
||||
style={styles.mascot}
|
||||
emotion={MASCOT_STYLE.NORMAL}
|
||||
animated
|
||||
entryAnimation={{
|
||||
|
@ -51,11 +63,8 @@ function MascotIntroWelcome() {
|
|||
useNativeDriver
|
||||
animation="fadeInUp"
|
||||
duration={500}
|
||||
style={{
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
fontSize: 25,
|
||||
}}>
|
||||
style={styles.text}
|
||||
>
|
||||
PABLO
|
||||
</Animatable.Text>
|
||||
<Animatable.View
|
||||
|
@ -63,18 +72,10 @@ function MascotIntroWelcome() {
|
|||
animation="fadeInUp"
|
||||
duration={500}
|
||||
delay={200}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 30,
|
||||
right: '20%',
|
||||
width: 50,
|
||||
height: 50,
|
||||
}}>
|
||||
style={styles.container}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
style={{
|
||||
...styles.center,
|
||||
transform: [{rotateZ: '70deg'}],
|
||||
}}
|
||||
style={styles.icon}
|
||||
name="undo"
|
||||
color="#fff"
|
||||
size={40}
|
||||
|
|
|
@ -19,28 +19,35 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
width: '40%',
|
||||
margin: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
cover: {
|
||||
height: 80,
|
||||
},
|
||||
});
|
||||
|
||||
function CardListItem(props: PropsType) {
|
||||
const { item } = props;
|
||||
const source =
|
||||
typeof item.image === 'number' ? item.image : { uri: item.image };
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
width: '40%',
|
||||
margin: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={item.onPress}>
|
||||
<Card style={styles.card}>
|
||||
<TouchableRipple style={GENERAL_STYLES.flex} onPress={item.onPress}>
|
||||
<View>
|
||||
<Card.Cover style={{height: 80}} source={source} />
|
||||
<Card.Cover style={styles.cover} source={source} />
|
||||
<Card.Content>
|
||||
<Paragraph>{item.title}</Paragraph>
|
||||
<Caption>{item.subtitle}</Caption>
|
||||
|
|
|
@ -19,14 +19,26 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType;
|
||||
width: number;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
ripple: {
|
||||
margin: 5,
|
||||
},
|
||||
text: {
|
||||
...GENERAL_STYLES.centerHorizontal,
|
||||
marginTop: 5,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
function ImageListItem(props: PropsType) {
|
||||
const { item } = props;
|
||||
const source =
|
||||
|
@ -36,28 +48,20 @@ function ImageListItem(props: PropsType) {
|
|||
style={{
|
||||
width: props.width,
|
||||
height: props.width + 40,
|
||||
margin: 5,
|
||||
...styles.ripple,
|
||||
}}
|
||||
onPress={item.onPress}>
|
||||
onPress={item.onPress}
|
||||
>
|
||||
<View>
|
||||
<Image
|
||||
style={{
|
||||
width: props.width - 20,
|
||||
height: props.width - 20,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
...GENERAL_STYLES.centerHorizontal,
|
||||
}}
|
||||
source={source}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
marginTop: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text style={styles.text}>{item.title}</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ import i18n from 'i18n-js';
|
|||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
import { isItemInCategoryFilter } from '../../../utils/Search';
|
||||
import type { ClubCategoryType } from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
categories: Array<ClubCategoryType>;
|
||||
|
@ -39,8 +40,7 @@ const styles = StyleSheet.create({
|
|||
paddingLeft: 0,
|
||||
marginTop: 5,
|
||||
marginBottom: 10,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
...GENERAL_STYLES.centerHorizontal,
|
||||
},
|
||||
chipContainer: {
|
||||
justifyContent: 'space-around',
|
||||
|
@ -49,6 +49,11 @@ const styles = StyleSheet.create({
|
|||
paddingLeft: 0,
|
||||
marginBottom: 5,
|
||||
},
|
||||
chip: {
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
|
||||
function ClubListHeader(props: PropsType) {
|
||||
|
@ -62,8 +67,9 @@ function ClubListHeader(props: PropsType) {
|
|||
])}
|
||||
mode="outlined"
|
||||
onPress={onPress}
|
||||
style={{marginRight: 5, marginLeft: 5, marginBottom: 5}}
|
||||
key={key}>
|
||||
style={styles.chip}
|
||||
key={key}
|
||||
>
|
||||
{category.name}
|
||||
</Chip>
|
||||
);
|
||||
|
@ -88,7 +94,8 @@ function ClubListHeader(props: PropsType) {
|
|||
icon="star"
|
||||
/>
|
||||
)}
|
||||
opened>
|
||||
opened
|
||||
>
|
||||
<Text style={styles.text}>
|
||||
{i18n.t('screens.clubs.categoriesFilterMessage')}
|
||||
</Text>
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import { Avatar, Chip, List, withTheme } from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import type {
|
||||
ClubCategoryType,
|
||||
ClubType,
|
||||
} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
onPress: () => void;
|
||||
|
@ -33,6 +34,28 @@ type PropsType = {
|
|||
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> {
|
||||
hasManagers: boolean;
|
||||
|
||||
|
@ -53,16 +76,14 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
const category = props.categoryTranslator(cat);
|
||||
if (category) {
|
||||
final.push(
|
||||
<Chip
|
||||
style={{marginRight: 5, marginBottom: 5}}
|
||||
key={`${props.item.id}:${category.id}`}>
|
||||
<Chip style={styles.chip} key={`${props.item.id}:${category.id}`}>
|
||||
{category.name}
|
||||
</Chip>,
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
return <View style={{flexDirection: 'row'}}>{final}</View>;
|
||||
return <View style={styles.chipContainer}>{final}</View>;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -77,22 +98,14 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
onPress={props.onPress}
|
||||
left={() => (
|
||||
<Avatar.Image
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
}}
|
||||
style={styles.avatar}
|
||||
size={64}
|
||||
source={{ uri: props.item.logo }}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
style={styles.icon}
|
||||
size={48}
|
||||
icon={
|
||||
this.hasManagers ? 'check-circle-outline' : 'alert-circle-outline'
|
||||
|
@ -102,7 +115,7 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
)}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
...styles.item,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 DashboardEditItem from './DashboardEditItem';
|
||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
|
@ -34,6 +34,13 @@ type PropsType = {
|
|||
onPress: (service: ServiceItemType) => void;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
},
|
||||
});
|
||||
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
function DashboardEditAccordion(props: PropsType) {
|
||||
|
@ -54,7 +61,7 @@ function DashboardEditAccordion(props: PropsType) {
|
|||
|
||||
const getItemLayout = (
|
||||
data: Array<ServiceItemType> | null | undefined,
|
||||
index: number,
|
||||
index: number
|
||||
): { length: number; offset: number; index: number } => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
|
@ -68,13 +75,7 @@ function DashboardEditAccordion(props: PropsType) {
|
|||
title={item.title}
|
||||
left={() =>
|
||||
typeof item.image === 'number' ? (
|
||||
<Image
|
||||
source={item.image}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
<Image source={item.image} style={styles.image} />
|
||||
) : (
|
||||
<MaterialCommunityIcons
|
||||
name={item.image}
|
||||
|
@ -82,7 +83,8 @@ function DashboardEditAccordion(props: PropsType) {
|
|||
size={40}
|
||||
/>
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
<FlatList
|
||||
data={item.content}
|
||||
extraData={props.activeDashboard.toString()}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Image} from 'react-native';
|
||||
import { Image, StyleSheet } from 'react-native';
|
||||
import { List, useTheme } from 'react-native-paper';
|
||||
import type { ServiceItemType } from '../../../managers/ServicesManager';
|
||||
|
||||
|
@ -29,9 +29,23 @@ type PropsType = {
|
|||
onPress: () => void;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
},
|
||||
item: {
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 30,
|
||||
},
|
||||
});
|
||||
|
||||
function DashboardEditItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const { item, onPress, height, isActive } = props;
|
||||
const backgroundColor = isActive
|
||||
? theme.colors.proxiwashFinishedColor
|
||||
: 'transparent';
|
||||
return (
|
||||
<List.Item
|
||||
title={item.title}
|
||||
|
@ -42,10 +56,7 @@ function DashboardEditItem(props: PropsType) {
|
|||
source={
|
||||
typeof item.image === 'string' ? { uri: item.image } : item.image
|
||||
}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
)}
|
||||
right={(iconProps) =>
|
||||
|
@ -58,12 +69,9 @@ function DashboardEditItem(props: PropsType) {
|
|||
) : null
|
||||
}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 30,
|
||||
backgroundColor: isActive
|
||||
? theme.colors.proxiwashFinishedColor
|
||||
: 'transparent',
|
||||
...styles.image,
|
||||
height: height,
|
||||
backgroundColor: backgroundColor,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import { TouchableRipple, useTheme } from 'react-native-paper';
|
||||
import {Dimensions, Image, View} from 'react-native';
|
||||
import { Dimensions, Image, StyleSheet, View } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
image?: string | number;
|
||||
|
@ -27,39 +27,50 @@ type PropsType = {
|
|||
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
|
||||
*/
|
||||
function DashboardEditPreviewItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const itemSize = Dimensions.get('window').width / 8;
|
||||
|
||||
const backgroundColor = props.isActive
|
||||
? theme.colors.textDisabled
|
||||
: 'transparent';
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={props.onPress}
|
||||
borderless
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
backgroundColor: props.isActive
|
||||
? theme.colors.textDisabled
|
||||
: 'transparent',
|
||||
borderRadius: 5,
|
||||
}}>
|
||||
...styles.ripple,
|
||||
backgroundColor: backgroundColor,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: itemSize,
|
||||
height: itemSize,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{props.image ? (
|
||||
<Image
|
||||
source={
|
||||
typeof props.image === 'string' ? {uri: props.image} : props.image
|
||||
typeof props.image === 'string'
|
||||
? { uri: props.image }
|
||||
: props.image
|
||||
}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
|
|
@ -27,6 +27,8 @@ import {
|
|||
getRelativeDateString,
|
||||
isEquipmentAvailable,
|
||||
} from '../../../utils/EquipmentBooking';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
|
@ -35,6 +37,15 @@ type PropsType = {
|
|||
height: number;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
item: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
function EquipmentListItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const { item, userDeviceRentDates, navigation, height } = props;
|
||||
|
@ -101,21 +112,12 @@ function EquipmentListItem(props: PropsType) {
|
|||
title={item.name}
|
||||
description={description}
|
||||
onPress={onPress}
|
||||
left={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
icon={icon}
|
||||
color={color}
|
||||
/>
|
||||
)}
|
||||
left={() => <Avatar.Icon style={styles.icon} icon={icon} color={color} />}
|
||||
right={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
backgroundColor: 'transparent',
|
||||
...GENERAL_STYLES.centerVertical,
|
||||
...styles.icon,
|
||||
}}
|
||||
size={48}
|
||||
icon="chevron-right"
|
||||
|
@ -123,7 +125,7 @@ function EquipmentListItem(props: PropsType) {
|
|||
)}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
...styles.item,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 GroupListItem from './GroupListItem';
|
||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
|
@ -40,6 +40,12 @@ type PropsType = {
|
|||
const LIST_ITEM_HEIGHT = 64;
|
||||
const REPLACE_REGEX = /_/g;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
class GroupListAccordion extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||
const { props } = this;
|
||||
|
@ -83,7 +89,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
|
||||
itemLayout = (
|
||||
data: Array<PlanexGroupType> | null | undefined,
|
||||
index: number,
|
||||
index: number
|
||||
): { length: number; offset: number; index: number } => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
|
@ -99,9 +105,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
<View>
|
||||
<AnimatedAccordion
|
||||
title={item.name.replace(REPLACE_REGEX, ' ')}
|
||||
style={{
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
style={styles.container}
|
||||
left={(iconProps) =>
|
||||
item.id === 0 ? (
|
||||
<List.Icon
|
||||
|
@ -112,7 +116,8 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
) : null
|
||||
}
|
||||
unmountWhenCollapsed={item.id !== 0} // Only render list if expanded for increased performance
|
||||
opened={props.currentSearchString.length > 0}>
|
||||
opened={props.currentSearchString.length > 0}
|
||||
>
|
||||
<FlatList
|
||||
data={this.getData()}
|
||||
extraData={props.currentSearchString + props.favorites.length}
|
||||
|
|
|
@ -22,7 +22,7 @@ import {List, TouchableRipple, withTheme} from 'react-native-paper';
|
|||
import * as Animatable from 'react-native-animatable';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import type { PlanexGroupType } from '../../../screens/Planex/GroupSelectionScreen';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { getPrettierPlanexGroupName } from '../../../utils/Utils';
|
||||
|
||||
type PropsType = {
|
||||
|
@ -34,6 +34,21 @@ type PropsType = {
|
|||
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> {
|
||||
isFav: boolean;
|
||||
|
||||
|
@ -98,15 +113,11 @@ class GroupListItem extends React.Component<PropsType> {
|
|||
<Animatable.View ref={this.starRef} useNativeDriver>
|
||||
<TouchableRipple
|
||||
onPress={this.onStarPress}
|
||||
style={{
|
||||
marginRight: 10,
|
||||
marginLeft: 'auto',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
style={styles.iconContainer}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
size={30}
|
||||
style={{padding: 10}}
|
||||
style={styles.icon}
|
||||
name="star"
|
||||
color={this.isFav ? colors.tetrisScore : iconProps.color}
|
||||
/>
|
||||
|
@ -115,7 +126,7 @@ class GroupListItem extends React.Component<PropsType> {
|
|||
)}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
...styles.item,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ import * as React from 'react';
|
|||
import { Avatar, List, Text } from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import type { ProximoArticleType } from '../../../screens/Services/Proximo/ProximoMainScreen';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
onPress: () => void;
|
||||
|
@ -29,28 +30,38 @@ type PropsType = {
|
|||
height: number;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
avatar: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
text: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
item: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
function ProximoListItem(props: PropsType) {
|
||||
return (
|
||||
<List.Item
|
||||
title={props.item.name}
|
||||
description={`${props.item.quantity} ${i18n.t(
|
||||
'screens.proximo.inStock',
|
||||
'screens.proximo.inStock'
|
||||
)}`}
|
||||
descriptionStyle={{ color: props.color }}
|
||||
onPress={props.onPress}
|
||||
left={() => (
|
||||
<Avatar.Image
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
style={styles.avatar}
|
||||
size={64}
|
||||
source={{ uri: props.item.image }}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
|
||||
)}
|
||||
right={() => <Text style={styles.text}>{props.item.price}€</Text>}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
...styles.item,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -42,7 +42,7 @@ type PropsType = {
|
|||
onPress: (
|
||||
title: string,
|
||||
item: ProxiwashMachineType,
|
||||
isDryer: boolean,
|
||||
isDryer: boolean
|
||||
) => void;
|
||||
isWatched: boolean;
|
||||
isDryer: boolean;
|
||||
|
@ -56,6 +56,7 @@ const styles = StyleSheet.create({
|
|||
margin: 5,
|
||||
justifyContent: 'center',
|
||||
elevation: 1,
|
||||
borderRadius: 4,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -65,6 +66,18 @@ const styles = StyleSheet.create({
|
|||
left: 0,
|
||||
borderRadius: 4,
|
||||
},
|
||||
item: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
textRow: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
textContainer: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -75,7 +88,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
[MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.states.ready'),
|
||||
[MachineStates.RUNNING]: i18n.t('screens.proxiwash.states.running'),
|
||||
[MachineStates.RUNNING_NOT_STARTED]: i18n.t(
|
||||
'screens.proxiwash.states.runningNotStarted',
|
||||
'screens.proxiwash.states.runningNotStarted'
|
||||
),
|
||||
[MachineStates.FINISHED]: i18n.t('screens.proxiwash.states.finished'),
|
||||
[MachineStates.UNAVAILABLE]: i18n.t('screens.proxiwash.states.broken'),
|
||||
|
@ -97,7 +110,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
const displayMaxWeight = props.item.maxWeight;
|
||||
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
|
||||
displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
|
||||
parseInt(props.item.number, 10),
|
||||
parseInt(props.item.number, 10)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -184,8 +197,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
style={{
|
||||
...styles.container,
|
||||
height: props.height,
|
||||
borderRadius: 4,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{!isReady ? (
|
||||
<ProgressBar
|
||||
style={{
|
||||
|
@ -201,26 +214,27 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
description={description}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
...styles.item,
|
||||
}}
|
||||
onPress={this.onListItemPress}
|
||||
left={() => icon}
|
||||
right={() => (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={{justifyContent: 'center'}}>
|
||||
<View style={styles.textRow}>
|
||||
<View style={styles.textContainer}>
|
||||
<Text
|
||||
style={
|
||||
machineState === MachineStates.FINISHED
|
||||
? {fontWeight: 'bold'}
|
||||
: {}
|
||||
}>
|
||||
? styles.text
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{stateString}
|
||||
</Text>
|
||||
{machineState === MachineStates.RUNNING ? (
|
||||
<Caption>{props.item.remainingTime} min</Caption>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={{justifyContent: 'center'}}>
|
||||
<View style={styles.textContainer}>
|
||||
<Avatar.Icon
|
||||
icon={stateIcon}
|
||||
color={colors.text}
|
||||
|
|
|
@ -44,6 +44,9 @@ const styles = StyleSheet.create({
|
|||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
textContainer: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -76,7 +79,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
color={iconColor}
|
||||
style={styles.icon}
|
||||
/>
|
||||
<View style={{justifyContent: 'center'}}>
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={styles.text}>{props.title}</Text>
|
||||
<Text style={{ color: props.theme.colors.subtitle }}>{subtitle}</Text>
|
||||
</View>
|
||||
|
|
|
@ -19,7 +19,13 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import {Image, TouchableWithoutFeedback, View, ViewStyle} from 'react-native';
|
||||
import {
|
||||
Image,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { AnimatableProperties } from 'react-native-animatable';
|
||||
|
||||
export type AnimatableViewRefType = {
|
||||
|
@ -77,6 +83,34 @@ export enum MASCOT_STYLE {
|
|||
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> {
|
||||
static defaultProps = {
|
||||
emotion: MASCOT_STYLE.NORMAL,
|
||||
|
@ -174,29 +208,21 @@ class Mascot extends React.Component<PropsType, StateType> {
|
|||
source={
|
||||
glasses != null ? glasses : this.glassesList[GLASSES_STYLE.NORMAL]
|
||||
}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '15%',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
style={styles.glassesImage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
getEye(style: EYE_STYLE, isRight: boolean, rotation: string = '0deg') {
|
||||
const eye = this.eyeList[style];
|
||||
const left = isRight ? '-11%' : '11%';
|
||||
return (
|
||||
<Image
|
||||
key={isRight ? 'right' : 'left'}
|
||||
source={eye != null ? eye : this.eyeList[EYE_STYLE.NORMAL]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '15%',
|
||||
left: isRight ? '-11%' : '11%',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...styles.eyesImage,
|
||||
left: left,
|
||||
transform: [{ rotateY: rotation }],
|
||||
}}
|
||||
/>
|
||||
|
@ -205,16 +231,7 @@ class Mascot extends React.Component<PropsType, StateType> {
|
|||
|
||||
getEyes(emotion: MASCOT_STYLE) {
|
||||
const final = [];
|
||||
final.push(
|
||||
<View
|
||||
key="container"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
final.push(<View key="container" style={styles.eyesContainer} />);
|
||||
if (emotion === MASCOT_STYLE.CUTE) {
|
||||
final.push(this.getEye(EYE_STYLE.CUTE, true));
|
||||
final.push(this.getEye(EYE_STYLE.CUTE, false));
|
||||
|
@ -255,26 +272,22 @@ class Mascot extends React.Component<PropsType, StateType> {
|
|||
return (
|
||||
<Animatable.View
|
||||
style={{
|
||||
aspectRatio: 1,
|
||||
...styles.container,
|
||||
...props.style,
|
||||
}}
|
||||
{...entryAnimation}>
|
||||
{...entryAnimation}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => {
|
||||
this.onPress(this.viewRef);
|
||||
}}
|
||||
onLongPress={() => {
|
||||
this.onLongPress(this.viewRef);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Animatable.View ref={this.viewRef}>
|
||||
<Animatable.View {...loopAnimation}>
|
||||
<Image
|
||||
source={MASCOT_IMAGE}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>
|
||||
<Image source={MASCOT_IMAGE} style={styles.mascot} />
|
||||
{this.getEyes(state.currentEmotion)}
|
||||
</Animatable.View>
|
||||
</Animatable.View>
|
||||
|
|
|
@ -31,12 +31,14 @@ import {
|
|||
BackHandler,
|
||||
Dimensions,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import Mascot from './Mascot';
|
||||
import SpeechArrow from './SpeechArrow';
|
||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
theme: ReactNativePaper.Theme;
|
||||
|
@ -67,6 +69,41 @@ type StateType = {
|
|||
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.
|
||||
*/
|
||||
|
@ -107,11 +144,12 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
componentDidMount() {
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
this.onBackButtonPressAndroid
|
||||
);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean {
|
||||
// TODO this is so dirty it shouldn't even work
|
||||
const { props, state } = this;
|
||||
if (nextProps.visible) {
|
||||
this.state.shouldRenderDialog = true;
|
||||
|
@ -155,13 +193,11 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
const { state, props } = this;
|
||||
return (
|
||||
<Animatable.View
|
||||
style={{
|
||||
marginLeft: '10%',
|
||||
marginRight: '10%',
|
||||
}}
|
||||
style={styles.speechBubbleContainer}
|
||||
useNativeDriver
|
||||
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
||||
duration={state.dialogVisible ? 1000 : 300}>
|
||||
duration={state.dialogVisible ? 1000 : 300}
|
||||
>
|
||||
<SpeechArrow
|
||||
style={{ marginLeft: this.mascotSize / 3 }}
|
||||
size={20}
|
||||
|
@ -170,9 +206,9 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
<Card
|
||||
style={{
|
||||
borderColor: props.theme.colors.mascotMessageArrow,
|
||||
borderWidth: 4,
|
||||
borderRadius: 10,
|
||||
}}>
|
||||
...styles.speechBubbleCard,
|
||||
}}
|
||||
>
|
||||
<Card.Title
|
||||
title={props.title}
|
||||
left={
|
||||
|
@ -180,7 +216,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
? () => (
|
||||
<Avatar.Icon
|
||||
size={48}
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
style={styles.speechBubbleIcon}
|
||||
color={props.theme.colors.primary}
|
||||
icon={props.icon}
|
||||
/>
|
||||
|
@ -191,13 +227,16 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
<Card.Content
|
||||
style={{
|
||||
maxHeight: this.windowHeight / 3,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ScrollView>
|
||||
<Paragraph style={{marginBottom: 10}}>{props.message}</Paragraph>
|
||||
<Paragraph style={styles.speechBubbleText}>
|
||||
{props.message}
|
||||
</Paragraph>
|
||||
</ScrollView>
|
||||
</Card.Content>
|
||||
|
||||
<Card.Actions style={{marginTop: 10, marginBottom: 10}}>
|
||||
<Card.Actions style={styles.actionsContainer}>
|
||||
{this.getButtons()}
|
||||
</Card.Actions>
|
||||
</Card>
|
||||
|
@ -211,7 +250,8 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
<Animatable.View
|
||||
useNativeDriver
|
||||
animation={state.dialogVisible ? 'bounceInLeft' : 'bounceOutLeft'}
|
||||
duration={state.dialogVisible ? 1500 : 200}>
|
||||
duration={state.dialogVisible ? 1500 : 200}
|
||||
>
|
||||
<Mascot
|
||||
style={{ width: this.mascotSize }}
|
||||
animated
|
||||
|
@ -226,41 +266,30 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
const { action } = props.buttons;
|
||||
const { cancel } = props.buttons;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.center}>
|
||||
{action != null ? (
|
||||
<Button
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 10,
|
||||
}}
|
||||
style={styles.button}
|
||||
mode="contained"
|
||||
icon={action.icon}
|
||||
color={action.color}
|
||||
onPress={() => {
|
||||
this.onDismiss(action.onPress);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{action.message}
|
||||
</Button>
|
||||
) : null}
|
||||
{cancel != null ? (
|
||||
<Button
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
style={styles.button}
|
||||
mode="contained"
|
||||
icon={cancel.icon}
|
||||
color={cancel.color}
|
||||
onPress={() => {
|
||||
this.onDismiss(cancel.onPress);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{cancel.message}
|
||||
</Button>
|
||||
) : null}
|
||||
|
@ -274,14 +303,10 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
<TouchableWithoutFeedback
|
||||
onPress={() => {
|
||||
this.onDismiss(props.buttons.cancel?.onPress);
|
||||
}}>
|
||||
<Animatable.View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<Animatable.View
|
||||
style={styles.background}
|
||||
useNativeDriver
|
||||
animation={state.dialogVisible ? 'fadeIn' : 'fadeOut'}
|
||||
duration={state.dialogVisible ? 300 : 300}
|
||||
|
@ -307,16 +332,8 @@ class MascotPopup extends React.Component<PropsType, StateType> {
|
|||
return (
|
||||
<Portal>
|
||||
{this.getBackground()}
|
||||
<View
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
marginTop: -80,
|
||||
width: '100%',
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.centerVertical}>
|
||||
<View style={styles.container}>
|
||||
{this.getMascot()}
|
||||
{this.getSpeechBubble()}
|
||||
</View>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View, ViewStyle} from 'react-native';
|
||||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
style?: ViewStyle;
|
||||
|
@ -26,20 +26,26 @@ type PropsType = {
|
|||
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) {
|
||||
return (
|
||||
<View style={props.style}>
|
||||
<View
|
||||
style={{
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderLeftWidth: 0,
|
||||
...styles.arrow,
|
||||
borderRightWidth: props.size,
|
||||
borderBottomWidth: props.size,
|
||||
borderStyle: 'solid',
|
||||
backgroundColor: 'transparent',
|
||||
borderLeftColor: 'transparent',
|
||||
borderRightColor: 'transparent',
|
||||
borderBottomColor: props.color,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -21,13 +21,20 @@ import * as React from 'react';
|
|||
import { TouchableRipple } from 'react-native-paper';
|
||||
import { Image } from 'react-native-animatable';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import {ViewStyle} from 'react-native';
|
||||
import { StyleSheet, ViewStyle } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
images: Array<{ url: string }>;
|
||||
style: ViewStyle;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
function ImageGalleryButton(props: PropsType) {
|
||||
const navigation = useNavigation();
|
||||
|
||||
|
@ -40,10 +47,7 @@ function ImageGalleryButton(props: PropsType) {
|
|||
<Image
|
||||
resizeMode="contain"
|
||||
source={{ uri: props.images[0].url }}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
</TouchableRipple>
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ import * as React from 'react';
|
|||
import { View } from 'react-native';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
import { Agenda, AgendaProps } from 'react-native-calendars';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
onRef: (ref: Agenda<any>) => void;
|
||||
|
@ -67,7 +68,7 @@ function CustomAgenda(props: PropsType) {
|
|||
|
||||
// Completely recreate the component on theme change to force theme reload
|
||||
if (theme.dark) {
|
||||
return <View style={{flex: 1}}>{getAgenda()}</View>;
|
||||
return <View style={GENERAL_STYLES.flex}>{getAgenda()}</View>;
|
||||
}
|
||||
return getAgenda();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ function CustomHTML(props: PropsType) {
|
|||
htmlAttribs: any,
|
||||
children: any,
|
||||
convertedCSSStyles: any,
|
||||
passProps: any,
|
||||
passProps: any
|
||||
) => {
|
||||
return <Text {...passProps}>{children}</Text>;
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ const MaterialHeaderButton = (props: HeaderButtonProps) => {
|
|||
};
|
||||
|
||||
const MaterialHeaderButtons = (
|
||||
props: HeaderButtonsProps & {children?: React.ReactNode},
|
||||
props: HeaderButtonsProps & { children?: React.ReactNode }
|
||||
) => {
|
||||
return (
|
||||
<HeaderButtons {...props} HeaderButtonComponent={MaterialHeaderButton} />
|
||||
|
|
|
@ -37,6 +37,7 @@ import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
|
|||
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
|
||||
import IntroIcon from '../Intro/IconIntro';
|
||||
import MascotIntroEnd from '../Intro/MascotIntroEnd';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
onDone: () => void;
|
||||
|
@ -75,11 +76,42 @@ const styles = StyleSheet.create({
|
|||
textAlign: 'center',
|
||||
marginBottom: 16,
|
||||
},
|
||||
center: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginLeft: 'auto',
|
||||
mascot: {
|
||||
marginLeft: 30,
|
||||
marginBottom: 0,
|
||||
width: 100,
|
||||
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)',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -175,7 +207,7 @@ export default class CustomIntroSlider extends React.Component<
|
|||
| (ListRenderItemInfo<IntroSlideType> & {
|
||||
dimensions: { width: number; height: number };
|
||||
})
|
||||
| ListRenderItemInfo<IntroSlideType>,
|
||||
| ListRenderItemInfo<IntroSlideType>
|
||||
) => {
|
||||
const item = data.item;
|
||||
const { state } = this;
|
||||
|
@ -185,19 +217,15 @@ export default class CustomIntroSlider extends React.Component<
|
|||
style={[styles.mainContent]}
|
||||
colors={item.colors}
|
||||
start={{ x: 0, y: 0.1 }}
|
||||
end={{x: 0.1, y: 1}}>
|
||||
end={{ x: 0.1, y: 1 }}
|
||||
>
|
||||
{state.currentSlide === index ? (
|
||||
<View style={{height: '100%', flex: 1}}>
|
||||
<View style={{flex: 1}}>{item.view()}</View>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<View style={GENERAL_STYLES.flex}>{item.view()}</View>
|
||||
<Animatable.View useNativeDriver animation="fadeIn">
|
||||
{item.mascotStyle != null ? (
|
||||
<Mascot
|
||||
style={{
|
||||
marginLeft: 30,
|
||||
marginBottom: 0,
|
||||
width: 100,
|
||||
marginTop: -30,
|
||||
}}
|
||||
style={styles.mascot}
|
||||
emotion={item.mascotStyle}
|
||||
animated
|
||||
entryAnimation={{
|
||||
|
@ -211,43 +239,23 @@ export default class CustomIntroSlider extends React.Component<
|
|||
}}
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
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,
|
||||
}}>
|
||||
<View style={styles.speechArrow} />
|
||||
<Card style={styles.card}>
|
||||
<Card.Content>
|
||||
<Animatable.Text
|
||||
useNativeDriver
|
||||
animation="fadeIn"
|
||||
delay={100}
|
||||
style={styles.title}>
|
||||
style={styles.title}
|
||||
>
|
||||
{item.title}
|
||||
</Animatable.Text>
|
||||
<Animatable.Text
|
||||
useNativeDriver
|
||||
animation="fadeIn"
|
||||
delay={200}
|
||||
style={styles.text}>
|
||||
style={styles.text}
|
||||
>
|
||||
{item.text}
|
||||
</Animatable.Text>
|
||||
</Card.Content>
|
||||
|
@ -272,7 +280,7 @@ export default class CustomIntroSlider extends React.Component<
|
|||
|
||||
onSkip = () => {
|
||||
CustomIntroSlider.setStatusBarColor(
|
||||
this.currentSlides[this.currentSlides.length - 1].colors[0],
|
||||
this.currentSlides[this.currentSlides.length - 1].colors[0]
|
||||
);
|
||||
if (this.sliderRef.current != null) {
|
||||
this.sliderRef.current.goToSlide(this.currentSlides.length - 1);
|
||||
|
@ -282,7 +290,7 @@ export default class CustomIntroSlider extends React.Component<
|
|||
onDone = () => {
|
||||
const { props } = this;
|
||||
CustomIntroSlider.setStatusBarColor(
|
||||
ThemeManager.getCurrentTheme().colors.surface,
|
||||
ThemeManager.getCurrentTheme().colors.surface
|
||||
);
|
||||
props.onDone();
|
||||
};
|
||||
|
@ -292,11 +300,8 @@ export default class CustomIntroSlider extends React.Component<
|
|||
<Animatable.View
|
||||
useNativeDriver
|
||||
animation="fadeIn"
|
||||
style={{
|
||||
borderRadius: 25,
|
||||
padding: 5,
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
}}>
|
||||
style={styles.nextButtonContainer}
|
||||
>
|
||||
<MaterialCommunityIcons name="arrow-right" color="#fff" size={40} />
|
||||
</Animatable.View>
|
||||
);
|
||||
|
@ -307,11 +312,8 @@ export default class CustomIntroSlider extends React.Component<
|
|||
<Animatable.View
|
||||
useNativeDriver
|
||||
animation="bounceIn"
|
||||
style={{
|
||||
borderRadius: 25,
|
||||
padding: 5,
|
||||
backgroundColor: 'rgb(190,21,34)',
|
||||
}}>
|
||||
style={styles.doneButtonContainer}
|
||||
>
|
||||
<MaterialCommunityIcons name="check" color="#fff" size={40} />
|
||||
</Animatable.View>
|
||||
);
|
||||
|
|
|
@ -41,11 +41,13 @@ function CustomModal(props: {
|
|||
adjustToContentHeight
|
||||
handlePosition="inside"
|
||||
modalStyle={{ backgroundColor: theme.colors.card }}
|
||||
handleStyle={{backgroundColor: theme.colors.primary}}>
|
||||
handleStyle={{ backgroundColor: theme.colors.primary }}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
</Modalize>
|
||||
|
|
|
@ -22,11 +22,24 @@ import {Text} from 'react-native-paper';
|
|||
import { View } from 'react-native-animatable';
|
||||
import Slider, { SliderProps } from '@react-native-community/slider';
|
||||
import { useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
valueSuffix?: string;
|
||||
} & 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
|
||||
*
|
||||
|
@ -44,15 +57,8 @@ function CustomSlider(props: PropsType) {
|
|||
};
|
||||
|
||||
return (
|
||||
<View style={{flex: 1, flexDirection: 'row'}}>
|
||||
<Text
|
||||
style={{
|
||||
marginHorizontal: 10,
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
{currentValue}min
|
||||
</Text>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>{currentValue}min</Text>
|
||||
<Slider {...props} ref={undefined} onValueChange={onValueChange} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -17,16 +17,24 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { ActivityIndicator, useTheme } from 'react-native-paper';
|
||||
|
||||
type Props = {
|
||||
isAbsolute?: boolean;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Component used to display a header button
|
||||
*
|
||||
|
@ -36,17 +44,15 @@ type Props = {
|
|||
export default function BasicLoadingScreen(props: Props) {
|
||||
const theme = useTheme();
|
||||
const { isAbsolute } = props;
|
||||
const position = isAbsolute ? 'absolute' : 'relative';
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: theme.colors.background,
|
||||
position: isAbsolute ? 'absolute' : 'relative',
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
position: position,
|
||||
...styles.container,
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator animating size="large" color={theme.colors.primary} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -90,7 +90,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
|||
mode="contained"
|
||||
icon="refresh"
|
||||
onPress={props.onRefresh}
|
||||
style={styles.button}>
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t('general.retry')}
|
||||
</Button>
|
||||
);
|
||||
|
@ -102,7 +103,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
|||
mode="contained"
|
||||
icon="login"
|
||||
onPress={this.goToLogin}
|
||||
style={styles.button}>
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t('screens.login.title')}
|
||||
</Button>
|
||||
);
|
||||
|
@ -190,7 +192,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
|||
}}
|
||||
animation="zoomIn"
|
||||
duration={200}
|
||||
useNativeDriver>
|
||||
useNativeDriver
|
||||
>
|
||||
<View style={styles.inner}>
|
||||
<View style={styles.iconContainer}>
|
||||
<MaterialCommunityIcons
|
||||
|
@ -204,7 +207,8 @@ class ErrorView extends React.PureComponent<PropsType> {
|
|||
style={{
|
||||
...styles.subheading,
|
||||
color: props.theme.colors.textDisabled,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{this.message}
|
||||
</Subheading>
|
||||
{button}
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
NativeSyntheticEvent,
|
||||
RefreshControl,
|
||||
SectionListData,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
|
@ -51,7 +52,7 @@ type PropsType<ItemT, RawData> = {
|
|||
renderItem: (data: { item: ItemT }) => React.ReactNode;
|
||||
createDataset: (
|
||||
data: RawData | null,
|
||||
isLoading?: boolean,
|
||||
isLoading?: boolean
|
||||
) => SectionListDataType<ItemT>;
|
||||
onScroll: (event: NativeSyntheticEvent<EventTarget>) => void;
|
||||
collapsibleStack: Collapsible;
|
||||
|
@ -60,11 +61,11 @@ type PropsType<ItemT, RawData> = {
|
|||
itemHeight?: number | null;
|
||||
updateData?: number;
|
||||
renderListHeaderComponent?: (
|
||||
data: RawData | null,
|
||||
data: RawData | null
|
||||
) => React.ComponentType<any> | React.ReactElement | null;
|
||||
renderSectionHeader?: (
|
||||
data: { section: SectionListData<ItemT> },
|
||||
isLoading?: boolean,
|
||||
isLoading?: boolean
|
||||
) => React.ReactElement | null;
|
||||
stickyHeader?: boolean;
|
||||
};
|
||||
|
@ -77,6 +78,12 @@ type StateType<RawData> = {
|
|||
|
||||
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
|
||||
*
|
||||
|
@ -204,7 +211,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
|||
getItemLayout = (
|
||||
height: number,
|
||||
data: Array<SectionListData<ItemT>> | null,
|
||||
index: number,
|
||||
index: number
|
||||
): { length: number; offset: number; index: number } => {
|
||||
return {
|
||||
length: height,
|
||||
|
@ -269,7 +276,7 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
|||
renderSectionHeader={this.getRenderSectionHeader}
|
||||
renderItem={this.getRenderItem}
|
||||
stickySectionHeadersEnabled={props.stickyHeader}
|
||||
style={{minHeight: '100%'}}
|
||||
style={styles.container}
|
||||
ListHeaderComponent={
|
||||
props.renderListHeaderComponent != null
|
||||
? props.renderListHeaderComponent(state.fetchedData)
|
||||
|
@ -304,7 +311,8 @@ class WebSectionList<ItemT, RawData> extends React.PureComponent<
|
|||
duration={4000}
|
||||
style={{
|
||||
bottom: CustomTabBar.TAB_BAR_HEIGHT,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{i18n.t('general.listUpdateFail')}
|
||||
</Snackbar>
|
||||
</View>
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
Linking,
|
||||
NativeScrollEvent,
|
||||
NativeSyntheticEvent,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { withTheme } from 'react-native-paper';
|
||||
|
@ -56,6 +57,12 @@ type PropsType = {
|
|||
|
||||
const AnimatedWebView = Animated.createAnimatedComponent(WebView);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overflow: {
|
||||
marginHorizontal: 10,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Class defining a webview screen.
|
||||
*/
|
||||
|
@ -92,13 +99,13 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
|||
props.navigation.addListener('focus', () => {
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
this.onBackButtonPressAndroid
|
||||
);
|
||||
});
|
||||
props.navigation.addListener('blur', () => {
|
||||
BackHandler.removeEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
this.onBackButtonPressAndroid
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -154,14 +161,15 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
|||
onPress={this.onRefreshClicked}
|
||||
/>
|
||||
<OverflowMenu
|
||||
style={{marginHorizontal: 10}}
|
||||
style={styles.overflow}
|
||||
OverflowIcon={
|
||||
<MaterialCommunityIcons
|
||||
name="dots-vertical"
|
||||
size={26}
|
||||
color={props.theme.colors.text}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<HiddenItem
|
||||
title={i18n.t('general.goBack')}
|
||||
onPress={this.onGoBackClicked}
|
||||
|
@ -248,7 +256,10 @@ class WebViewScreen extends React.PureComponent<PropsType> {
|
|||
|
||||
render() {
|
||||
const { props } = this;
|
||||
const {containerPaddingTop, onScrollWithListener} = props.collapsibleStack;
|
||||
const {
|
||||
containerPaddingTop,
|
||||
onScrollWithListener,
|
||||
} = props.collapsibleStack;
|
||||
return (
|
||||
<AnimatedWebView
|
||||
ref={this.webviewRef}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Animated} from 'react-native';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
import { withTheme } from 'react-native-paper';
|
||||
import { Collapsible } from 'react-navigation-collapsible';
|
||||
import TabIcon from './TabIcon';
|
||||
|
@ -51,6 +51,16 @@ const TAB_ICONS = {
|
|||
planex: 'clock',
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
},
|
||||
});
|
||||
|
||||
class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||
static TAB_BAR_HEIGHT = 48;
|
||||
|
||||
|
@ -202,15 +212,12 @@ class CustomTabBar extends React.Component<PropsType, StateType> {
|
|||
return (
|
||||
<Animated.View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
height: CustomTabBar.TAB_BAR_HEIGHT,
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
backgroundColor: props.theme.colors.surface,
|
||||
transform: [{ translateY: state.translateY }],
|
||||
}}>
|
||||
...styles.container,
|
||||
}}
|
||||
>
|
||||
{icons}
|
||||
</Animated.View>
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
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 * as Animatable from 'react-native-animatable';
|
||||
const FOCUSED_ICON = require('../../../assets/tab-icon.png');
|
||||
|
@ -33,6 +33,25 @@ type PropsType = {
|
|||
|
||||
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
|
||||
*/
|
||||
|
@ -91,20 +110,13 @@ class TabHomeIcon extends React.Component<PropsType> {
|
|||
render() {
|
||||
const { props } = this;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: props.tabBarHeight + 30,
|
||||
marginBottom: -15,
|
||||
}}>
|
||||
...styles.subcontainer,
|
||||
}}
|
||||
>
|
||||
<AnimatedFAB
|
||||
duration={200}
|
||||
easing="ease-out"
|
||||
|
@ -112,11 +124,7 @@ class TabHomeIcon extends React.Component<PropsType> {
|
|||
icon={this.getIconRender}
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
style={{
|
||||
marginTop: 15,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
style={styles.fab}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { TouchableRipple, withTheme } from 'react-native-paper';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
focused: boolean;
|
||||
|
@ -34,6 +35,19 @@ type PropsType = {
|
|||
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
|
||||
*/
|
||||
|
@ -93,26 +107,21 @@ class TabIcon extends React.Component<PropsType> {
|
|||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
rippleColor={props.theme.colors.primary}
|
||||
borderless
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
borderRadius: 10,
|
||||
}}>
|
||||
borderless={true}
|
||||
style={styles.container}
|
||||
>
|
||||
<View>
|
||||
<Animatable.View
|
||||
duration={200}
|
||||
easing="ease-out"
|
||||
animation={props.focused ? 'focusIn' : 'focusOut'}
|
||||
useNativeDriver>
|
||||
useNativeDriver
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={props.icon}
|
||||
color={props.color}
|
||||
size={26}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
style={GENERAL_STYLES.centerHorizontal}
|
||||
/>
|
||||
</Animatable.View>
|
||||
<Animatable.Text
|
||||
|
@ -120,10 +129,9 @@ class TabIcon extends React.Component<PropsType> {
|
|||
useNativeDriver
|
||||
style={{
|
||||
color: props.color,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
fontSize: 10,
|
||||
}}>
|
||||
...styles.text,
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</Animatable.Text>
|
||||
</View>
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
const ICON_AMICALE = require('../../assets/amicale.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;
|
|
@ -67,7 +67,7 @@ export default class AprilFoolsManager {
|
|||
* @returns {Object}
|
||||
*/
|
||||
static getFakeMenuItem(
|
||||
menu: Array<RuFoodCategoryType>,
|
||||
menu: Array<RuFoodCategoryType>
|
||||
): Array<RuFoodCategoryType> {
|
||||
menu[1].dishes.splice(4, 0, { name: 'Coq au vin' });
|
||||
menu[1].dishes.splice(2, 0, { name: "Bat'Soupe" });
|
||||
|
@ -83,7 +83,7 @@ export default class AprilFoolsManager {
|
|||
* @param dryers
|
||||
*/
|
||||
static getNewProxiwashDryerOrderedList(
|
||||
dryers: Array<ProxiwashMachineType> | null,
|
||||
dryers: Array<ProxiwashMachineType> | null
|
||||
) {
|
||||
if (dryers != null) {
|
||||
const second = dryers[1];
|
||||
|
@ -98,7 +98,7 @@ export default class AprilFoolsManager {
|
|||
* @param washers
|
||||
*/
|
||||
static getNewProxiwashWasherOrderedList(
|
||||
washers: Array<ProxiwashMachineType> | null,
|
||||
washers: Array<ProxiwashMachineType> | null
|
||||
) {
|
||||
if (washers != null) {
|
||||
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}}}
|
||||
*/
|
||||
static getAprilFoolsTheme(
|
||||
currentTheme: ReactNativePaper.Theme,
|
||||
currentTheme: ReactNativePaper.Theme
|
||||
): ReactNativePaper.Theme {
|
||||
return {
|
||||
...currentTheme,
|
||||
|
|
|
@ -155,7 +155,7 @@ export default class AsyncStorageManager {
|
|||
*/
|
||||
static set(
|
||||
key: string,
|
||||
value: number | string | boolean | object | Array<any>,
|
||||
value: number | string | boolean | object | Array<any>
|
||||
) {
|
||||
AsyncStorageManager.getInstance().setPreference(key, value);
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ export default class AsyncStorageManager {
|
|||
* @return {Promise<void>}
|
||||
*/
|
||||
async loadPreferences() {
|
||||
return new Promise((resolve: () => void) => {
|
||||
return new Promise((resolve: (val: void) => void) => {
|
||||
const prefKeys: Array<string> = [];
|
||||
// Get all available keys
|
||||
Object.keys(AsyncStorageManager.PREFERENCES).forEach((key: string) => {
|
||||
|
@ -240,7 +240,7 @@ export default class AsyncStorageManager {
|
|||
*/
|
||||
setPreference(
|
||||
key: string,
|
||||
value: number | string | boolean | object | Array<any>,
|
||||
value: number | string | boolean | object | Array<any>
|
||||
) {
|
||||
if (AsyncStorageManager.PREFERENCES[key] != null) {
|
||||
let convertedValue;
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import type { ApiDataLoginType } from '../utils/WebData';
|
||||
import { apiRequest, ERROR_TYPE } from '../utils/WebData';
|
||||
|
@ -84,7 +82,7 @@ export default class ConnectionManager {
|
|||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(resolve);
|
||||
.catch(() => resolve());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -159,7 +157,7 @@ export default class ConnectionManager {
|
|||
}
|
||||
})
|
||||
.catch((error: number): void => reject(error));
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -172,7 +170,7 @@ export default class ConnectionManager {
|
|||
*/
|
||||
async authenticatedRequest<T>(
|
||||
path: string,
|
||||
params: {[key: string]: any},
|
||||
params: { [key: string]: any }
|
||||
): Promise<T> {
|
||||
return new Promise(
|
||||
(resolve: (response: T) => void, reject: (error: number) => void) => {
|
||||
|
@ -187,7 +185,7 @@ export default class ConnectionManager {
|
|||
} else {
|
||||
reject(ERROR_TYPE.TOKEN_RETRIEVE);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import type { ServiceItemType } from './ServicesManager';
|
||||
import ServicesManager from './ServicesManager';
|
||||
import { getSublistWithIds } from '../utils/Services';
|
||||
|
@ -27,7 +25,7 @@ import AsyncStorageManager from './AsyncStorageManager';
|
|||
export default class DashboardManager extends ServicesManager {
|
||||
getCurrentDashboard(): Array<ServiceItemType | null> {
|
||||
const dashboardIdList = AsyncStorageManager.getObject<Array<string>>(
|
||||
AsyncStorageManager.PREFERENCES.dashboardItems.key,
|
||||
AsyncStorageManager.PREFERENCES.dashboardItems.key
|
||||
);
|
||||
const allDatasets = [
|
||||
...this.amicaleDataset,
|
||||
|
|
|
@ -84,7 +84,7 @@ export default class DateManager {
|
|||
date.setFullYear(
|
||||
parseInt(dateArray[0], 10),
|
||||
parseInt(dateArray[1], 10) - 1,
|
||||
parseInt(dateArray[2], 10),
|
||||
parseInt(dateArray[2], 10)
|
||||
);
|
||||
return `${this.daysOfWeek[date.getDay()]} ${date.getDate()} ${
|
||||
this.monthsOfYear[date.getMonth()]
|
||||
|
|
|
@ -235,14 +235,14 @@ export default class ThemeManager {
|
|||
static getNightMode(): boolean {
|
||||
return (
|
||||
(AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.nightMode.key,
|
||||
AsyncStorageManager.PREFERENCES.nightMode.key
|
||||
) &&
|
||||
(!AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
|
||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
|
||||
) ||
|
||||
colorScheme === 'no-preference')) ||
|
||||
(AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
|
||||
AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
|
||||
) &&
|
||||
colorScheme === 'dark')
|
||||
);
|
||||
|
@ -289,7 +289,7 @@ export default class ThemeManager {
|
|||
setNightMode(isNightMode: boolean) {
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.nightMode.key,
|
||||
isNightMode,
|
||||
isNightMode
|
||||
);
|
||||
if (this.updateThemeCallback != null) {
|
||||
this.updateThemeCallback();
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
*/
|
||||
|
||||
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 { Platform } from 'react-native';
|
||||
import SettingsScreen from '../screens/Other/Settings/SettingsScreen';
|
||||
|
@ -81,13 +84,13 @@ export enum MainRoutes {
|
|||
type DefaultParams = { [key in MainRoutes]: object | undefined };
|
||||
|
||||
export interface FullParamsList extends DefaultParams {
|
||||
login: {nextScreen: string};
|
||||
'login': { nextScreen: string };
|
||||
'equipment-confirm': {
|
||||
item?: DeviceType;
|
||||
dates: [string, string];
|
||||
};
|
||||
'equipment-rent': { item?: DeviceType };
|
||||
gallery: {images: Array<{url: string}>};
|
||||
'gallery': { images: Array<{ url: string }> };
|
||||
}
|
||||
|
||||
// Don't know why but TS is complaining without this
|
||||
|
@ -114,7 +117,8 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
<MainStack.Navigator
|
||||
initialRouteName={MainRoutes.Main}
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
<MainStack.Screen
|
||||
name={MainRoutes.Main}
|
||||
component={createTabNavigator}
|
||||
|
@ -135,31 +139,31 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
MainRoutes.Settings,
|
||||
MainStack,
|
||||
SettingsScreen,
|
||||
i18n.t('screens.settings.title'),
|
||||
i18n.t('screens.settings.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.DashboardEdit,
|
||||
MainStack,
|
||||
DashboardEditScreen,
|
||||
i18n.t('screens.settings.dashboardEdit.title'),
|
||||
i18n.t('screens.settings.dashboardEdit.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.About,
|
||||
MainStack,
|
||||
AboutScreen,
|
||||
i18n.t('screens.about.title'),
|
||||
i18n.t('screens.about.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Dependencies,
|
||||
MainStack,
|
||||
AboutDependenciesScreen,
|
||||
i18n.t('screens.about.libs'),
|
||||
i18n.t('screens.about.libs')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Debug,
|
||||
MainStack,
|
||||
DebugScreen,
|
||||
i18n.t('screens.about.debug'),
|
||||
i18n.t('screens.about.debug')
|
||||
)}
|
||||
|
||||
{CreateScreenCollapsibleStack(
|
||||
|
@ -169,7 +173,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
i18n.t('screens.game.title'),
|
||||
true,
|
||||
undefined,
|
||||
'transparent',
|
||||
'transparent'
|
||||
)}
|
||||
<MainStack.Screen
|
||||
name={MainRoutes.GameMain}
|
||||
|
@ -185,7 +189,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
i18n.t('screens.login.title'),
|
||||
true,
|
||||
{ headerTintColor: '#fff' },
|
||||
'transparent',
|
||||
'transparent'
|
||||
)}
|
||||
{getWebsiteStack('website', MainStack, WebsiteScreen, '')}
|
||||
|
||||
|
@ -193,19 +197,19 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
MainRoutes.SelfMenu,
|
||||
MainStack,
|
||||
SelfMenuScreen,
|
||||
i18n.t('screens.menu.title'),
|
||||
i18n.t('screens.menu.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Proximo,
|
||||
MainStack,
|
||||
ProximoMainScreen,
|
||||
i18n.t('screens.proximo.title'),
|
||||
i18n.t('screens.proximo.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.ProximoList,
|
||||
MainStack,
|
||||
ProximoListScreen,
|
||||
i18n.t('screens.proximo.articleList'),
|
||||
i18n.t('screens.proximo.articleList')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.ProximoAbout,
|
||||
|
@ -213,20 +217,20 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
ProximoAboutScreen,
|
||||
i18n.t('screens.proximo.title'),
|
||||
true,
|
||||
{...modalTransition},
|
||||
{ ...modalTransition }
|
||||
)}
|
||||
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Profile,
|
||||
MainStack,
|
||||
ProfileScreen,
|
||||
i18n.t('screens.profile.title'),
|
||||
i18n.t('screens.profile.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.ClubList,
|
||||
MainStack,
|
||||
ClubListScreen,
|
||||
i18n.t('screens.clubs.title'),
|
||||
i18n.t('screens.clubs.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.ClubInformation,
|
||||
|
@ -234,7 +238,7 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
ClubDisplayScreen,
|
||||
i18n.t('screens.clubs.details'),
|
||||
true,
|
||||
{...modalTransition},
|
||||
{ ...modalTransition }
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.ClubAbout,
|
||||
|
@ -242,37 +246,37 @@ function MainStackComponent(props: {createTabNavigator: () => JSX.Element}) {
|
|||
ClubAboutScreen,
|
||||
i18n.t('screens.clubs.title'),
|
||||
true,
|
||||
{...modalTransition},
|
||||
{ ...modalTransition }
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.EquipmentList,
|
||||
MainStack,
|
||||
EquipmentScreen,
|
||||
i18n.t('screens.equipment.title'),
|
||||
i18n.t('screens.equipment.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.EquipmentRent,
|
||||
MainStack,
|
||||
EquipmentLendScreen,
|
||||
i18n.t('screens.equipment.book'),
|
||||
i18n.t('screens.equipment.book')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.EquipmentConfirm,
|
||||
MainStack,
|
||||
EquipmentConfirmScreen,
|
||||
i18n.t('screens.equipment.confirm'),
|
||||
i18n.t('screens.equipment.confirm')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Vote,
|
||||
MainStack,
|
||||
VoteScreen,
|
||||
i18n.t('screens.vote.title'),
|
||||
i18n.t('screens.vote.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
MainRoutes.Feedback,
|
||||
MainStack,
|
||||
BugReportScreen,
|
||||
i18n.t('screens.feedback.title'),
|
||||
i18n.t('screens.feedback.title')
|
||||
)}
|
||||
</MainStack.Navigator>
|
||||
);
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
} from '@react-navigation/stack';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
|
||||
import { Title, useTheme } from 'react-native-paper';
|
||||
import {Platform} from 'react-native';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import { createCollapsibleStack } from 'react-navigation-collapsible';
|
||||
import { View } from 'react-native-animatable';
|
||||
|
@ -58,6 +61,20 @@ const defaultScreenOptions = {
|
|||
...modalTransition,
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
mascot: {
|
||||
width: 50,
|
||||
},
|
||||
title: {
|
||||
marginLeft: 10,
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
const ServicesStack = createStackNavigator();
|
||||
|
||||
function ServicesStackComponent() {
|
||||
|
@ -65,24 +82,25 @@ function ServicesStackComponent() {
|
|||
<ServicesStack.Navigator
|
||||
initialRouteName="index"
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
{CreateScreenCollapsibleStack(
|
||||
'index',
|
||||
ServicesStack,
|
||||
WebsitesHomeScreen,
|
||||
i18n.t('screens.services.title'),
|
||||
i18n.t('screens.services.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'services-section',
|
||||
ServicesStack,
|
||||
ServicesSectionScreen,
|
||||
'SECTION',
|
||||
'SECTION'
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'amicale-contact',
|
||||
ServicesStack,
|
||||
AmicaleContactScreen,
|
||||
i18n.t('screens.amicaleAbout.title'),
|
||||
i18n.t('screens.amicaleAbout.title')
|
||||
)}
|
||||
</ServicesStack.Navigator>
|
||||
);
|
||||
|
@ -95,18 +113,19 @@ function ProxiwashStackComponent() {
|
|||
<ProxiwashStack.Navigator
|
||||
initialRouteName="index"
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
{CreateScreenCollapsibleStack(
|
||||
'index',
|
||||
ProxiwashStack,
|
||||
ProxiwashScreen,
|
||||
i18n.t('screens.proxiwash.title'),
|
||||
i18n.t('screens.proxiwash.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'proxiwash-about',
|
||||
ProxiwashStack,
|
||||
ProxiwashAboutScreen,
|
||||
i18n.t('screens.proxiwash.title'),
|
||||
i18n.t('screens.proxiwash.title')
|
||||
)}
|
||||
</ProxiwashStack.Navigator>
|
||||
);
|
||||
|
@ -119,7 +138,8 @@ function PlanningStackComponent() {
|
|||
<PlanningStack.Navigator
|
||||
initialRouteName="index"
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
<PlanningStack.Screen
|
||||
name="index"
|
||||
component={PlanningScreen}
|
||||
|
@ -129,7 +149,7 @@ function PlanningStackComponent() {
|
|||
'planning-information',
|
||||
PlanningStack,
|
||||
PlanningDisplayScreen,
|
||||
i18n.t('screens.planning.eventDetails'),
|
||||
i18n.t('screens.planning.eventDetails')
|
||||
)}
|
||||
</PlanningStack.Navigator>
|
||||
);
|
||||
|
@ -139,7 +159,7 @@ const HomeStack = createStackNavigator();
|
|||
|
||||
function HomeStackComponent(
|
||||
initialRoute: string | null,
|
||||
defaultData: {[key: string]: string},
|
||||
defaultData: { [key: string]: string }
|
||||
) {
|
||||
let params;
|
||||
if (initialRoute) {
|
||||
|
@ -150,7 +170,8 @@ function HomeStackComponent(
|
|||
<HomeStack.Navigator
|
||||
initialRouteName="index"
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
{createCollapsibleStack(
|
||||
<HomeStack.Screen
|
||||
name="index"
|
||||
|
@ -161,11 +182,9 @@ function HomeStackComponent(
|
|||
backgroundColor: colors.surface,
|
||||
},
|
||||
headerTitle: () => (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={styles.header}>
|
||||
<Mascot
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
style={styles.mascot}
|
||||
emotion={MASCOT_STYLE.RANDOM}
|
||||
animated
|
||||
entryAnimation={{
|
||||
|
@ -178,12 +197,7 @@ function HomeStackComponent(
|
|||
iterationCount: 'infinite',
|
||||
}}
|
||||
/>
|
||||
<Title
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
<Title style={styles.title}>
|
||||
{i18n.t('screens.home.title')}
|
||||
</Title>
|
||||
</View>
|
||||
|
@ -194,7 +208,7 @@ function HomeStackComponent(
|
|||
{
|
||||
collapsedColor: colors.surface,
|
||||
useNativeDriver: true,
|
||||
},
|
||||
}
|
||||
)}
|
||||
<HomeStack.Screen
|
||||
name="scanner"
|
||||
|
@ -206,19 +220,19 @@ function HomeStackComponent(
|
|||
'club-information',
|
||||
HomeStack,
|
||||
ClubDisplayScreen,
|
||||
i18n.t('screens.clubs.details'),
|
||||
i18n.t('screens.clubs.details')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'feed-information',
|
||||
HomeStack,
|
||||
FeedItemScreen,
|
||||
i18n.t('screens.home.feed'),
|
||||
i18n.t('screens.home.feed')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'planning-information',
|
||||
HomeStack,
|
||||
PlanningDisplayScreen,
|
||||
i18n.t('screens.planning.eventDetails'),
|
||||
i18n.t('screens.planning.eventDetails')
|
||||
)}
|
||||
</HomeStack.Navigator>
|
||||
);
|
||||
|
@ -231,18 +245,19 @@ function PlanexStackComponent() {
|
|||
<PlanexStack.Navigator
|
||||
initialRouteName="index"
|
||||
headerMode="screen"
|
||||
screenOptions={defaultScreenOptions}>
|
||||
screenOptions={defaultScreenOptions}
|
||||
>
|
||||
{getWebsiteStack(
|
||||
'index',
|
||||
PlanexStack,
|
||||
PlanexScreen,
|
||||
i18n.t('screens.planex.title'),
|
||||
i18n.t('screens.planex.title')
|
||||
)}
|
||||
{CreateScreenCollapsibleStack(
|
||||
'group-select',
|
||||
PlanexStack,
|
||||
GroupSelectionScreen,
|
||||
'',
|
||||
''
|
||||
)}
|
||||
</PlanexStack.Navigator>
|
||||
);
|
||||
|
@ -264,7 +279,7 @@ export default class TabNavigator extends React.Component<PropsType> {
|
|||
this.defaultRoute = 'home';
|
||||
if (!props.defaultHomeRoute) {
|
||||
this.defaultRoute = AsyncStorageManager.getString(
|
||||
AsyncStorageManager.PREFERENCES.defaultStartScreen.key,
|
||||
AsyncStorageManager.PREFERENCES.defaultStartScreen.key
|
||||
).toLowerCase();
|
||||
}
|
||||
this.createHomeStackComponent = () =>
|
||||
|
@ -275,7 +290,8 @@ export default class TabNavigator extends React.Component<PropsType> {
|
|||
return (
|
||||
<Tab.Navigator
|
||||
initialRouteName={this.defaultRoute}
|
||||
tabBar={(tabProps) => <CustomTabBar {...tabProps} />}>
|
||||
tabBar={(tabProps) => <CustomTabBar {...tabProps} />}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="services"
|
||||
component={ServicesStackComponent}
|
||||
|
|
|
@ -70,7 +70,7 @@ export default class AboutDependenciesScreen extends React.Component<{}> {
|
|||
|
||||
getItemLayout = (
|
||||
data: Array<ListItemType> | null | undefined,
|
||||
index: number,
|
||||
index: number
|
||||
): { length: number; offset: number; index: number } => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
*/
|
||||
|
||||
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 { Avatar, Card, List } from 'react-native-paper';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
|
@ -26,6 +33,7 @@ import packageJson from '../../../package.json';
|
|||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||
import OptionsDialog 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');
|
||||
|
||||
|
@ -69,6 +77,15 @@ type StateType = {
|
|||
dialogButtons: Array<OptionsDialogButtonType>;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
list: {
|
||||
padding: 5,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Opens a link in the device's browser
|
||||
* @param link The link to open
|
||||
|
@ -348,7 +365,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getAppCard() {
|
||||
return (
|
||||
<Card style={{marginBottom: 10}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title="Campus"
|
||||
subtitle={packageJson.version}
|
||||
|
@ -377,7 +394,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getTeamCard() {
|
||||
return (
|
||||
<Card style={{marginBottom: 10}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.about.team')}
|
||||
left={(iconProps) => (
|
||||
|
@ -402,7 +419,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getThanksCard() {
|
||||
return (
|
||||
<Card style={{marginBottom: 10}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.about.thanks')}
|
||||
left={(iconProps) => (
|
||||
|
@ -427,7 +444,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getTechnoCard() {
|
||||
return (
|
||||
<Card style={{marginBottom: 10}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.about.technologies')}
|
||||
left={(iconProps) => (
|
||||
|
@ -478,7 +495,7 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
marginRight: number;
|
||||
marginVertical?: number;
|
||||
};
|
||||
},
|
||||
}
|
||||
) {
|
||||
return (
|
||||
<List.Icon color={props.color} style={props.style} icon={item.icon} />
|
||||
|
@ -553,12 +570,9 @@ class AboutScreen extends React.Component<PropsType, StateType> {
|
|||
render() {
|
||||
const { state } = this;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: '100%',
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<CollapsibleFlatList
|
||||
style={{padding: 5}}
|
||||
style={styles.list}
|
||||
data={this.dataOrder}
|
||||
renderItem={this.getMainCard}
|
||||
/>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import {
|
||||
Button,
|
||||
List,
|
||||
|
@ -47,6 +47,17 @@ type StateType = {
|
|||
currentPreferences: Array<PreferenceItemType>;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 10,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Class defining the Debug screen.
|
||||
* This screen allows the user to get and modify information on the app/device.
|
||||
|
@ -95,11 +106,7 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
}}>
|
||||
<View style={styles.container}>
|
||||
<Title>{key}</Title>
|
||||
<Subheading>Default: {defaultValue}</Subheading>
|
||||
<Subheading>Current: {current}</Subheading>
|
||||
|
@ -109,18 +116,15 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
|||
this.modalInputValue = text;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginTop: 10,
|
||||
}}>
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
dark
|
||||
color={props.theme.colors.success}
|
||||
onPress={() => {
|
||||
this.saveNewPrefs(key, this.modalInputValue);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Save new value
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -129,7 +133,8 @@ class DebugScreen extends React.Component<PropsType, StateType> {
|
|||
color={props.theme.colors.danger}
|
||||
onPress={() => {
|
||||
this.saveNewPrefs(key, defaultValue);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Reset to default
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
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 i18n from 'i18n-js';
|
||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||
|
@ -31,6 +31,24 @@ type DatasetItemType = {
|
|||
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.
|
||||
*/
|
||||
|
@ -129,22 +147,14 @@ class AmicaleContactScreen extends React.Component<{}> {
|
|||
getScreen = () => {
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 100,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<View style={styles.imageContainer}>
|
||||
<Image
|
||||
source={AMICALE_LOGO}
|
||||
style={{flex: 1, resizeMode: 'contain'}}
|
||||
style={styles.image}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.amicaleAbout.title')}
|
||||
subtitle={i18n.t('screens.amicaleAbout.subtitle')}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
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 i18n from 'i18n-js';
|
||||
import Autolink from 'react-native-autolink';
|
||||
|
@ -27,26 +27,39 @@ const AMICALE_ICON = require('../../../../assets/amicale.png');
|
|||
|
||||
const CONTACT_LINK = 'clubs@amicale-insat.fr';
|
||||
|
||||
function ClubAboutScreen() {
|
||||
return (
|
||||
<CollapsibleScrollView style={{padding: 5}}>
|
||||
<View
|
||||
style={{
|
||||
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() {
|
||||
return (
|
||||
<CollapsibleScrollView style={styles.container}>
|
||||
<View style={styles.imageContainer}>
|
||||
<Image
|
||||
source={AMICALE_ICON}
|
||||
style={{flex: 1, resizeMode: 'contain'}}
|
||||
style={styles.image}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
<Text>{i18n.t('screens.clubs.about.text')}</Text>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.clubs.about.title')}
|
||||
subtitle={i18n.t('screens.clubs.about.subtitle')}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Linking, View} from 'react-native';
|
||||
import { Linking, StyleSheet, View } from 'react-native';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
|
@ -51,6 +51,37 @@ type PropsType = {
|
|||
|
||||
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.
|
||||
* 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) => {
|
||||
if (cat != null) {
|
||||
final.push(
|
||||
<Chip style={{marginRight: 5}} key={cat}>
|
||||
<Chip style={styles.category} key={cat}>
|
||||
{this.getCategoryName(cat)}
|
||||
</Chip>,
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
});
|
||||
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
|
||||
return <View style={styles.categoryContainer}>{final}</View>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,7 +173,11 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
const hasManagers = managers.length > 0;
|
||||
return (
|
||||
<Card
|
||||
style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
style={{
|
||||
marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20,
|
||||
...styles.card,
|
||||
}}
|
||||
>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.clubs.managers')}
|
||||
subtitle={
|
||||
|
@ -153,7 +188,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
left={(iconProps) => (
|
||||
<Avatar.Icon
|
||||
size={iconProps.size}
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
style={styles.icon}
|
||||
color={
|
||||
hasManagers
|
||||
? props.theme.colors.success
|
||||
|
@ -193,7 +228,8 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
onPress={() => {
|
||||
Linking.openURL(`mailto:${destinationEmail}`);
|
||||
}}
|
||||
style={{marginLeft: 'auto'}}>
|
||||
style={styles.emailButton}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
@ -205,19 +241,12 @@ class ClubDisplayScreen extends React.Component<PropsType> {
|
|||
if (data != null) {
|
||||
this.updateHeaderTitle(data);
|
||||
return (
|
||||
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
|
||||
<CollapsibleScrollView style={styles.scroll} hasTab>
|
||||
{this.getCategoriesRender(data.category)}
|
||||
{data.logo !== null ? (
|
||||
<ImageGalleryButton
|
||||
images={[{ url: data.logo }]}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
style={styles.imageButton}
|
||||
/>
|
||||
) : (
|
||||
<View />
|
||||
|
|
|
@ -24,7 +24,10 @@ import i18n from 'i18n-js';
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen';
|
||||
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 MaterialHeaderButtons, {
|
||||
Item,
|
||||
|
@ -147,7 +150,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
|||
data: Array<{
|
||||
categories: Array<ClubCategoryType>;
|
||||
clubs: Array<ClubType>;
|
||||
} | null>,
|
||||
} | null>
|
||||
) => {
|
||||
let categoryList: Array<ClubCategoryType> = [];
|
||||
let clubList: Array<ClubType> = [];
|
||||
|
@ -222,7 +225,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
|
|||
|
||||
itemLayout = (
|
||||
data: Array<ClubType> | null | undefined,
|
||||
index: number,
|
||||
index: number
|
||||
): { length: number; offset: number; index: number } => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
|
|
|
@ -26,12 +26,13 @@ import {
|
|||
Title,
|
||||
useTheme,
|
||||
} from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import { getRelativeDateString } from '../../../utils/EquipmentBooking';
|
||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
import { StackScreenProps } from '@react-navigation/stack';
|
||||
import { MainStackParamsList } from '../../../navigation/MainNavigator';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type EquipmentConfirmScreenNavigationProp = StackScreenProps<
|
||||
MainStackParamsList,
|
||||
|
@ -40,6 +41,32 @@ type EquipmentConfirmScreenNavigationProp = StackScreenProps<
|
|||
|
||||
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) {
|
||||
const theme = useTheme();
|
||||
const item = props.route.params?.item;
|
||||
|
@ -63,31 +90,20 @@ function EquipmentConfirmScreen(props: Props) {
|
|||
}
|
||||
return (
|
||||
<CollapsibleScrollView>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Content>
|
||||
<View style={{flex: 1}}>
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
}}>
|
||||
<Headline style={{textAlign: 'center'}}>{item.name}</Headline>
|
||||
<Caption
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: 35,
|
||||
marginLeft: 10,
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Headline style={styles.title}>{item.name}</Headline>
|
||||
<Caption style={styles.caption}>
|
||||
({i18n.t('screens.equipment.bail', { cost: item.caution })})
|
||||
</Caption>
|
||||
</View>
|
||||
</View>
|
||||
<Title style={{color: theme.colors.success, textAlign: 'center'}}>
|
||||
<Title style={{ color: theme.colors.success, ...styles.subtitle }}>
|
||||
{buttonText}
|
||||
</Title>
|
||||
<Paragraph style={{textAlign: 'center'}}>
|
||||
<Paragraph style={styles.text}>
|
||||
{i18n.t('screens.equipment.bookingConfirmedMessage')}
|
||||
</Paragraph>
|
||||
</Card.Content>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import i18n from 'i18n-js';
|
||||
|
@ -28,6 +28,7 @@ import MascotPopup from '../../../components/Mascot/MascotPopup';
|
|||
import { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
||||
import AsyncStorageManager from '../../../managers/AsyncStorageManager';
|
||||
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
|
@ -53,6 +54,14 @@ export type RentedDeviceType = {
|
|||
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerContainer: {
|
||||
width: '100%',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
});
|
||||
|
||||
class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
||||
userRents: null | Array<RentedDeviceType>;
|
||||
|
||||
|
@ -65,7 +74,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
|||
this.userRents = null;
|
||||
this.state = {
|
||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
|
||||
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key
|
||||
),
|
||||
};
|
||||
this.canRefresh = false;
|
||||
|
@ -115,20 +124,13 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getListHeader() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<View style={styles.headerContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="help-circle"
|
||||
onPress={this.showMascotDialog}
|
||||
style={{
|
||||
marginRight: 'auto',
|
||||
marginLeft: 'auto',
|
||||
}}>
|
||||
style={GENERAL_STYLES.centerHorizontal}
|
||||
>
|
||||
{i18n.t('screens.equipment.mascotDialog.title')}
|
||||
</Button>
|
||||
</View>
|
||||
|
@ -145,8 +147,10 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getScreen = (
|
||||
data: Array<
|
||||
{devices: Array<DeviceType>} | {locations: Array<RentedDeviceType>} | null
|
||||
>,
|
||||
| { devices: Array<DeviceType> }
|
||||
| { locations: Array<RentedDeviceType> }
|
||||
| null
|
||||
>
|
||||
) => {
|
||||
const [allDevices, userRents] = data;
|
||||
if (userRents) {
|
||||
|
@ -175,7 +179,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
|||
hideMascotDialog = () => {
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
this.setState({ mascotDialogVisible: false });
|
||||
};
|
||||
|
@ -183,7 +187,7 @@ class EquipmentListScreen extends React.Component<PropsType, StateType> {
|
|||
render() {
|
||||
const { props, state } = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<AuthenticatedScreen
|
||||
navigation={props.navigation}
|
||||
ref={this.authRef}
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
withTheme,
|
||||
} from 'react-native-paper';
|
||||
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 i18n from 'i18n-js';
|
||||
import { CalendarList, PeriodMarking } from 'react-native-calendars';
|
||||
|
@ -45,6 +45,7 @@ import {
|
|||
import ConnectionManager from '../../../managers/ConnectionManager';
|
||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
import { MainStackParamsList } from '../../../navigation/MainNavigator';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type EquipmentRentScreenNavigationProp = StackScreenProps<
|
||||
MainStackParamsList,
|
||||
|
@ -67,6 +68,50 @@ type StateType = {
|
|||
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> {
|
||||
item: DeviceType | null;
|
||||
|
||||
|
@ -108,7 +153,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
const range = getValidRange(
|
||||
new Date(date.begin),
|
||||
new Date(date.end),
|
||||
null,
|
||||
null
|
||||
);
|
||||
this.lockedDates = {
|
||||
...this.lockedDates,
|
||||
|
@ -126,13 +171,13 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
navigation.addListener('focus', () => {
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
this.onBackButtonPressAndroid
|
||||
);
|
||||
});
|
||||
navigation.addListener('blur', () => {
|
||||
BackHandler.removeEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
this.onBackButtonPressAndroid
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -315,28 +360,17 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
const isAvailable = isEquipmentAvailable(item);
|
||||
const firstAvailability = getFirstEquipmentAvailability(item);
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<CollapsibleScrollView>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card style={styles.card}>
|
||||
<Card.Content>
|
||||
<View style={{flex: 1}}>
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
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})})
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Headline style={styles.title}>{item.name}</Headline>
|
||||
<Caption style={styles.caption}>
|
||||
(
|
||||
{i18n.t('screens.equipment.bail', { cost: item.caution })}
|
||||
)
|
||||
</Caption>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -348,17 +382,13 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
? props.theme.colors.success
|
||||
: props.theme.colors.primary
|
||||
}
|
||||
mode="text">
|
||||
mode="text"
|
||||
>
|
||||
{i18n.t('screens.equipment.available', {
|
||||
date: getRelativeDateString(firstAvailability),
|
||||
})}
|
||||
</Button>
|
||||
<Subheading
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
minHeight: 50,
|
||||
}}>
|
||||
<Subheading style={styles.subtitle}>
|
||||
{subHeadingText}
|
||||
</Subheading>
|
||||
</Card.Content>
|
||||
|
@ -384,28 +414,28 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
markingType={'period'}
|
||||
markedDates={{ ...this.lockedDates, ...state.markedDates }}
|
||||
theme={{
|
||||
backgroundColor: props.theme.colors.agendaBackgroundColor,
|
||||
calendarBackground: props.theme.colors.background,
|
||||
textSectionTitleColor: props.theme.colors.agendaDayTextColor,
|
||||
selectedDayBackgroundColor: props.theme.colors.primary,
|
||||
selectedDayTextColor: '#ffffff',
|
||||
todayTextColor: props.theme.colors.text,
|
||||
dayTextColor: props.theme.colors.text,
|
||||
textDisabledColor: props.theme.colors.agendaDayTextColor,
|
||||
dotColor: props.theme.colors.primary,
|
||||
selectedDotColor: '#ffffff',
|
||||
arrowColor: props.theme.colors.primary,
|
||||
monthTextColor: props.theme.colors.text,
|
||||
indicatorColor: props.theme.colors.primary,
|
||||
textDayFontFamily: 'monospace',
|
||||
textMonthFontFamily: 'monospace',
|
||||
textDayHeaderFontFamily: 'monospace',
|
||||
textDayFontWeight: '300',
|
||||
textMonthFontWeight: 'bold',
|
||||
textDayHeaderFontWeight: '300',
|
||||
textDayFontSize: 16,
|
||||
textMonthFontSize: 16,
|
||||
textDayHeaderFontSize: 16,
|
||||
'backgroundColor': props.theme.colors.agendaBackgroundColor,
|
||||
'calendarBackground': props.theme.colors.background,
|
||||
'textSectionTitleColor': props.theme.colors.agendaDayTextColor,
|
||||
'selectedDayBackgroundColor': props.theme.colors.primary,
|
||||
'selectedDayTextColor': '#ffffff',
|
||||
'todayTextColor': props.theme.colors.text,
|
||||
'dayTextColor': props.theme.colors.text,
|
||||
'textDisabledColor': props.theme.colors.agendaDayTextColor,
|
||||
'dotColor': props.theme.colors.primary,
|
||||
'selectedDotColor': '#ffffff',
|
||||
'arrowColor': props.theme.colors.primary,
|
||||
'monthTextColor': props.theme.colors.text,
|
||||
'indicatorColor': props.theme.colors.primary,
|
||||
'textDayFontFamily': 'monospace',
|
||||
'textMonthFontFamily': 'monospace',
|
||||
'textDayHeaderFontFamily': 'monospace',
|
||||
'textDayFontWeight': '300',
|
||||
'textMonthFontWeight': 'bold',
|
||||
'textDayHeaderFontWeight': '300',
|
||||
'textDayFontSize': 16,
|
||||
'textMonthFontSize': 16,
|
||||
'textDayHeaderFontSize': 16,
|
||||
'stylesheet.day.period': {
|
||||
base: {
|
||||
overflow: 'hidden',
|
||||
|
@ -415,7 +445,7 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
},
|
||||
},
|
||||
}}
|
||||
style={{marginBottom: 50}}
|
||||
style={styles.calendar}
|
||||
/>
|
||||
</CollapsibleScrollView>
|
||||
<LoadingConfirmDialog
|
||||
|
@ -435,26 +465,14 @@ class EquipmentRentScreen extends React.Component<Props, StateType> {
|
|||
<Animatable.View
|
||||
ref={this.bookRef}
|
||||
useNativeDriver
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
flex: 1,
|
||||
transform: [{translateY: 100}],
|
||||
}}>
|
||||
style={styles.buttonContainer}
|
||||
>
|
||||
<Button
|
||||
icon="bookmark-check"
|
||||
mode="contained"
|
||||
onPress={this.showDialog}
|
||||
style={{
|
||||
width: '80%',
|
||||
flex: 1,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 20,
|
||||
borderRadius: 10,
|
||||
}}>
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t('screens.equipment.bookButton')}
|
||||
</Button>
|
||||
</Animatable.View>
|
||||
|
|
|
@ -37,6 +37,7 @@ import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
|||
import MascotPopup from '../../components/Mascot/MascotPopup';
|
||||
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
||||
import { MainStackParamsList } from '../../navigation/MainNavigator';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type LoginScreenNavigationProp = StackScreenProps<MainStackParamsList, 'login'>;
|
||||
|
||||
|
@ -63,9 +64,6 @@ const RESET_PASSWORD_PATH = 'https://www.amicale-insat.fr/password/reset';
|
|||
const emailRegex = /^.+@.+\..+$/;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
card: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
|
@ -74,10 +72,18 @@ const styles = StyleSheet.create({
|
|||
fontSize: 36,
|
||||
marginBottom: 48,
|
||||
},
|
||||
textInput: {},
|
||||
btnContainer: {
|
||||
marginTop: 5,
|
||||
marginBottom: 10,
|
||||
text: {
|
||||
color: '#ffffff',
|
||||
},
|
||||
buttonContainer: {
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
lockButton: {
|
||||
marginRight: 'auto',
|
||||
marginBottom: 20,
|
||||
},
|
||||
sendButton: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -113,7 +119,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
dialogVisible: false,
|
||||
dialogError: 0,
|
||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.loginShowMascot.key,
|
||||
AsyncStorageManager.PREFERENCES.loginShowMascot.key
|
||||
),
|
||||
};
|
||||
}
|
||||
|
@ -249,9 +255,9 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
<View style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.login.title')}
|
||||
titleStyle={{color: '#fff'}}
|
||||
titleStyle={styles.text}
|
||||
subtitle={i18n.t('screens.login.subtitle')}
|
||||
subtitleStyle={{color: '#fff'}}
|
||||
subtitleStyle={styles.text}
|
||||
left={({ size }) => (
|
||||
<Image
|
||||
source={ICON_AMICALE}
|
||||
|
@ -264,13 +270,14 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
/>
|
||||
<Card.Content>
|
||||
{this.getFormInput()}
|
||||
<Card.Actions style={{flexWrap: 'wrap'}}>
|
||||
<Card.Actions style={styles.buttonContainer}>
|
||||
<Button
|
||||
icon="lock-question"
|
||||
mode="contained"
|
||||
onPress={this.onResetPasswordClick}
|
||||
color={props.theme.colors.warning}
|
||||
style={{marginRight: 'auto', marginBottom: 20}}>
|
||||
style={styles.lockButton}
|
||||
>
|
||||
{i18n.t('screens.login.resetPassword')}
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -279,7 +286,8 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
disabled={!this.shouldEnableLogin()}
|
||||
loading={state.loading}
|
||||
onPress={this.onSubmit}
|
||||
style={{marginLeft: 'auto'}}>
|
||||
style={styles.sendButton}
|
||||
>
|
||||
{i18n.t('screens.login.title')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
@ -288,10 +296,8 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
icon="help-circle"
|
||||
mode="contained"
|
||||
onPress={this.showMascotDialog}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
style={GENERAL_STYLES.centerHorizontal}
|
||||
>
|
||||
{i18n.t('screens.login.mascotDialog.title')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
@ -317,7 +323,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
hideMascotDialog = () => {
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.loginShowMascot.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
this.setState({ mascotDialogVisible: false });
|
||||
};
|
||||
|
@ -351,7 +357,7 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
// Do not show the home login banner again
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.homeShowMascot.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
if (this.nextScreen == null) {
|
||||
navigation.goBack();
|
||||
|
@ -423,20 +429,20 @@ class LoginScreen extends React.Component<Props, StateType> {
|
|||
const { mascotDialogVisible, dialogVisible, dialogError } = this.state;
|
||||
return (
|
||||
<LinearGradient
|
||||
style={{
|
||||
height: '100%',
|
||||
}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
colors={['#9e0d18', '#530209']}
|
||||
start={{ x: 0, y: 0.1 }}
|
||||
end={{x: 0.1, y: 1}}>
|
||||
end={{ x: 0.1, y: 1 }}
|
||||
>
|
||||
<KeyboardAvoidingView
|
||||
behavior="height"
|
||||
contentContainerStyle={styles.container}
|
||||
style={styles.container}
|
||||
contentContainerStyle={GENERAL_STYLES.flex}
|
||||
style={GENERAL_STYLES.flex}
|
||||
enabled
|
||||
keyboardVerticalOffset={100}>
|
||||
keyboardVerticalOffset={100}
|
||||
>
|
||||
<CollapsibleScrollView>
|
||||
<View style={{height: '100%'}}>{this.getMainCard()}</View>
|
||||
<View style={GENERAL_STYLES.flex}>{this.getMainCard()}</View>
|
||||
<MascotPopup
|
||||
visible={mascotDialogVisible}
|
||||
title={i18n.t('screens.login.mascotDialog.title')}
|
||||
|
|
|
@ -41,6 +41,7 @@ import Mascot, {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
|||
import ServicesManager, { SERVICES_KEY } from '../../managers/ServicesManager';
|
||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||
import type { ServiceItemType } from '../../managers/ServicesManager';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
|
@ -79,6 +80,12 @@ const styles = StyleSheet.create({
|
|||
editButton: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
mascot: {
|
||||
width: 60,
|
||||
},
|
||||
title: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
});
|
||||
|
||||
class ProfileScreen extends React.Component<PropsType, StateType> {
|
||||
|
@ -131,7 +138,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
|||
const { dialogVisible } = this.state;
|
||||
this.data = data[0];
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<CollapsibleFlatList
|
||||
renderItem={this.getRenderItem}
|
||||
data={this.flatListData}
|
||||
|
@ -181,9 +188,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
|||
})}
|
||||
left={() => (
|
||||
<Mascot
|
||||
style={{
|
||||
width: 60,
|
||||
}}
|
||||
style={styles.mascot}
|
||||
emotion={MASCOT_STYLE.COOL}
|
||||
animated
|
||||
entryAnimation={{
|
||||
|
@ -192,7 +197,7 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
titleStyle={{marginLeft: 10}}
|
||||
titleStyle={styles.title}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Divider />
|
||||
|
@ -207,7 +212,8 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
|||
onPress={() => {
|
||||
navigation.navigate('feedback');
|
||||
}}
|
||||
style={styles.editButton}>
|
||||
style={styles.editButton}
|
||||
>
|
||||
{i18n.t('screens.feedback.homeButtonTitle')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
@ -297,7 +303,8 @@ class ProfileScreen extends React.Component<PropsType, StateType> {
|
|||
title: i18n.t('screens.websites.amicale'),
|
||||
});
|
||||
}}
|
||||
style={styles.editButton}>
|
||||
style={styles.editButton}
|
||||
>
|
||||
{i18n.t('screens.profile.editInformation')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
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 i18n from 'i18n-js';
|
||||
import { Button } from 'react-native-paper';
|
||||
|
@ -33,6 +33,7 @@ import MascotPopup from '../../components/Mascot/MascotPopup';
|
|||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||
import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
|
||||
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||
import GENERAL_STYLES from '../../constants/Styles';
|
||||
|
||||
export type VoteTeamType = {
|
||||
id: number;
|
||||
|
@ -118,6 +119,14 @@ type StateType = {
|
|||
mascotDialogVisible: boolean;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Screen displaying vote information and controls
|
||||
*/
|
||||
|
@ -146,7 +155,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
this.state = {
|
||||
hasVoted: false,
|
||||
mascotDialogVisible: AsyncStorageManager.getBool(
|
||||
AsyncStorageManager.PREFERENCES.voteShowMascot.key,
|
||||
AsyncStorageManager.PREFERENCES.voteShowMascot.key
|
||||
),
|
||||
};
|
||||
this.hasVoted = false;
|
||||
|
@ -182,11 +191,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
mode="contained"
|
||||
icon="help-circle"
|
||||
onPress={this.showMascotDialog}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 20,
|
||||
}}>
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t('screens.vote.mascotDialog.title')}
|
||||
</Button>
|
||||
</View>
|
||||
|
@ -270,7 +276,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
teams={this.teams}
|
||||
dateEnd={this.getDateString(
|
||||
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
|
||||
startDate={this.getDateString(
|
||||
this.dates.date_begin,
|
||||
this.datesString.date_begin,
|
||||
this.datesString.date_begin
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
@ -308,7 +314,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
) {
|
||||
startDate = this.getDateString(
|
||||
this.dates.date_result_begin,
|
||||
this.datesString.date_result_begin,
|
||||
this.datesString.date_result_begin
|
||||
);
|
||||
}
|
||||
return (
|
||||
|
@ -345,7 +351,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
hideMascotDialog = () => {
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.voteShowMascot.key,
|
||||
false,
|
||||
false
|
||||
);
|
||||
this.setState({ mascotDialogVisible: false });
|
||||
};
|
||||
|
@ -414,7 +420,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
|
|||
render() {
|
||||
const { props, state } = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<AuthenticatedScreen<TeamResponseType | VoteDatesStringType>
|
||||
navigation={props.navigation}
|
||||
ref={this.authRef}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
export type CoordinatesType = {
|
||||
x: number;
|
||||
y: number;
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import GridManager from '../logic/GridManager';
|
||||
import ScoreManager from '../logic/ScoreManager';
|
||||
import Piece from '../logic/Piece';
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import Piece from '../logic/Piece';
|
||||
import ShapeI from '../Shapes/ShapeI';
|
||||
|
||||
|
@ -33,9 +30,7 @@ let theme = {
|
|||
jest.mock('../Shapes/ShapeI');
|
||||
|
||||
beforeAll(() => {
|
||||
jest
|
||||
.spyOn(Piece.prototype, 'getRandomShape')
|
||||
.mockImplementation((colors: Object) => {
|
||||
jest.spyOn(Piece.prototype, 'getRandomShape').mockImplementation((colors) => {
|
||||
return new ShapeI(colors);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import ScoreManager from '../logic/ScoreManager';
|
||||
|
||||
test('incrementScore', () => {
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
import BaseShape from '../Shapes/BaseShape';
|
||||
import ShapeI from '../Shapes/ShapeI';
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
export type CellType = { color: string; isEmpty: boolean; key: string };
|
||||
|
||||
|
@ -26,17 +26,24 @@ type PropsType = {
|
|||
cell: CellType;
|
||||
};
|
||||
|
||||
function CellComponent(props: PropsType) {
|
||||
const item = props.cell;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
const styles = StyleSheet.create({
|
||||
cell: {
|
||||
flex: 1,
|
||||
backgroundColor: item.isEmpty ? 'transparent' : item.color,
|
||||
borderColor: 'transparent',
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
aspectRatio: 1,
|
||||
},
|
||||
});
|
||||
|
||||
function CellComponent(props: PropsType) {
|
||||
const item = props.cell;
|
||||
const backgroundColor = item.isEmpty ? 'transparent' : item.color;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: backgroundColor,
|
||||
...styles.cell,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View, ViewStyle} from 'react-native';
|
||||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||
import type { CellType } from './CellComponent';
|
||||
import CellComponent from './CellComponent';
|
||||
|
||||
|
@ -31,13 +31,22 @@ type PropsType = {
|
|||
style: ViewStyle;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
grid: {
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
||||
const getCellRender = (item: CellType) => {
|
||||
return <CellComponent cell={item} key={item.key} />;
|
||||
};
|
||||
|
||||
function getRow(grid: GridType, rowNumber: number) {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}} key={rowNumber.toString()}>
|
||||
<View style={styles.row} key={rowNumber.toString()}>
|
||||
{grid[rowNumber].map(getCellRender)}
|
||||
</View>
|
||||
);
|
||||
|
@ -57,9 +66,10 @@ function GridComponent(props: PropsType) {
|
|||
<View
|
||||
style={{
|
||||
aspectRatio: width / height,
|
||||
borderRadius: 4,
|
||||
...style,
|
||||
}}>
|
||||
...styles.grid,
|
||||
}}
|
||||
>
|
||||
{getGrid(grid, height)}
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View, ViewStyle} from 'react-native';
|
||||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||
import type { GridType } from './GridComponent';
|
||||
import GridComponent from './GridComponent';
|
||||
|
||||
|
@ -27,17 +27,21 @@ type PropsType = {
|
|||
style: ViewStyle;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
grid: {
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
|
||||
function getGridRender(item: GridType, index: number) {
|
||||
return (
|
||||
<GridComponent
|
||||
width={item[0].length}
|
||||
height={item.length}
|
||||
grid={item}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
marginBottom: 5,
|
||||
}}
|
||||
style={styles.grid}
|
||||
key={index.toString()}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ import type {GridType} from '../components/GridComponent';
|
|||
export type TickCallbackType = (
|
||||
score: number,
|
||||
level: number,
|
||||
grid: GridType,
|
||||
grid: GridType
|
||||
) => void;
|
||||
|
||||
export type ClockCallbackType = (time: number) => void;
|
||||
|
@ -33,7 +33,7 @@ export type ClockCallbackType = (time: number) => void;
|
|||
export type EndCallbackType = (
|
||||
time: number,
|
||||
score: number,
|
||||
isRestart: boolean,
|
||||
isRestart: boolean
|
||||
) => void;
|
||||
|
||||
export type MovementCallbackType = (grid: GridType, score?: number) => void;
|
||||
|
@ -101,7 +101,7 @@ export default class GameLogic {
|
|||
this.gridManager = new GridManager(
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.theme,
|
||||
this.theme
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -179,14 +179,14 @@ export default class GameLogic {
|
|||
this.gridManager.getCurrentGrid(),
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.onFreeze,
|
||||
this.onFreeze
|
||||
);
|
||||
}
|
||||
if (callback) {
|
||||
callback(
|
||||
this.scoreManager.getScore(),
|
||||
this.scoreManager.getLevel(),
|
||||
this.gridManager.getCurrentGrid(),
|
||||
this.gridManager.getCurrentGrid()
|
||||
);
|
||||
}
|
||||
if (this.scoreManager.canLevelUp()) {
|
||||
|
@ -224,7 +224,7 @@ export default class GameLogic {
|
|||
isInitial: boolean,
|
||||
callback: MovementCallbackType,
|
||||
x: number,
|
||||
y: number,
|
||||
y: number
|
||||
) {
|
||||
if (!this.canUseInput() || !this.isPressedIn) {
|
||||
return;
|
||||
|
@ -237,14 +237,14 @@ export default class GameLogic {
|
|||
this.gridManager.getCurrentGrid(),
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.onFreeze,
|
||||
this.onFreeze
|
||||
);
|
||||
if (moved) {
|
||||
if (y === 1) {
|
||||
this.scoreManager.incrementScore();
|
||||
callback(
|
||||
this.gridManager.getCurrentGrid(),
|
||||
this.scoreManager.getScore(),
|
||||
this.scoreManager.getScore()
|
||||
);
|
||||
} else {
|
||||
callback(this.gridManager.getCurrentGrid());
|
||||
|
@ -254,7 +254,7 @@ export default class GameLogic {
|
|||
() => {
|
||||
this.movePressedRepeat(false, callback, x, y);
|
||||
},
|
||||
isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay,
|
||||
isInitial ? this.autoRepeatActivationDelay : this.autoRepeatDelay
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ export default class GameLogic {
|
|||
this.currentObject.tryRotate(
|
||||
this.gridManager.getCurrentGrid(),
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.getHeight()
|
||||
)
|
||||
) {
|
||||
callback(this.gridManager.getCurrentGrid());
|
||||
|
@ -315,7 +315,7 @@ export default class GameLogic {
|
|||
!this.currentObject.isPositionValid(
|
||||
this.gridManager.getCurrentGrid(),
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.getHeight()
|
||||
)
|
||||
) {
|
||||
this.endGame(false);
|
||||
|
@ -346,7 +346,7 @@ export default class GameLogic {
|
|||
startGame(
|
||||
tickCallback: TickCallbackType,
|
||||
clockCallback: ClockCallbackType,
|
||||
endCallback: EndCallbackType,
|
||||
endCallback: EndCallbackType
|
||||
) {
|
||||
if (this.gameRunning) {
|
||||
this.endGame(true);
|
||||
|
@ -359,7 +359,7 @@ export default class GameLogic {
|
|||
this.gridManager = new GridManager(
|
||||
this.getWidth(),
|
||||
this.getHeight(),
|
||||
this.theme,
|
||||
this.theme
|
||||
);
|
||||
this.nextPieces = [];
|
||||
this.generateNextPieces();
|
||||
|
@ -367,7 +367,7 @@ export default class GameLogic {
|
|||
tickCallback(
|
||||
this.scoreManager.getScore(),
|
||||
this.scoreManager.getLevel(),
|
||||
this.gridManager.getCurrentGrid(),
|
||||
this.gridManager.getCurrentGrid()
|
||||
);
|
||||
clockCallback(this.gameTime);
|
||||
this.startTick();
|
||||
|
|
|
@ -134,7 +134,7 @@ export default class GridManager {
|
|||
freezeTetromino(currentObject: Piece, scoreManager: ScoreManager) {
|
||||
this.clearLines(
|
||||
this.getLinesToClear(currentObject.getCoordinates()),
|
||||
scoreManager,
|
||||
scoreManager
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ export default class Piece {
|
|||
*/
|
||||
removeFromGrid(grid: GridType) {
|
||||
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
|
||||
true,
|
||||
true
|
||||
);
|
||||
pos.forEach((coordinates: CoordinatesType) => {
|
||||
grid[coordinates.y][coordinates.x] = {
|
||||
|
@ -85,7 +85,7 @@ export default class Piece {
|
|||
*/
|
||||
toGrid(grid: GridType, isPreview: boolean) {
|
||||
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
|
||||
!isPreview,
|
||||
!isPreview
|
||||
);
|
||||
pos.forEach((coordinates: CoordinatesType) => {
|
||||
grid[coordinates.y][coordinates.x] = {
|
||||
|
@ -107,7 +107,7 @@ export default class Piece {
|
|||
isPositionValid(grid: GridType, width: number, height: number): boolean {
|
||||
let isValid = true;
|
||||
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
|
||||
true,
|
||||
true
|
||||
);
|
||||
for (let i = 0; i < pos.length; i += 1) {
|
||||
if (
|
||||
|
@ -141,7 +141,7 @@ export default class Piece {
|
|||
grid: GridType,
|
||||
width: number,
|
||||
height: number,
|
||||
freezeCallback: () => void,
|
||||
freezeCallback: () => void
|
||||
): boolean {
|
||||
let newX = x;
|
||||
let newY = y;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { Caption, IconButton, Text, withTheme } from 'react-native-paper';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import i18n from 'i18n-js';
|
||||
|
@ -32,6 +32,7 @@ import MaterialHeaderButtons, {
|
|||
} from '../../../components/Overrides/CustomHeaderButton';
|
||||
import type { OptionsDialogButtonType } from '../../../components/Dialogs/OptionsDialog';
|
||||
import OptionsDialog from '../../../components/Dialogs/OptionsDialog';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
|
@ -52,6 +53,67 @@ type StateType = {
|
|||
onDialogDismiss: () => void;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
gridContainer: {
|
||||
flex: 4,
|
||||
},
|
||||
centerSmallMargin: {
|
||||
...GENERAL_STYLES.centerHorizontal,
|
||||
marginBottom: 5,
|
||||
},
|
||||
centerVerticalSmallMargin: {
|
||||
...GENERAL_STYLES.centerVertical,
|
||||
marginLeft: 5,
|
||||
},
|
||||
centerBigMargin: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 20,
|
||||
},
|
||||
statusContainer: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
statusIcon: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
scoreMainContainer: {
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
scoreCurrentContainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
scoreText: {
|
||||
marginLeft: 5,
|
||||
fontSize: 20,
|
||||
},
|
||||
scoreBestContainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 5,
|
||||
},
|
||||
controlsContainer: {
|
||||
height: 80,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
directionsContainer: {
|
||||
flexDirection: 'row',
|
||||
flex: 4,
|
||||
},
|
||||
preview: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
},
|
||||
});
|
||||
|
||||
class GameMainScreen extends React.Component<PropsType, StateType> {
|
||||
static getFormattedTime(seconds: number): string {
|
||||
const date = new Date();
|
||||
|
@ -151,27 +213,15 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<Caption
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 5,
|
||||
}}>
|
||||
...GENERAL_STYLES.flex,
|
||||
...GENERAL_STYLES.centerVertical,
|
||||
}}
|
||||
>
|
||||
<View style={GENERAL_STYLES.centerHorizontal}>
|
||||
<Caption style={styles.centerSmallMargin}>
|
||||
{i18n.t('screens.game.time')}
|
||||
</Caption>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={styles.statusContainer}>
|
||||
<MaterialCommunityIcons
|
||||
name="timer"
|
||||
color={props.theme.colors.subtitle}
|
||||
|
@ -179,42 +229,25 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
/>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
...styles.statusIcon,
|
||||
color: props.theme.colors.subtitle,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{GameMainScreen.getFormattedTime(state.gameTime)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 20,
|
||||
}}>
|
||||
<Caption
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 5,
|
||||
}}>
|
||||
<View style={styles.centerBigMargin}>
|
||||
<Caption style={styles.centerSmallMargin}>
|
||||
{i18n.t('screens.game.level')}
|
||||
</Caption>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={styles.statusContainer}>
|
||||
<MaterialCommunityIcons
|
||||
name="gamepad-square"
|
||||
color={props.theme.colors.text}
|
||||
size={20}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
}}>
|
||||
{state.gameLevel}
|
||||
</Text>
|
||||
<Text style={styles.statusIcon}>{state.gameLevel}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -228,59 +261,32 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
? state.gameScore
|
||||
: this.highScore;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
fontSize: 20,
|
||||
}}>
|
||||
<View style={styles.scoreMainContainer}>
|
||||
<View style={styles.scoreCurrentContainer}>
|
||||
<Text style={styles.scoreText}>
|
||||
{i18n.t('screens.game.score', { score: state.gameScore })}
|
||||
</Text>
|
||||
<MaterialCommunityIcons
|
||||
name="star"
|
||||
color={props.theme.colors.tetrisScore}
|
||||
size={20}
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
marginLeft: 5,
|
||||
}}
|
||||
style={styles.centerVerticalSmallMargin}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 5,
|
||||
}}>
|
||||
<View style={styles.scoreBestContainer}>
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
fontSize: 10,
|
||||
...styles.scoreText,
|
||||
color: props.theme.colors.textDisabled,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{i18n.t('screens.game.highScore', { score: highScore })}
|
||||
</Text>
|
||||
<MaterialCommunityIcons
|
||||
name="star"
|
||||
color={props.theme.colors.tetrisScore}
|
||||
size={10}
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
marginLeft: 5,
|
||||
}}
|
||||
style={styles.centerVerticalSmallMargin}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -290,28 +296,20 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
getControlButtons() {
|
||||
const { props } = this;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: 80,
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={styles.controlsContainer}>
|
||||
<IconButton
|
||||
icon="rotate-right-variant"
|
||||
size={40}
|
||||
onPress={() => {
|
||||
this.logic.rotatePressed(this.updateGrid);
|
||||
}}
|
||||
style={{flex: 1}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
flex: 4,
|
||||
}}>
|
||||
<View style={styles.directionsContainer}>
|
||||
<IconButton
|
||||
icon="chevron-left"
|
||||
size={40}
|
||||
style={{flex: 1}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
onPress={() => {
|
||||
this.logic.pressedOut();
|
||||
}}
|
||||
|
@ -322,7 +320,7 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
<IconButton
|
||||
icon="chevron-right"
|
||||
size={40}
|
||||
style={{flex: 1}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
onPress={() => {
|
||||
this.logic.pressedOut();
|
||||
}}
|
||||
|
@ -340,7 +338,7 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
onPress={() => {
|
||||
this.logic.pressedOut();
|
||||
}}
|
||||
style={{flex: 1}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
color={props.theme.colors.tetrisScore}
|
||||
/>
|
||||
</View>
|
||||
|
@ -422,14 +420,10 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
render() {
|
||||
const { props, state } = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<View style={styles.container}>
|
||||
{this.getStatusIcons()}
|
||||
<View style={{flex: 4}}>
|
||||
<View style={styles.gridContainer}>
|
||||
{this.getScoreIcon()}
|
||||
<GridComponent
|
||||
width={this.logic.getWidth()}
|
||||
|
@ -437,21 +431,16 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
|||
grid={state.grid}
|
||||
style={{
|
||||
backgroundColor: props.theme.colors.tetrisBackground,
|
||||
flex: 1,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
...GENERAL_STYLES.flex,
|
||||
...GENERAL_STYLES.centerHorizontal,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<Preview
|
||||
items={this.logic.getNextPiecesPreviews()}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
}}
|
||||
style={styles.preview}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
Text,
|
||||
withTheme,
|
||||
} from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
|
@ -42,6 +42,7 @@ import GridManager from '../logic/GridManager';
|
|||
import Piece from '../logic/Piece';
|
||||
import SpeechArrow from '../../../components/Mascot/SpeechArrow';
|
||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
import GENERAL_STYLES from '../../../constants/Styles';
|
||||
|
||||
type GameStatsType = {
|
||||
score: number;
|
||||
|
@ -57,6 +58,87 @@ type PropsType = {
|
|||
theme: ReactNativePaper.Theme;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
pieceContainer: {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
pieceBackground: {
|
||||
position: 'absolute',
|
||||
},
|
||||
playButton: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
},
|
||||
recapCard: {
|
||||
borderWidth: 2,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
},
|
||||
recapContainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
recapScoreContainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
recapScore: {
|
||||
fontSize: 20,
|
||||
},
|
||||
recapScoreIcon: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
recapIcon: {
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
},
|
||||
welcomeMascot: {
|
||||
width: '40%',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
welcomeCard: {
|
||||
borderWidth: 2,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
centertext: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
welcomeText: {
|
||||
textAlign: 'center',
|
||||
marginTop: 10,
|
||||
},
|
||||
speechArrow: {
|
||||
marginLeft: '60%',
|
||||
},
|
||||
podiumContainer: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
podiumIconContainer: {
|
||||
position: 'absolute',
|
||||
top: -20,
|
||||
},
|
||||
topScoreContainer: {
|
||||
marginBottom: 20,
|
||||
marginTop: 20,
|
||||
},
|
||||
topScoreSubcontainer: {
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
class GameStartScreen extends React.Component<PropsType> {
|
||||
gridManager: GridManager;
|
||||
|
||||
|
@ -71,7 +153,7 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
this.isHighScore = false;
|
||||
this.gridManager = new GridManager(4, 4, props.theme);
|
||||
this.scores = AsyncStorageManager.getObject(
|
||||
AsyncStorageManager.PREFERENCES.gameScores.key,
|
||||
AsyncStorageManager.PREFERENCES.gameScores.key
|
||||
);
|
||||
this.scores.sort((a: number, b: number): number => b - a);
|
||||
if (props.route.params != null) {
|
||||
|
@ -88,12 +170,7 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
piece.toGrid(gridList[i], true);
|
||||
}
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}>
|
||||
<View style={styles.pieceContainer}>
|
||||
{gridList.map((item: GridType, index: number) => {
|
||||
const size = 10 + Math.floor(Math.random() * 30);
|
||||
const top = Math.floor(Math.random() * 100);
|
||||
|
@ -110,10 +187,11 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
key={`piece${index.toString()}`}
|
||||
style={{
|
||||
width: `${size}%`,
|
||||
position: 'absolute',
|
||||
top: `${top}%`,
|
||||
left: `${left}%`,
|
||||
}}>
|
||||
...styles.pieceBackground,
|
||||
}}
|
||||
>
|
||||
<GridComponent
|
||||
width={4}
|
||||
height={4}
|
||||
|
@ -131,98 +209,71 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
|
||||
getPostGameContent(stats: GameStatsType) {
|
||||
const { props } = this;
|
||||
const width = this.isHighScore ? '50%' : '30%';
|
||||
const margin = this.isHighScore ? 'auto' : undefined;
|
||||
const marginLeft = this.isHighScore ? '60%' : '20%';
|
||||
const color = this.isHighScore
|
||||
? props.theme.colors.gameGold
|
||||
: props.theme.colors.primary;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
<Mascot
|
||||
emotion={this.isHighScore ? MASCOT_STYLE.LOVE : MASCOT_STYLE.NORMAL}
|
||||
animated={this.isHighScore}
|
||||
style={{
|
||||
width: this.isHighScore ? '50%' : '30%',
|
||||
marginLeft: this.isHighScore ? 'auto' : undefined,
|
||||
marginRight: this.isHighScore ? 'auto' : undefined,
|
||||
width: width,
|
||||
marginLeft: margin,
|
||||
marginRight: margin,
|
||||
}}
|
||||
/>
|
||||
<SpeechArrow
|
||||
style={{marginLeft: this.isHighScore ? '60%' : '20%'}}
|
||||
style={{ marginLeft: marginLeft }}
|
||||
size={20}
|
||||
color={props.theme.colors.mascotMessageArrow}
|
||||
/>
|
||||
<Card
|
||||
style={{
|
||||
borderColor: props.theme.colors.mascotMessageArrow,
|
||||
borderWidth: 2,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
}}>
|
||||
...styles.recapCard,
|
||||
}}
|
||||
>
|
||||
<Card.Content>
|
||||
<Headline
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: this.isHighScore
|
||||
? props.theme.colors.gameGold
|
||||
: props.theme.colors.primary,
|
||||
}}>
|
||||
color: color,
|
||||
...styles.centertext,
|
||||
}}
|
||||
>
|
||||
{this.isHighScore
|
||||
? i18n.t('screens.game.newHighScore')
|
||||
: i18n.t('screens.game.gameOver')}
|
||||
</Headline>
|
||||
<Divider />
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 20,
|
||||
}}>
|
||||
<View style={styles.recapScoreContainer}>
|
||||
<Text style={styles.recapScore}>
|
||||
{i18n.t('screens.game.score', { score: stats.score })}
|
||||
</Text>
|
||||
<MaterialCommunityIcons
|
||||
name="star"
|
||||
color={props.theme.colors.tetrisScore}
|
||||
size={30}
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
}}
|
||||
style={styles.recapScoreIcon}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<View style={styles.recapContainer}>
|
||||
<Text>{i18n.t('screens.game.level')}</Text>
|
||||
<MaterialCommunityIcons
|
||||
style={{
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
}}
|
||||
style={styles.recapIcon}
|
||||
name="gamepad-square"
|
||||
size={20}
|
||||
color={props.theme.colors.textDisabled}
|
||||
/>
|
||||
<Text>{stats.level}</Text>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<View style={styles.recapContainer}>
|
||||
<Text>{i18n.t('screens.game.time')}</Text>
|
||||
<MaterialCommunityIcons
|
||||
style={{
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
}}
|
||||
style={styles.recapIcon}
|
||||
name="timer"
|
||||
size={20}
|
||||
color={props.theme.colors.textDisabled}
|
||||
|
@ -239,40 +290,29 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
const { props } = this;
|
||||
return (
|
||||
<View>
|
||||
<Mascot
|
||||
emotion={MASCOT_STYLE.COOL}
|
||||
style={{
|
||||
width: '40%',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
<Mascot emotion={MASCOT_STYLE.COOL} style={styles.welcomeMascot} />
|
||||
<SpeechArrow
|
||||
style={{marginLeft: '60%'}}
|
||||
style={styles.speechArrow}
|
||||
size={20}
|
||||
color={props.theme.colors.mascotMessageArrow}
|
||||
/>
|
||||
<Card
|
||||
style={{
|
||||
borderColor: props.theme.colors.mascotMessageArrow,
|
||||
borderWidth: 2,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
}}>
|
||||
...styles.welcomeCard,
|
||||
}}
|
||||
>
|
||||
<Card.Content>
|
||||
<Headline
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: props.theme.colors.primary,
|
||||
}}>
|
||||
...styles.centertext,
|
||||
}}
|
||||
>
|
||||
{i18n.t('screens.game.welcomeTitle')}
|
||||
</Headline>
|
||||
<Divider />
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
marginTop: 10,
|
||||
}}>
|
||||
<Paragraph style={styles.welcomeText}>
|
||||
{i18n.t('screens.game.welcomeMessage')}
|
||||
</Paragraph>
|
||||
</Card.Content>
|
||||
|
@ -298,15 +338,17 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
fontSize = 15;
|
||||
size = 50;
|
||||
}
|
||||
const marginLeft = place === 2 ? 20 : 'auto';
|
||||
const marginRight = place === 3 ? 20 : 'auto';
|
||||
const fontWeight = place === 1 ? 'bold' : undefined;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginLeft: place === 2 ? 20 : 'auto',
|
||||
marginRight: place === 3 ? 20 : 'auto',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
}}>
|
||||
marginLeft: marginLeft,
|
||||
marginRight: marginRight,
|
||||
...styles.podiumContainer,
|
||||
}}
|
||||
>
|
||||
{this.isHighScore && place === 1 ? (
|
||||
<Animatable.View
|
||||
animation="swing"
|
||||
|
@ -314,14 +356,13 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
duration={2000}
|
||||
delay={1000}
|
||||
useNativeDriver
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: -20,
|
||||
}}>
|
||||
style={styles.podiumIconContainer}
|
||||
>
|
||||
<Animatable.View
|
||||
animation="pulse"
|
||||
iterationCount="infinite"
|
||||
useNativeDriver>
|
||||
useNativeDriver
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name="decagram"
|
||||
color={props.theme.colors.gameGold}
|
||||
|
@ -337,10 +378,11 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
/>
|
||||
<Text
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
fontWeight: place === 1 ? 'bold' : undefined,
|
||||
fontWeight: fontWeight,
|
||||
fontSize,
|
||||
}}>
|
||||
...styles.centertext,
|
||||
}}
|
||||
>
|
||||
{score}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -352,18 +394,9 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
const silver = this.scores.length > 1 ? this.scores[1] : '-';
|
||||
const bronze = this.scores.length > 2 ? this.scores[2] : '-';
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginBottom: 20,
|
||||
marginTop: 20,
|
||||
}}>
|
||||
<View style={styles.topScoreContainer}>
|
||||
{this.getPodiumRender(1, gold.toString())}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<View style={styles.topScoreSubcontainer}>
|
||||
{this.getPodiumRender(3, bronze.toString())}
|
||||
{this.getPodiumRender(2, silver.toString())}
|
||||
</View>
|
||||
|
@ -374,7 +407,7 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
getMainContent() {
|
||||
const { props } = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
{this.gameStats != null
|
||||
? this.getPostGameContent(this.gameStats)
|
||||
: this.getWelcomeText()}
|
||||
|
@ -386,11 +419,8 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
highScore: this.scores.length > 0 ? this.scores[0] : null,
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginTop: 10,
|
||||
}}>
|
||||
style={styles.playButton}
|
||||
>
|
||||
{i18n.t('screens.game.play')}
|
||||
</Button>
|
||||
{this.getTopScoresRender()}
|
||||
|
@ -420,7 +450,7 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
}
|
||||
AsyncStorageManager.set(
|
||||
AsyncStorageManager.PREFERENCES.gameScores.key,
|
||||
this.scores,
|
||||
this.scores
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -428,16 +458,17 @@ class GameStartScreen extends React.Component<PropsType> {
|
|||
render() {
|
||||
const { props } = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View style={GENERAL_STYLES.flex}>
|
||||
{this.getPiecesBackground()}
|
||||
<LinearGradient
|
||||
style={{flex: 1}}
|
||||
style={GENERAL_STYLES.flex}
|
||||
colors={[
|
||||
`${props.theme.colors.background}00`,
|
||||
props.theme.colors.background,
|
||||
]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{x: 0, y: 1}}>
|
||||
end={{ x: 0, y: 1 }}
|
||||
>
|
||||
<CollapsibleScrollView>
|
||||
{this.getMainContent()}
|
||||
<MascotPopup
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Linking, Image} from 'react-native';
|
||||
import { Linking, Image, StyleSheet } from 'react-native';
|
||||
import { Card, Text } from 'react-native-paper';
|
||||
import Autolink from 'react-native-autolink';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
|
@ -39,6 +39,22 @@ type PropsType = {
|
|||
route: { params: { data: FeedItemType; date: string } };
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
margin: 5,
|
||||
},
|
||||
image: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
},
|
||||
button: {
|
||||
width: 250,
|
||||
height: 250,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Class defining a feed item page.
|
||||
*/
|
||||
|
@ -89,32 +105,21 @@ class FeedItemScreen extends React.Component<PropsType> {
|
|||
const pageSource: NewsSourceType =
|
||||
NewsSourcesConstants[this.displayData.page_id as AvailablePages];
|
||||
return (
|
||||
<CollapsibleScrollView style={{margin: 5}} hasTab>
|
||||
<CollapsibleScrollView style={styles.container} hasTab>
|
||||
<Card.Title
|
||||
title={pageSource.name}
|
||||
subtitle={this.date}
|
||||
left={() => (
|
||||
<Image
|
||||
source={pageSource.icon}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
left={() => <Image source={pageSource.icon} style={styles.image} />}
|
||||
/>
|
||||
{this.displayData.image ? (
|
||||
<ImageGalleryButton
|
||||
images={[{ url: this.displayData.image }]}
|
||||
style={{
|
||||
width: 250,
|
||||
height: 250,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
style={styles.button}
|
||||
/>
|
||||
) : null}
|
||||
<Card.Content style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
<Card.Content
|
||||
style={{ paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20 }}
|
||||
>
|
||||
{this.displayData.message !== undefined ? (
|
||||
<Autolink
|
||||
text={this.displayData.message}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue