From db9c41303d0d63951470e54bc004513c3f5fa174 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Thu, 16 Jan 2020 04:45:03 -0800 Subject: [PATCH] Introduce Flipper Tic Tac Toe example Summary: This Diff introduces an example for how to develop a React Native pure JS plugin and will be used in the docs. See the attached project as demo. The RN sources for the plugin component are: ``` import React, {useState, useEffect} from "react"; import { StyleSheet, View, Text, Button, } from 'react-native'; import {addPlugin} from "react-native-flipper"; const initialState = { cells: [" ", " ", " "," ", " ", " "," ", " ", " ",], turn: ' ', winner: ' ', } export default function FlipperTicTacToe() { const [status, setStatus] = useState("Waiting for Flipper Desktop Player...") const [gameState, setGameState] = useState(initialState); const [connection, setConnection] = useState(null); useEffect(() => { addPlugin({ getId() { return 'ReactNativeTicTacToe'; }, onConnect(connection) { setStatus("Desktop player present"); setConnection(connection); // listen to updates connection.receive('SetState', (gameState, responder) => { if (gameState.winner !== " ") { setStatus(`Winner is ${gameState.winner}! Waiting for a new game...`); } else { setStatus(gameState.turn === "X" ? "Your turn...": "Awaiting desktop players turn..."); } setGameState(gameState); responder.success(); }) // request initial state connection.send('GetState'); }, onDisconnect() { setConnection(null); setStatus("Desktop player gone..."); } }) }, []); return ( Flipper Tic-Tac-Toe {status} {gameState.cells.map((state, idx) => -
{JSON.stringify(persistedState, null, 2)}
-
- ); - } -} diff --git a/src/plugins/rn-tic-tac-toe/index.tsx b/src/plugins/rn-tic-tac-toe/index.tsx new file mode 100644 index 000000000..755e88179 --- /dev/null +++ b/src/plugins/rn-tic-tac-toe/index.tsx @@ -0,0 +1,190 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import { + FlipperPlugin, + RoundedSection, + Button, + produce, + CenteredView, + Info, + colors, + styled, + FlexRow, + Text, + brandColors, +} from 'flipper'; +import {Draft} from 'immer'; + +type Player = ' ' | 'X' | 'O'; + +type State = { + cells: readonly [ + Player, + Player, + Player, + Player, + Player, + Player, + Player, + Player, + Player, + ]; + winner: Player; + turn: 'X' | 'O'; +}; + +function initialState(): State { + return { + // Cells + // 0 1 2 + // 3 4 5 + // 6 7 8 + cells: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] as const, + turn: Math.random() < 0.5 ? 'O' : 'X', + winner: ' ', + } as const; +} + +const computeNextState = produce( + (draft: Draft, cell: number, player: 'X' | 'O') => { + draft.cells[cell] = player; + draft.turn = player === 'X' ? 'O' : 'X'; + draft.winner = computeWinner(draft.cells); + }, +); + +function computeWinner(c: State['cells']): Player { + // check the 2 diagonals + if ((c[0] === c[4] && c[0] === c[8]) || (c[2] === c[4] && c[2] === c[6])) { + return c[4]; + } + for (let i = 0; i < 3; i++) { + // check vertical + if (c[i] === c[3 + i] && c[i] === c[6 + i]) { + return c[i]; + } + // check horizontal + if (c[i * 3] === c[i * 3 + 1] && c[i * 3] === c[i * 3 + 2]) { + return c[i * 3]; + } + } + return ' '; +} + +export default class ReactNativeTicTacToe extends FlipperPlugin< + State, + any, + any +> { + state = initialState(); + + componentDidMount() { + this.client.subscribe('XMove', ({move}: {move: number}) => { + this.makeMove('X', move); + }); + this.client.subscribe('GetState', () => { + this.sendUpdate(); + }); + this.sendUpdate(); + } + + makeMove(player: 'X' | 'O', move: number) { + if (this.state.turn === player && this.state.cells[move] === ' ') { + this.setState(computeNextState(this.state, move, player), () => + this.sendUpdate(), + ); + } + } + + sendUpdate() { + this.client.call('SetState', this.state); + } + + handleCellClick(move: number) { + this.makeMove('O', move); + } + + handleReset() { + this.setState(initialState(), () => this.sendUpdate()); + } + + render() { + const {winner, turn, cells} = this.state; + return ( + + + + This plugin demonstrates how to create pure JavaScript Flipper + plugins for React Native. Find out how to create a similar plugin at{' '} + + fbflipper.com + + . + + + Flipper Tic-Tac-Toe +
+
+ + {winner !== ' ' + ? `Winner! ${winner}` + : turn === 'O' + ? 'Your turn' + : 'Mobile players turn..'} + + + {cells.map((c, idx) => ( + this.handleCellClick(idx)}> + {c} + + ))} + + +
+
+
+ ); + } +} + +const Container = styled('div')({ + border: `4px solid ${brandColors.Flipper}`, + borderRadius: 4, + padding: 20, + marginTop: 20, +}); + +const GameBoard = styled(FlexRow)({ + flexWrap: 'wrap', + justifyContent: 'space-between', + marginTop: 20, + marginBottom: 20, +}); + +const Cell = styled('button')({ + padding: 20, + height: 80, + minWidth: 80, + fontSize: 24, + margin: 20, + flex: 0, + borderRadius: 4, + backgroundColor: colors.highlight, + color: 'white', + ':disabled': { + backgroundColor: colors.greyTint2, + }, +}); diff --git a/src/plugins/rn-example-plugin/package.json b/src/plugins/rn-tic-tac-toe/package.json similarity index 60% rename from src/plugins/rn-example-plugin/package.json rename to src/plugins/rn-tic-tac-toe/package.json index 142be1ac5..a6b7d9a02 100644 --- a/src/plugins/rn-example-plugin/package.json +++ b/src/plugins/rn-tic-tac-toe/package.json @@ -1,12 +1,12 @@ { - "name": "ReactNativeExamplePlugin", + "name": "ReactNativeTicTacToe", "version": "1.0.0", "main": "index.tsx", "license": "MIT", "keywords": ["flipper-plugin"], "icon": "apps", - "title": "React Native Example Plugin", - "category": "Example Plugin", + "title": "React Native Tic Tac Toe", + "category": "Examples", "bugs": { "email": "mweststrate@fb.com" } diff --git a/src/plugins/rn-example-plugin/yarn.lock b/src/plugins/rn-tic-tac-toe/yarn.lock similarity index 100% rename from src/plugins/rn-example-plugin/yarn.lock rename to src/plugins/rn-tic-tac-toe/yarn.lock