Improved game flow typing

This commit is contained in:
Arnaud Vergnet 2020-07-18 19:25:51 +02:00
parent 494b319f19
commit fe26ec0cc4
14 changed files with 164 additions and 197 deletions

View file

@ -5,17 +5,21 @@ import {Alert, View} from 'react-native';
import {IconButton, Text, withTheme} from 'react-native-paper';
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import GameLogic from "./GameLogic";
import Grid from "./components/Grid";
import type {Grid} from "./components/GridComponent";
import GridComponent from "./components/GridComponent";
import Preview from "./components/Preview";
import i18n from "i18n-js";
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../managers/ThemeManager";
type Props = {
navigation: Object,
navigation: StackNavigationProp,
theme: CustomTheme,
}
type State = {
grid: Array<Array<Object>>,
grid: Grid,
gameRunning: boolean,
gameTime: number,
gameScore: number,
@ -24,19 +28,11 @@ type State = {
class GameScreen extends React.Component<Props, State> {
colors: Object;
logic: GameLogic;
onTick: Function;
onClock: Function;
onGameEnd: Function;
updateGrid: Function;
updateGridScore: Function;
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.logic = new GameLogic(20, 10, this.colors);
this.logic = new GameLogic(20, 10, this.props.theme.colors);
this.state = {
grid: this.logic.getCurrentGrid(),
gameRunning: false,
@ -44,38 +40,32 @@ class GameScreen extends React.Component<Props, State> {
gameScore: 0,
gameLevel: 0,
};
this.onTick = this.onTick.bind(this);
this.onClock = this.onClock.bind(this);
this.onGameEnd = this.onGameEnd.bind(this);
this.updateGrid = this.updateGrid.bind(this);
this.updateGridScore = this.updateGridScore.bind(this);
this.props.navigation.addListener('blur', this.onScreenBlur.bind(this));
this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
this.props.navigation.addListener('blur', this.onScreenBlur);
this.props.navigation.addListener('focus', this.onScreenFocus);
}
componentDidMount() {
const rightButton = this.getRightButton.bind(this);
this.props.navigation.setOptions({
headerRight: rightButton,
headerRight: this.getRightButton,
});
this.startGame();
}
getRightButton() {
getRightButton = () => {
return <MaterialHeaderButtons>
<Item title="pause" iconName="pause" onPress={() => this.togglePause()}/>
<Item title="pause" iconName="pause" onPress={this.togglePause}/>
</MaterialHeaderButtons>;
}
/**
* Remove any interval on un-focus
*/
onScreenBlur() {
onScreenBlur = () => {
if (!this.logic.isGamePaused())
this.logic.togglePause();
}
onScreenFocus() {
onScreenFocus = () => {
if (!this.logic.isGameRunning())
this.startGame();
else if (this.logic.isGamePaused())
@ -97,7 +87,7 @@ class GameScreen extends React.Component<Props, State> {
return format;
}
onTick(score: number, level: number, newGrid: Array<Array<Object>>) {
onTick = (score: number, level: number, newGrid: Grid) => {
this.setState({
gameScore: score,
gameLevel: level,
@ -105,50 +95,50 @@ class GameScreen extends React.Component<Props, State> {
});
}
onClock(time: number) {
onClock = (time: number) => {
this.setState({
gameTime: time,
});
}
updateGrid(newGrid: Array<Array<Object>>) {
updateGrid = (newGrid: Grid) => {
this.setState({
grid: newGrid,
});
}
updateGridScore(newGrid: Array<Array<Object>>, score: number) {
updateGridScore = (newGrid: Grid, score: number) => {
this.setState({
grid: newGrid,
gameScore: score,
});
}
togglePause() {
togglePause = () => {
this.logic.togglePause();
if (this.logic.isGamePaused())
this.showPausePopup();
}
showPausePopup() {
showPausePopup = () => {
Alert.alert(
i18n.t("screens.game.pause"),
i18n.t("screens.game.pauseMessage"),
[
{text: i18n.t("screens.game.restart.text"), onPress: () => this.showRestartConfirm()},
{text: i18n.t("screens.game.resume"), onPress: () => this.togglePause()},
{text: i18n.t("screens.game.restart.text"), onPress: this.showRestartConfirm},
{text: i18n.t("screens.game.resume"), onPress: this.togglePause},
],
{cancelable: false},
);
}
showRestartConfirm() {
showRestartConfirm = () => {
Alert.alert(
i18n.t("screens.game.restart.confirm"),
i18n.t("screens.game.restart.confirmMessage"),
[
{text: i18n.t("screens.game.restart.confirmNo"), onPress: () => this.showPausePopup()},
{text: i18n.t("screens.game.restart.confirmYes"), onPress: () => this.startGame()},
{text: i18n.t("screens.game.restart.confirmNo"), onPress: this.showPausePopup},
{text: i18n.t("screens.game.restart.confirmYes"), onPress: this.startGame},
],
{cancelable: false},
);
@ -163,20 +153,20 @@ class GameScreen extends React.Component<Props, State> {
message,
[
{text: i18n.t("screens.game.gameOver.exit"), onPress: () => this.props.navigation.goBack()},
{text: i18n.t("screens.game.restart.text"), onPress: () => this.startGame()},
{text: i18n.t("screens.game.restart.text"), onPress: this.startGame},
],
{cancelable: false},
);
}
startGame() {
startGame = () => {
this.logic.startGame(this.onTick, this.onClock, this.onGameEnd);
this.setState({
gameRunning: true,
});
}
onGameEnd(time: number, score: number, isRestart: boolean) {
onGameEnd = (time: number, score: number, isRestart: boolean) => {
this.setState({
gameTime: time,
gameScore: score,
@ -187,6 +177,7 @@ class GameScreen extends React.Component<Props, State> {
}
render() {
const colors = this.props.theme.colors;
return (
<View style={{
width: '100%',
@ -200,11 +191,11 @@ class GameScreen extends React.Component<Props, State> {
}}>
<MaterialCommunityIcons
name={'timer'}
color={this.colors.subtitle}
color={colors.subtitle}
size={20}/>
<Text style={{
marginLeft: 5,
color: this.colors.subtitle
color: colors.subtitle
}}>{this.getFormattedTime(this.state.gameTime)}</Text>
</View>
<View style={{
@ -215,7 +206,7 @@ class GameScreen extends React.Component<Props, State> {
}}>
<MaterialCommunityIcons
name={'gamepad'}
color={this.colors.text}
color={colors.text}
size={20}/>
<Text style={{
marginLeft: 5
@ -228,20 +219,20 @@ class GameScreen extends React.Component<Props, State> {
}}>
<MaterialCommunityIcons
name={'star'}
color={this.colors.tetrisScore}
color={colors.tetrisScore}
size={30}/>
<Text style={{
marginLeft: 5,
fontSize: 22,
}}>{this.state.gameScore}</Text>
</View>
<Grid
<GridComponent
width={this.logic.getWidth()}
height={this.logic.getHeight()}
containerMaxHeight={'80%'}
containerMaxWidth={'60%'}
grid={this.state.grid}
backgroundColor={this.colors.tetrisBackground}
backgroundColor={colors.tetrisBackground}
/>
<View style={{
position: 'absolute',
@ -249,7 +240,7 @@ class GameScreen extends React.Component<Props, State> {
right: 5,
}}>
<Preview
next={this.logic.getNextPiecesPreviews()}
items={this.logic.getNextPiecesPreviews()}
/>
</View>
<View style={{
@ -287,7 +278,7 @@ class GameScreen extends React.Component<Props, State> {
onPressIn={() => this.logic.downPressedIn(this.updateGridScore)}
onPress={() => this.logic.pressedOut()}
style={{marginLeft: 'auto'}}
color={this.colors.tetrisScore}
color={colors.tetrisScore}
/>
</View>
</View>

View file

@ -2,39 +2,37 @@
import Piece from "./Piece";
import ScoreManager from "./ScoreManager";
import type {coordinates} from './Shapes/BaseShape';
export type cell = {color: string, isEmpty: boolean, key: string};
export type grid = Array<Array<cell>>;
import type {Coordinates} from './Shapes/BaseShape';
import type {Grid} from "./components/GridComponent";
import type {Cell} from "./components/CellComponent";
import type {CustomTheme} from "../../managers/ThemeManager";
/**
* Class used to manage the game grid
*
*/
export default class GridManager {
#currentGrid: grid;
#colors: Object;
#currentGrid: Grid;
#theme: CustomTheme;
/**
* Initializes a grid of the given size
*
* @param width The grid width
* @param height The grid height
* @param colors Object containing current theme colors
* @param theme Object containing current theme
*/
constructor(width: number, height: number, colors: Object) {
this.#colors = colors;
constructor(width: number, height: number, theme: CustomTheme) {
this.#theme = theme;
this.#currentGrid = this.getEmptyGrid(height, width);
}
/**
* Get the current grid
*
* @return {grid} The current grid
* @return {Grid} The current grid
*/
getCurrentGrid(): grid {
getCurrentGrid(): Grid {
return this.#currentGrid;
}
@ -42,13 +40,13 @@ export default class GridManager {
* Get a new empty grid line of the given size
*
* @param width The line size
* @return {Array<cell>}
* @return {Array<Cell>}
*/
getEmptyLine(width: number): Array<cell> {
getEmptyLine(width: number): Array<Cell> {
let line = [];
for (let col = 0; col < width; col++) {
line.push({
color: this.#colors.tetrisBackground,
color: this.#theme.colors.tetrisBackground,
isEmpty: true,
key: col.toString(),
});
@ -61,9 +59,9 @@ export default class GridManager {
*
* @param width The grid width
* @param height The grid height
* @return {grid} A new empty grid
* @return {Grid} A new empty grid
*/
getEmptyGrid(height: number, width: number): grid {
getEmptyGrid(height: number, width: number): Grid {
let grid = [];
for (let row = 0; row < height; row++) {
grid.push(this.getEmptyLine(width));
@ -91,21 +89,21 @@ export default class GridManager {
* Gets the lines to clear around the given piece's coordinates.
* The piece's coordinates are used for optimization and to prevent checking the whole grid.
*
* @param coord The piece's coordinates to check lines at
* @param pos The piece's coordinates to check lines at
* @return {Array<number>} An array containing the line numbers to clear
*/
getLinesToClear(coord: Array<coordinates>): Array<number> {
getLinesToClear(pos: Array<Coordinates>): Array<number> {
let rows = [];
for (let i = 0; i < coord.length; i++) {
for (let i = 0; i < pos.length; i++) {
let isLineFull = true;
for (let col = 0; col < this.#currentGrid[coord[i].y].length; col++) {
if (this.#currentGrid[coord[i].y][col].isEmpty) {
for (let col = 0; col < this.#currentGrid[pos[i].y].length; col++) {
if (this.#currentGrid[pos[i].y][col].isEmpty) {
isLineFull = false;
break;
}
}
if (isLineFull && rows.indexOf(coord[i].y) === -1)
rows.push(coord[i].y);
if (isLineFull && rows.indexOf(pos[i].y) === -1)
rows.push(pos[i].y);
}
return rows;
}

View file

@ -5,8 +5,10 @@ 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 type {grid} from './GridManager';
import type {Coordinates} from './Shapes/BaseShape';
import BaseShape from "./Shapes/BaseShape";
import type {Grid} from "./components/GridComponent";
import type {CustomTheme} from "../../managers/ThemeManager";
/**
* Class used as an abstraction layer for shapes.
@ -24,26 +26,26 @@ export default class Piece {
ShapeT,
ShapeZ,
];
#currentShape: Object;
#colors: Object;
#currentShape: BaseShape;
#theme: CustomTheme;
/**
* Initializes this piece's color and shape
*
* @param colors Object containing current theme colors
* @param theme Object containing current theme
*/
constructor(colors: Object) {
this.#currentShape = this.getRandomShape(colors);
this.#colors = colors;
constructor(theme: CustomTheme) {
this.#currentShape = this.getRandomShape(theme);
this.#theme = theme;
}
/**
* Gets a random shape object
*
* @param colors Object containing current theme colors
* @param theme Object containing current theme
*/
getRandomShape(colors: Object) {
return new this.#shapes[Math.floor(Math.random() * 7)](colors);
getRandomShape(theme: CustomTheme) {
return new this.#shapes[Math.floor(Math.random() * 7)](theme);
}
/**
@ -51,13 +53,13 @@ export default class Piece {
*
* @param grid The grid to remove the piece from
*/
removeFromGrid(grid: grid) {
const coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(true);
for (let i = 0; i < coord.length; i++) {
grid[coord[i].y][coord[i].x] = {
color: this.#colors.tetrisBackground,
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[coord[i].y][coord[i].x].key
key: grid[pos[i].y][pos[i].x].key
};
}
}
@ -68,13 +70,13 @@ export default class Piece {
* @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 coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(!isPreview);
for (let i = 0; i < coord.length; i++) {
grid[coord[i].y][coord[i].x] = {
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[coord[i].y][coord[i].x].key
key: grid[pos[i].y][pos[i].x].key
};
}
}
@ -87,15 +89,15 @@ export default class Piece {
* @param height The grid's height
* @return {boolean} If the position is valid
*/
isPositionValid(grid: grid, width: number, height: number) {
isPositionValid(grid: Grid, width: number, height: number) {
let isValid = true;
const coord: Array<coordinates> = this.#currentShape.getCellsCoordinates(true);
for (let i = 0; i < coord.length; i++) {
if (coord[i].x >= width
|| coord[i].x < 0
|| coord[i].y >= height
|| coord[i].y < 0
|| !grid[coord[i].y][coord[i].x].isEmpty) {
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;
}
@ -114,7 +116,7 @@ export default class Piece {
* @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: Function) {
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;
@ -143,7 +145,7 @@ export default class Piece {
* @param height The grid's height
* @return {boolean} True if the rotation was valid, false otherwise
*/
tryRotate(grid: grid, width: number, height: number) {
tryRotate(grid: Grid, width: number, height: number) {
this.removeFromGrid(grid);
this.#currentShape.rotate(true);
if (!this.isPositionValid(grid, width, height)) {
@ -158,9 +160,9 @@ export default class Piece {
/**
* Gets this piece used cells coordinates
*
* @return {Array<coordinates>} An array of coordinates
* @return {Array<Coordinates>} An array of coordinates
*/
getCoordinates(): Array<coordinates> {
getCoordinates(): Array<Coordinates> {
return this.#currentShape.getCellsCoordinates(true);
}
}

View file

@ -1,10 +1,14 @@
// @flow
export type coordinates = {
import type {CustomTheme} from "../../../managers/ThemeManager";
export type Coordinates = {
x: number,
y: number,
}
type Shape = Array<Array<number>>;
/**
* Abstract class used to represent a BaseShape.
* Abstract classes do not exist by default in Javascript: we force it by throwing errors in the constructor
@ -12,16 +16,18 @@ export type coordinates = {
*/
export default class BaseShape {
#currentShape: Array<Array<number>>;
#currentShape: Shape;
#rotation: number;
position: coordinates;
position: Coordinates;
theme: CustomTheme;
/**
* Prevent instantiation if classname is BaseShape to force class to be abstract
*/
constructor() {
constructor(theme: CustomTheme) {
if (this.constructor === BaseShape)
throw new Error("Abstract class can't be instantiated");
this.theme = theme;
this.#rotation = 0;
this.position = {x: 0, y: 0};
this.#currentShape = this.getShapes()[this.#rotation];
@ -41,7 +47,7 @@ export default class BaseShape {
*
* Used by tests to read private fields
*/
getShapes(): Array<Array<Array<number>>> {
getShapes(): Array<Shape> {
throw new Error("Method 'getShapes()' must be implemented");
}
@ -50,7 +56,7 @@ export default class BaseShape {
*
* Used by tests to read private fields
*/
getCurrentShape(): Array<Array<number>> {
getCurrentShape(): Shape {
return this.#currentShape;
}
@ -59,9 +65,9 @@ export default class BaseShape {
* This will return an array of coordinates representing the positions of the cells used by this object.
*
* @param isAbsolute Should we take into account the current position of the object?
* @return {Array<coordinates>} This object cells coordinates
* @return {Array<Coordinates>} This object cells coordinates
*/
getCellsCoordinates(isAbsolute: boolean): Array<coordinates> {
getCellsCoordinates(isAbsolute: boolean): Array<Coordinates> {
let coordinates = [];
for (let row = 0; row < this.#currentShape.length; row++) {
for (let col = 0; col < this.#currentShape[row].length; col++) {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeI extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisI;
return this.theme.colors.tetrisI;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeJ extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisJ;
return this.theme.colors.tetrisJ;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeL extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisL;
return this.theme.colors.tetrisL;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeO extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 4;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisO;
return this.theme.colors.tetrisO;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeS extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisS;
return this.theme.colors.tetrisS;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeT extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisT;
return this.theme.colors.tetrisT;
}
getShapes() {

View file

@ -1,19 +1,17 @@
// @flow
import BaseShape from "./BaseShape";
import type {CustomTheme} from "../../../managers/ThemeManager";
export default class ShapeZ extends BaseShape {
#colors: Object;
constructor(colors: Object) {
super();
constructor(theme: CustomTheme) {
super(theme);
this.position.x = 3;
this.#colors = colors;
}
getColor(): string {
return this.#colors.tetrisZ;
return this.theme.colors.tetrisZ;
}
getShapes() {

View file

@ -3,28 +3,25 @@
import * as React from 'react';
import {View} from 'react-native';
import {withTheme} from 'react-native-paper';
import type {CustomTheme} from "../../../managers/ThemeManager";
export type Cell = {color: string, isEmpty: boolean, key: string};
type Props = {
item: Object
cell: Cell,
theme: CustomTheme,
}
class Cell extends React.PureComponent<Props> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
class CellComponent extends React.PureComponent<Props> {
render() {
const item = this.props.item;
const item = this.props.cell;
return (
<View
style={{
flex: 1,
backgroundColor: item.isEmpty ? 'transparent' : item.color,
borderColor: item.isEmpty ? 'transparent' : this.colors.tetrisBorder,
borderColor: item.isEmpty ? 'transparent' : this.props.theme.colors.tetrisBorder,
borderStyle: 'solid',
borderRadius: 2,
borderWidth: 1,
@ -38,4 +35,4 @@ class Cell extends React.PureComponent<Props> {
}
export default withTheme(Cell);
export default withTheme(CellComponent);

View file

@ -3,10 +3,12 @@
import * as React from 'react';
import {View} from 'react-native';
import {withTheme} from 'react-native-paper';
import Cell from "./Cell";
import type {Cell} from "./CellComponent";
import CellComponent from "./CellComponent";
export type Grid = Array<Array<CellComponent>>;
type Props = {
navigation: Object,
grid: Array<Array<Object>>,
backgroundColor: string,
height: number,
@ -15,14 +17,7 @@ type Props = {
containerMaxWidth: number | string,
}
class Grid extends React.Component<Props> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
class GridComponent extends React.Component<Props> {
getRow(rowNumber: number) {
let cells = this.props.grid[rowNumber].map(this.getCellRender);
@ -39,8 +34,8 @@ class Grid extends React.Component<Props> {
);
}
getCellRender = (item: Object) => {
return <Cell item={item} key={item.key}/>;
getCellRender = (item: Cell) => {
return <CellComponent cell={item}/>;
};
getGrid() {
@ -67,4 +62,4 @@ class Grid extends React.Component<Props> {
}
}
export default withTheme(Grid);
export default withTheme(GridComponent);

View file

@ -3,33 +3,25 @@
import * as React from 'react';
import {View} from 'react-native';
import {withTheme} from 'react-native-paper';
import Grid from "./Grid";
import type {Grid} from "./GridComponent";
import GridComponent from "./GridComponent";
type Props = {
next: Object,
items: Array<Grid>,
}
class Preview extends React.PureComponent<Props> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
getGrids() {
let grids = [];
for (let i = 0; i < this.props.next.length; i++) {
grids.push(
this.getGridRender(this.props.next[i], i)
);
for (let i = 0; i < this.props.items.length; i++) {
grids.push(this.getGridRender(this.props.items[i], i));
}
return grids;
}
getGridRender(item: Object, index: number) {
return <Grid
getGridRender(item: Grid, index: number) {
return <GridComponent
width={item[0].length}
height={item.length}
grid={item}
@ -41,7 +33,7 @@ class Preview extends React.PureComponent<Props> {
};
render() {
if (this.props.next.length > 0) {
if (this.props.items.length > 0) {
return (
<View>
{this.getGrids()}