Improve remaining files to match linter

This commit is contained in:
Arnaud Vergnet 2020-08-05 20:58:28 +02:00
parent cbe3777957
commit 1e81b2cd7b
39 changed files with 362 additions and 323 deletions

4
App.js
View file

@ -10,7 +10,7 @@ import {OverflowMenuProvider} from 'react-navigation-header-buttons';
import LocaleManager from './src/managers/LocaleManager';
import AsyncStorageManager from './src/managers/AsyncStorageManager';
import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider';
import type {CustomTheme} from './src/managers/ThemeManager';
import type {CustomThemeType} from './src/managers/ThemeManager';
import ThemeManager from './src/managers/ThemeManager';
import MainNavigator from './src/navigation/MainNavigator';
import AprilFoolsManager from './src/managers/AprilFoolsManager';
@ -35,7 +35,7 @@ type StateType = {
showIntro: boolean,
showUpdate: boolean,
showAprilFools: boolean,
currentTheme: CustomTheme | null,
currentTheme: CustomThemeType | null,
};
export default class App extends React.Component<null, StateType> {

View file

@ -6,4 +6,5 @@ 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);

View file

@ -6,12 +6,13 @@
*/
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
transformer: {
// eslint-disable-next-line flowtype/require-return-type
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};

View file

@ -4,10 +4,10 @@ import * as React from 'react';
import {View} from 'react-native';
import {Headline, withTheme} from 'react-native-paper';
import i18n from 'i18n-js';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
};
class VoteNotAvailable extends React.Component<PropsType> {

View file

@ -12,12 +12,12 @@ import {
import {FlatList, StyleSheet} from 'react-native';
import i18n from 'i18n-js';
import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
teams: Array<VoteTeamType>,
dateEnd: string,
theme: CustomTheme,
theme: CustomThemeType,
};
const styles = StyleSheet.create({

View file

@ -9,14 +9,14 @@ import {
} from 'react-native-paper';
import {StyleSheet} from 'react-native';
import i18n from 'i18n-js';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
startDate: string | null,
justVoted: boolean,
hasVoted: boolean,
isVoteRunning: boolean,
theme: CustomTheme,
theme: CustomThemeType,
};
const styles = StyleSheet.create({

View file

@ -5,10 +5,10 @@ import {View} from 'react-native';
import {List, withTheme} from 'react-native-paper';
import Collapsible from 'react-native-collapsible';
import * as Animatable from 'react-native-animatable';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
title: string,
subtitle?: string,
left?: () => React.Node,

View file

@ -7,13 +7,14 @@ import * as Animatable from 'react-native-animatable';
import {StackNavigationProp} from '@react-navigation/stack';
import AutoHideHandler from '../../utils/AutoHideHandler';
import CustomTabBar from '../Tabbar/CustomTabBar';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
import type {OnScrollType} from '../../utils/AutoHideHandler';
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
type PropsType = {
navigation: StackNavigationProp,
theme: CustomTheme,
theme: CustomThemeType,
onPress: (action: string, data?: string) => void,
seekAttention: boolean,
};
@ -94,7 +95,7 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
}
};
onScroll = (event: SyntheticEvent<EventTarget>) => {
onScroll = (event: OnScrollType) => {
this.hideHandler.onScroll(event);
};

View file

@ -2,7 +2,7 @@
import * as React from 'react';
import {Collapsible} from 'react-navigation-collapsible';
import {withCollapsible} from '../../utils/withCollapsible';
import withCollapsible from '../../utils/withCollapsible';
import CustomTabBar from '../Tabbar/CustomTabBar';
export type CollapsibleComponentPropsType = {

View file

@ -5,11 +5,11 @@ import {List, withTheme} from 'react-native-paper';
import {View} from 'react-native';
import i18n from 'i18n-js';
import {StackNavigationProp} from '@react-navigation/stack';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
navigation: StackNavigationProp,
theme: CustomTheme,
theme: CustomThemeType,
};
class ActionsDashBoardItem extends React.Component<PropsType> {

View file

@ -10,12 +10,12 @@ import {
} from 'react-native-paper';
import {StyleSheet, View} from 'react-native';
import i18n from 'i18n-js';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
eventNumber: number,
clickAction: () => void,
theme: CustomTheme,
theme: CustomThemeType,
children?: React.Node,
};

View file

@ -4,13 +4,13 @@ import * as React from 'react';
import {Badge, TouchableRipple, withTheme} from 'react-native-paper';
import {Dimensions, Image, View} from 'react-native';
import * as Animatable from 'react-native-animatable';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
image: string | null,
onPress: () => void | null,
badgeCount: number | null,
theme: CustomTheme,
theme: CustomThemeType,
};
const AnimatableBadge = Animatable.createAnimatableComponent(Badge);

View file

@ -7,14 +7,14 @@ import type {
ClubCategoryType,
ClubType,
} from '../../../screens/Amicale/Clubs/ClubListScreen';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
onPress: () => void,
categoryTranslator: (id: number) => ClubCategoryType,
item: ClubType,
height: number,
theme: CustomTheme,
theme: CustomThemeType,
};
class ClubListItem extends React.Component<PropsType> {

View file

@ -10,13 +10,13 @@ import type {
ServiceCategoryType,
ServiceItemType,
} from '../../../managers/ServicesManager';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
item: ServiceCategoryType,
activeDashboard: Array<string>,
onPress: (service: ServiceItemType) => void,
theme: CustomTheme,
theme: CustomThemeType,
};
const LIST_ITEM_HEIGHT = 64;

View file

@ -3,7 +3,7 @@
import * as React from 'react';
import {Image} from 'react-native';
import {List, withTheme} from 'react-native-paper';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {ServiceItemType} from '../../../managers/ServicesManager';
type PropsType = {
@ -11,7 +11,7 @@ type PropsType = {
isActive: boolean,
height: number,
onPress: () => void,
theme: CustomTheme,
theme: CustomThemeType,
};
class DashboardEditItem extends React.Component<PropsType> {

View file

@ -3,13 +3,13 @@
import * as React from 'react';
import {TouchableRipple, withTheme} from 'react-native-paper';
import {Dimensions, Image, View} from 'react-native';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
image: string,
isActive: boolean,
onPress: () => void,
theme: CustomTheme,
theme: CustomThemeType,
};
/**

View file

@ -4,7 +4,7 @@ import * as React from 'react';
import {Avatar, List, withTheme} from 'react-native-paper';
import i18n from 'i18n-js';
import {StackNavigationProp} from '@react-navigation/stack';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
import {
getFirstEquipmentAvailability,
@ -17,7 +17,7 @@ type PropsType = {
userDeviceRentDates: [string, string],
item: DeviceType,
height: number,
theme: CustomTheme,
theme: CustomThemeType,
};
class EquipmentListItem extends React.Component<PropsType> {

View file

@ -10,7 +10,7 @@ import type {
PlanexGroupType,
PlanexGroupCategoryType,
} from '../../../screens/Planex/GroupSelectionScreen';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = {
item: PlanexGroupCategoryType,
@ -19,7 +19,7 @@ type PropsType = {
currentSearchString: string,
favoriteNumber: number,
height: number,
theme: CustomTheme,
theme: CustomThemeType,
};
const LIST_ITEM_HEIGHT = 64;

View file

@ -2,11 +2,11 @@
import * as React from 'react';
import {IconButton, List, withTheme} from 'react-native-paper';
import type {CustomTheme} from '../../../managers/ThemeManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
onPress: () => void,
onStarPress: () => void,
item: PlanexGroupType,

View file

@ -18,12 +18,12 @@ import {
View,
} from 'react-native';
import Mascot from './Mascot';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
import SpeechArrow from './SpeechArrow';
import AsyncStorageManager from '../../managers/AsyncStorageManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
icon: string,
title: string,
message: string,

View file

@ -4,10 +4,10 @@ import * as React from 'react';
import {View} from 'react-native';
import {withTheme} from 'react-native-paper';
import {Agenda} from 'react-native-calendars';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
onRef: (ref: Agenda) => void,
};

View file

@ -5,10 +5,10 @@ import * as React from 'react';
import {Text, withTheme} from 'react-native-paper';
import HTML from 'react-native-render-html';
import {Linking} from 'react-native';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
html: string,
};

View file

@ -4,10 +4,10 @@ import * as React from 'react';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {HeaderButton, HeaderButtons} from 'react-navigation-header-buttons';
import {withTheme} from 'react-native-paper';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
const MaterialHeaderButton = (props: {
theme: CustomTheme,
theme: CustomThemeType,
color: string,
}): React.Node => {
const {color, theme} = props;

View file

@ -5,7 +5,7 @@ import {withTheme} from 'react-native-paper';
import {Modalize} from 'react-native-modalize';
import {View} from 'react-native-animatable';
import CustomTabBar from '../Tabbar/CustomTabBar';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
/**
* Abstraction layer for Modalize component, using custom configuration
@ -14,7 +14,7 @@ import type {CustomTheme} from '../../managers/ThemeManager';
* @return {*}
*/
function CustomModal(props: {
theme: CustomTheme,
theme: CustomThemeType,
onRef: (re: Modalize) => void,
children?: React.Node,
}): React.Node {

View file

@ -4,10 +4,10 @@ import * as React from 'react';
import {Text, withTheme} from 'react-native-paper';
import {View} from 'react-native-animatable';
import Slider, {SliderProps} from '@react-native-community/slider';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
theme: CustomTheme,
theme: CustomThemeType,
valueSuffix?: string,
...SliderProps,
};

View file

@ -3,7 +3,7 @@
import * as React from 'react';
import {View} from 'react-native';
import {ActivityIndicator, withTheme} from 'react-native-paper';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
/**
* Component used to display a header button
@ -12,7 +12,7 @@ import type {CustomTheme} from '../../managers/ThemeManager';
* @return {*}
*/
function BasicLoadingScreen(props: {
theme: CustomTheme,
theme: CustomThemeType,
isAbsolute: boolean,
}): React.Node {
const {theme, isAbsolute} = props;

View file

@ -8,11 +8,11 @@ import i18n from 'i18n-js';
import * as Animatable from 'react-native-animatable';
import {StackNavigationProp} from '@react-navigation/stack';
import {ERROR_TYPE} from '../../utils/WebData';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
navigation: StackNavigationProp,
theme: CustomTheme,
theme: CustomThemeType,
route: {name: string},
onRefresh?: () => void,
errorCode?: number,
@ -47,7 +47,7 @@ const styles = StyleSheet.create({
class ErrorView extends React.PureComponent<PropsType> {
static defaultProps = {
onRefresh: (): void => null,
onRefresh: () => {},
errorCode: 0,
icon: '',
message: '',
@ -141,10 +141,12 @@ class ErrorView extends React.PureComponent<PropsType> {
this.icon = 'alert-circle-outline';
break;
}
this.message += `\n\nCode ${props.errorCode}`;
this.message += `\n\nCode ${
props.errorCode != null ? props.errorCode : -1
}`;
} else {
this.message = props.message;
this.icon = props.icon;
this.message = props.message != null ? props.message : '';
this.icon = props.icon != null ? props.icon : '';
}
}
@ -168,6 +170,7 @@ class ErrorView extends React.PureComponent<PropsType> {
<View style={styles.inner}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons
// $FlowFixMe
name={this.icon}
size={150}
color={props.theme.colors.textDisabled}

View file

@ -9,7 +9,7 @@ import {Collapsible} from 'react-navigation-collapsible';
import {StackNavigationProp} from '@react-navigation/stack';
import ErrorView from './ErrorView';
import BasicLoadingScreen from './BasicLoadingScreen';
import {withCollapsible} from '../../utils/withCollapsible';
import withCollapsible from '../../utils/withCollapsible';
import CustomTabBar from '../Tabbar/CustomTabBar';
import {ERROR_TYPE, readData} from '../../utils/WebData';
import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';

View file

@ -14,7 +14,7 @@ import {withTheme} from 'react-native-paper';
import {StackNavigationProp} from '@react-navigation/stack';
import {Collapsible} from 'react-navigation-collapsible';
import type {CustomThemeType} from '../../managers/ThemeManager';
import {withCollapsible} from '../../utils/withCollapsible';
import withCollapsible from '../../utils/withCollapsible';
import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
import {ERROR_TYPE} from '../../utils/WebData';
import ErrorView from './ErrorView';

View file

@ -7,7 +7,7 @@ import {Collapsible} from 'react-navigation-collapsible';
import {StackNavigationProp} from '@react-navigation/stack';
import TabIcon from './TabIcon';
import TabHomeIcon from './TabHomeIcon';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type RouteType = {
name: string,
@ -33,7 +33,7 @@ type PropsType = {
},
},
navigation: StackNavigationProp,
theme: CustomTheme,
theme: CustomThemeType,
};
type StateType = {

View file

@ -6,13 +6,13 @@ import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
import * as Animatable from 'react-native-animatable';
import FOCUSED_ICON from '../../../assets/tab-icon.png';
import UNFOCUSED_ICON from '../../../assets/tab-icon-outline.png';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
focused: boolean,
onPress: () => void,
onLongPress: () => void,
theme: CustomTheme,
theme: CustomThemeType,
tabBarHeight: number,
};

View file

@ -6,7 +6,7 @@ import {TouchableRipple, withTheme} from 'react-native-paper';
import type {MaterialCommunityIconsGlyphs} from 'react-native-vector-icons/MaterialCommunityIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import * as Animatable from 'react-native-animatable';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
type PropsType = {
focused: boolean,
@ -15,7 +15,7 @@ type PropsType = {
icon: MaterialCommunityIconsGlyphs,
onPress: () => void,
onLongPress: () => void,
theme: CustomTheme,
theme: CustomThemeType,
extraData: null | boolean | number | string,
};

View file

@ -145,9 +145,7 @@ class AboutScreen extends React.Component<PropsType> {
*/
additionalDevData = [
{
onPressCallback: () => {
console.log('Meme this');
},
onPressCallback: () => {},
icon: 'account',
text: 'Yohan SIMARD',
showChevron: false,

View file

@ -1,14 +1,16 @@
import ShapeL from "../Shapes/ShapeL";
import ShapeI from "../Shapes/ShapeI";
import ShapeJ from "../Shapes/ShapeJ";
import ShapeO from "../Shapes/ShapeO";
import ShapeS from "../Shapes/ShapeS";
import ShapeT from "../Shapes/ShapeT";
import ShapeZ from "../Shapes/ShapeZ";
import type {Coordinates} from '../Shapes/BaseShape';
import BaseShape from "../Shapes/BaseShape";
import type {Grid} from "../components/GridComponent";
import type {CustomTheme} from "../../../managers/ThemeManager";
// @flow
import ShapeL from '../Shapes/ShapeL';
import ShapeI from '../Shapes/ShapeI';
import ShapeJ from '../Shapes/ShapeJ';
import ShapeO from '../Shapes/ShapeO';
import ShapeS from '../Shapes/ShapeS';
import ShapeT from '../Shapes/ShapeT';
import ShapeZ from '../Shapes/ShapeZ';
import type {CoordinatesType} from '../Shapes/BaseShape';
import BaseShape from '../Shapes/BaseShape';
import type {GridType} from '../components/GridComponent';
import type {CustomThemeType} from '../../../managers/ThemeManager';
/**
* Class used as an abstraction layer for shapes.
@ -16,157 +18,167 @@ import type {CustomTheme} from "../../../managers/ThemeManager";
*
*/
export default class Piece {
shapes = [ShapeL, ShapeI, ShapeJ, ShapeO, ShapeS, ShapeT, ShapeZ];
#shapes = [
ShapeL,
ShapeI,
ShapeJ,
ShapeO,
ShapeS,
ShapeT,
ShapeZ,
];
#currentShape: BaseShape;
#theme: CustomTheme;
currentShape: BaseShape;
/**
* Initializes this piece's color and shape
*
* @param theme Object containing current theme
*/
constructor(theme: CustomTheme) {
this.#currentShape = this.getRandomShape(theme);
this.#theme = theme;
theme: CustomThemeType;
/**
* Initializes this piece's color and shape
*
* @param theme Object containing current theme
*/
constructor(theme: CustomThemeType) {
this.currentShape = this.getRandomShape(theme);
this.theme = theme;
}
/**
* Gets a random shape object
*
* @param theme Object containing current theme
*/
getRandomShape(theme: CustomThemeType): BaseShape {
return new this.shapes[Math.floor(Math.random() * 7)](theme);
}
/**
* Removes the piece from the given grid
*
* @param grid The grid to remove the piece from
*/
removeFromGrid(grid: GridType) {
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
true,
);
pos.forEach((coordinates: CoordinatesType) => {
// eslint-disable-next-line no-param-reassign
grid[coordinates.y][coordinates.x] = {
color: this.theme.colors.tetrisBackground,
isEmpty: true,
key: grid[coordinates.y][coordinates.x].key,
};
});
}
/**
* Adds this piece to the given grid
*
* @param grid The grid to add the piece to
* @param isPreview Should we use this piece's current position to determine the cells?
*/
toGrid(grid: GridType, isPreview: boolean) {
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
!isPreview,
);
pos.forEach((coordinates: CoordinatesType) => {
// eslint-disable-next-line no-param-reassign
grid[coordinates.y][coordinates.x] = {
color: this.currentShape.getColor(),
isEmpty: false,
key: grid[coordinates.y][coordinates.x].key,
};
});
}
/**
* Checks if the piece's current position is valid
*
* @param grid The current game grid
* @param width The grid's width
* @param height The grid's height
* @return {boolean} If the position is valid
*/
isPositionValid(grid: GridType, width: number, height: number): boolean {
let isValid = true;
const pos: Array<CoordinatesType> = this.currentShape.getCellsCoordinates(
true,
);
for (let i = 0; i < pos.length; i += 1) {
if (
pos[i].x >= width ||
pos[i].x < 0 ||
pos[i].y >= height ||
pos[i].y < 0 ||
!grid[pos[i].y][pos[i].x].isEmpty
) {
isValid = false;
break;
}
}
return isValid;
}
/**
* Gets a random shape object
*
* @param theme Object containing current theme
*/
getRandomShape(theme: CustomTheme) {
return new this.#shapes[Math.floor(Math.random() * 7)](theme);
/**
* Tries to move the piece by the given offset on the given grid
*
* @param x Position X offset
* @param y Position Y offset
* @param grid The grid to move the piece on
* @param width The grid's width
* @param height The grid's height
* @param freezeCallback Callback to use if the piece should freeze itself
* @return {boolean} True if the move was valid, false otherwise
*/
tryMove(
x: number,
y: number,
grid: GridType,
width: number,
height: number,
freezeCallback: () => void,
): boolean {
let newX = x;
let newY = y;
if (x > 1) newX = 1; // Prevent moving from more than one tile
if (x < -1) newX = -1;
if (y > 1) newY = 1;
if (y < -1) newY = -1;
if (x !== 0 && y !== 0) newY = 0; // Prevent diagonal movement
this.removeFromGrid(grid);
this.currentShape.move(newX, newY);
const isValid = this.isPositionValid(grid, width, height);
if (!isValid) this.currentShape.move(-newX, -newY);
const shouldFreeze = !isValid && newY !== 0;
this.toGrid(grid, false);
if (shouldFreeze) freezeCallback();
return isValid;
}
/**
* Tries to rotate the piece
*
* @param grid The grid to rotate the piece on
* @param width The grid's width
* @param height The grid's height
* @return {boolean} True if the rotation was valid, false otherwise
*/
tryRotate(grid: GridType, width: number, height: number): boolean {
this.removeFromGrid(grid);
this.currentShape.rotate(true);
if (!this.isPositionValid(grid, width, height)) {
this.currentShape.rotate(false);
this.toGrid(grid, false);
return false;
}
this.toGrid(grid, false);
return true;
}
/**
* Removes the piece from the given grid
*
* @param grid The grid to remove the piece from
*/
removeFromGrid(grid: Grid) {
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
for (let i = 0; i < pos.length; i++) {
grid[pos[i].y][pos[i].x] = {
color: this.#theme.colors.tetrisBackground,
isEmpty: true,
key: grid[pos[i].y][pos[i].x].key
};
}
}
/**
* Gets this piece used cells coordinates
*
* @return {Array<CoordinatesType>} An array of coordinates
*/
getCoordinates(): Array<CoordinatesType> {
return this.currentShape.getCellsCoordinates(true);
}
/**
* Adds this piece to the given grid
*
* @param grid The grid to add the piece to
* @param isPreview Should we use this piece's current position to determine the cells?
*/
toGrid(grid: Grid, isPreview: boolean) {
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(!isPreview);
for (let i = 0; i < pos.length; i++) {
grid[pos[i].y][pos[i].x] = {
color: this.#currentShape.getColor(),
isEmpty: false,
key: grid[pos[i].y][pos[i].x].key
};
}
}
/**
* Checks if the piece's current position is valid
*
* @param grid The current game grid
* @param width The grid's width
* @param height The grid's height
* @return {boolean} If the position is valid
*/
isPositionValid(grid: Grid, width: number, height: number) {
let isValid = true;
const pos: Array<Coordinates> = this.#currentShape.getCellsCoordinates(true);
for (let i = 0; i < pos.length; i++) {
if (pos[i].x >= width
|| pos[i].x < 0
|| pos[i].y >= height
|| pos[i].y < 0
|| !grid[pos[i].y][pos[i].x].isEmpty) {
isValid = false;
break;
}
}
return isValid;
}
/**
* Tries to move the piece by the given offset on the given grid
*
* @param x Position X offset
* @param y Position Y offset
* @param grid The grid to move the piece on
* @param width The grid's width
* @param height The grid's height
* @param freezeCallback Callback to use if the piece should freeze itself
* @return {boolean} True if the move was valid, false otherwise
*/
tryMove(x: number, y: number, grid: Grid, width: number, height: number, freezeCallback: () => void) {
if (x > 1) x = 1; // Prevent moving from more than one tile
if (x < -1) x = -1;
if (y > 1) y = 1;
if (y < -1) y = -1;
if (x !== 0 && y !== 0) y = 0; // Prevent diagonal movement
this.removeFromGrid(grid);
this.#currentShape.move(x, y);
let isValid = this.isPositionValid(grid, width, height);
if (!isValid)
this.#currentShape.move(-x, -y);
let shouldFreeze = !isValid && y !== 0;
this.toGrid(grid, false);
if (shouldFreeze)
freezeCallback();
return isValid;
}
/**
* Tries to rotate the piece
*
* @param grid The grid to rotate the piece on
* @param width The grid's width
* @param height The grid's height
* @return {boolean} True if the rotation was valid, false otherwise
*/
tryRotate(grid: Grid, width: number, height: number) {
this.removeFromGrid(grid);
this.#currentShape.rotate(true);
if (!this.isPositionValid(grid, width, height)) {
this.#currentShape.rotate(false);
this.toGrid(grid, false);
return false;
}
this.toGrid(grid, false);
return true;
}
/**
* Gets this piece used cells coordinates
*
* @return {Array<Coordinates>} An array of coordinates
*/
getCoordinates(): Array<Coordinates> {
return this.#currentShape.getCellsCoordinates(true);
}
getCurrentShape() {
return this.#currentShape;
}
getCurrentShape(): BaseShape {
return this.currentShape;
}
}

View file

@ -19,7 +19,7 @@ import MaterialHeaderButtons, {
Item,
} from '../../components/Overrides/CustomHeaderButton';
import AnimatedFAB from '../../components/Animations/AnimatedFAB';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
import ConnectionManager from '../../managers/ConnectionManager';
import LogoutDialog from '../../components/Amicale/LogoutDialog';
import AsyncStorageManager from '../../managers/AsyncStorageManager';
@ -78,7 +78,7 @@ type RawDashboardType = {
type PropsType = {
navigation: StackNavigationProp,
route: {params: {nextScreen: string, data: {...}}},
theme: CustomTheme,
theme: CustomThemeType,
};
type StateType = {

View file

@ -6,7 +6,7 @@ import i18n from 'i18n-js';
import {View} from 'react-native';
import {CommonActions} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import type {CustomTheme} from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
import ThemeManager from '../../managers/ThemeManager';
import WebViewScreen from '../../components/Screens/WebViewScreen';
import AsyncStorageManager from '../../managers/AsyncStorageManager';
@ -22,7 +22,7 @@ import MascotPopup from '../../components/Mascot/MascotPopup';
type PropsType = {
navigation: StackNavigationProp,
route: {params: {group: PlanexGroupType}},
theme: CustomTheme,
theme: CustomThemeType,
};
type StateType = {

View file

@ -6,7 +6,7 @@ import {CommonActions} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import CardList from '../../components/Lists/CardList/CardList';
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
import {withCollapsible} from '../../utils/withCollapsible';
import withCollapsible from '../../utils/withCollapsible';
import type {ServiceCategoryType} from '../../managers/ServicesManager';
type PropsType = {

View file

@ -1,70 +1,85 @@
// @flow
import * as React from 'react';
const speedOffset = 5;
type ListenerFunctionType = (shouldHide: boolean) => void;
export type OnScrollType = {
nativeEvent: {
contentInset: {bottom: number, left: number, right: number, top: number},
contentOffset: {x: number, y: number},
contentSize: {height: number, width: number},
layoutMeasurement: {height: number, width: number},
zoomScale: number,
},
};
/**
* Class used to detect when to show or hide a component based on scrolling
*/
export default class AutoHideHandler {
lastOffset: number;
lastOffset: number;
isHidden: boolean;
isHidden: boolean;
listeners: Array<Function>;
listeners: Array<ListenerFunctionType>;
constructor(startHidden: boolean) {
this.listeners = [];
this.isHidden = startHidden;
constructor(startHidden: boolean) {
this.listeners = [];
this.isHidden = startHidden;
}
/**
* Adds a listener to the hide event
*
* @param listener
*/
addListener(listener: (shouldHide: boolean) => void) {
this.listeners.push(listener);
}
/**
* Notifies every listener whether they should hide or show.
*
* @param shouldHide
*/
notifyListeners(shouldHide: boolean) {
this.listeners.forEach((func: ListenerFunctionType) => {
func(shouldHide);
});
}
/**
* Callback to be used on the onScroll animated component event.
*
* Detects if the current speed exceeds a threshold and notifies listeners to hide or show.
*
* The hide even is triggered when the user scrolls down, and the show event on scroll up.
* This does not take into account the speed when the y coordinate is negative, to prevent hiding on over scroll.
* (When scrolling up and hitting the top on ios for example)
*
* //TODO Known issue:
* When refreshing a list with the pull down gesture on ios,
* this can trigger the hide event as it scrolls down the list to show the refresh indicator.
* Android shows the refresh indicator on top of the list so this is not an issue.
*
* @param event The scroll event generated by the animated component onScroll prop
*/
onScroll(event: OnScrollType) {
const {nativeEvent} = event;
const speed =
nativeEvent.contentOffset.y < 0
? 0
: this.lastOffset - nativeEvent.contentOffset.y;
if (speed < -speedOffset && !this.isHidden) {
// Go down
this.notifyListeners(true);
this.isHidden = true;
} else if (speed > speedOffset && this.isHidden) {
// Go up
this.notifyListeners(false);
this.isHidden = false;
}
/**
* Adds a listener to the hide event
*
* @param listener
*/
addListener(listener: Function) {
this.listeners.push(listener);
}
/**
* Notifies every listener whether they should hide or show.
*
* @param shouldHide
*/
notifyListeners(shouldHide: boolean) {
for (let i = 0; i < this.listeners.length; i++) {
this.listeners[i](shouldHide);
}
}
/**
* Callback to be used on the onScroll animated component event.
*
* Detects if the current speed exceeds a threshold and notifies listeners to hide or show.
*
* The hide even is triggered when the user scrolls down, and the show event on scroll up.
* This does not take into account the speed when the y coordinate is negative, to prevent hiding on over scroll.
* (When scrolling up and hitting the top on ios for example)
*
* //TODO Known issue:
* When refreshing a list with the pull down gesture on ios,
* this can trigger the hide event as it scrolls down the list to show the refresh indicator.
* Android shows the refresh indicator on top of the list so this is not an issue.
*
* @param nativeEvent The scroll event generated by the animated component onScroll prop
*/
onScroll({nativeEvent}: Object) {
const speed = nativeEvent.contentOffset.y < 0 ? 0 : this.lastOffset - nativeEvent.contentOffset.y;
if (speed < -speedOffset && !this.isHidden) { // Go down
this.notifyListeners(true);
this.isHidden = true;
} else if (speed > speedOffset && this.isHidden) { // Go up
this.notifyListeners(false);
this.isHidden = false;
}
this.lastOffset = nativeEvent.contentOffset.y;
}
this.lastOffset = nativeEvent.contentOffset.y;
}
}

View file

@ -1,9 +1,9 @@
// @flow
import * as React from 'react';
import {useTheme} from "react-native-paper";
import {createCollapsibleStack} from "react-navigation-collapsible";
import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
import {useTheme} from 'react-native-paper';
import {createCollapsibleStack} from 'react-navigation-collapsible';
import StackNavigator, {StackNavigationOptions} from '@react-navigation/stack';
/**
* Creates a navigation stack with the collapsible library, allowing the header to collapse on scroll.
@ -22,32 +22,34 @@ import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
* @returns {JSX.Element}
*/
export function createScreenCollapsibleStack(
name: string,
Stack: StackNavigator,
component: React.ComponentType<any>,
title: string,
useNativeDriver?: boolean,
options?: StackNavigationOptions,
headerColor?: string) {
const {colors} = useTheme();
const screenOptions = options != null ? options : {};
return createCollapsibleStack(
<Stack.Screen
name={name}
component={component}
options={{
title: title,
headerStyle: {
backgroundColor: headerColor!=null ? headerColor :colors.surface,
},
...screenOptions,
}}
/>,
{
collapsedColor: headerColor!=null ? headerColor :colors.surface,
useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
}
)
name: string,
Stack: StackNavigator,
// eslint-disable-next-line flowtype/no-weak-types
component: React.ComponentType<any>,
title: string,
useNativeDriver?: boolean,
options?: StackNavigationOptions,
headerColor?: string,
): React.Node {
const {colors} = useTheme();
const screenOptions = options != null ? options : {};
return createCollapsibleStack(
<Stack.Screen
name={name}
component={component}
options={{
title,
headerStyle: {
backgroundColor: headerColor != null ? headerColor : colors.surface,
},
...screenOptions,
}}
/>,
{
collapsedColor: headerColor != null ? headerColor : colors.surface,
useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
},
);
}
/**
@ -62,6 +64,12 @@ export function createScreenCollapsibleStack(
* @param title
* @returns {JSX.Element}
*/
export function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
return createScreenCollapsibleStack(name, Stack, component, title, false);
export function getWebsiteStack(
name: string,
Stack: StackNavigator,
// eslint-disable-next-line flowtype/no-weak-types
component: React.ComponentType<any>,
title: string,
): React.Node {
return createScreenCollapsibleStack(name, Stack, component, title, false);
}