Refactor tic-tac-toe plugin to Sandy architecture
Summary: CHANGELOG: Refactor tic-tac-toe plugin to Sandy architecture Sandy architecture unblocks using the plugin in a headless mode. Eventual goal - platy tic-tac-toe form a terminal Reviewed By: passy Differential Revision: D36513795 fbshipit-source-id: 5a967a325cfb52cc3ae72840240b22b0a4e8f031
This commit is contained in:
committed by
Facebook GitHub Bot
parent
566f7108c9
commit
a7b148fcc2
@@ -8,20 +8,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {Typography, Button, Alert, Space} from 'antd';
|
||||||
FlipperPlugin,
|
|
||||||
RoundedSection,
|
|
||||||
Button,
|
|
||||||
produce,
|
|
||||||
CenteredView,
|
|
||||||
Info,
|
|
||||||
colors,
|
|
||||||
styled,
|
|
||||||
FlexRow,
|
|
||||||
Text,
|
|
||||||
brandColors,
|
|
||||||
} from 'flipper';
|
|
||||||
import {Draft} from 'immer';
|
import {Draft} from 'immer';
|
||||||
|
import {
|
||||||
|
createState,
|
||||||
|
PluginClient,
|
||||||
|
usePlugin,
|
||||||
|
useValue,
|
||||||
|
styled,
|
||||||
|
theme,
|
||||||
|
Layout,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
|
||||||
type Player = ' ' | 'X' | 'O';
|
type Player = ' ' | 'X' | 'O';
|
||||||
|
|
||||||
@@ -53,13 +50,12 @@ function initialState(): State {
|
|||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
const computeNextState = produce(
|
const computeNextState =
|
||||||
(draft: Draft<State>, cell: number, player: 'X' | 'O') => {
|
(cell: number, player: 'X' | 'O') => (draft: Draft<State>) => {
|
||||||
draft.cells[cell] = player;
|
draft.cells[cell] = player;
|
||||||
draft.turn = player === 'X' ? 'O' : 'X';
|
draft.turn = player === 'X' ? 'O' : 'X';
|
||||||
draft.winner = computeWinner(draft.cells);
|
draft.winner = computeWinner(draft.cells);
|
||||||
},
|
};
|
||||||
);
|
|
||||||
|
|
||||||
function computeWinner(c: State['cells']): Player {
|
function computeWinner(c: State['cells']): Player {
|
||||||
// check the 2 diagonals
|
// check the 2 diagonals
|
||||||
@@ -79,110 +75,119 @@ function computeWinner(c: State['cells']): Player {
|
|||||||
return ' ';
|
return ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ReactNativeTicTacToe extends FlipperPlugin<
|
type Events = {
|
||||||
State,
|
XMove: {move: number};
|
||||||
any,
|
GetState: never;
|
||||||
any
|
};
|
||||||
> {
|
|
||||||
state = initialState();
|
|
||||||
|
|
||||||
componentDidMount() {
|
type Methods = {
|
||||||
this.client.subscribe('XMove', ({move}: {move: number}) => {
|
SetState: (state: State) => Promise<void>;
|
||||||
this.makeMove('X', move);
|
};
|
||||||
});
|
|
||||||
this.client.subscribe('GetState', () => {
|
|
||||||
this.sendUpdate();
|
|
||||||
});
|
|
||||||
this.sendUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
makeMove(player: 'X' | 'O', move: number) {
|
export const plugin = (client: PluginClient<Events, Methods>) => {
|
||||||
if (this.state.turn === player && this.state.cells[move] === ' ') {
|
const state = createState(initialState());
|
||||||
this.setState(computeNextState(this.state, move, player), () =>
|
|
||||||
this.sendUpdate(),
|
const sendUpdate = () => {
|
||||||
);
|
client.send('SetState', state.get());
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeMove = (player: 'X' | 'O', move: number) => {
|
||||||
|
if (state.get().turn === player && state.get().cells[move] === ' ') {
|
||||||
|
state.update(computeNextState(move, player));
|
||||||
|
sendUpdate();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
sendUpdate() {
|
const reset = () => {
|
||||||
this.client.call('SetState', this.state);
|
state.set(initialState());
|
||||||
}
|
sendUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
handleCellClick(move: number) {
|
client.onConnect(() => {
|
||||||
this.makeMove('O', move);
|
client.onMessage('XMove', ({move}) => {
|
||||||
}
|
makeMove('X', move);
|
||||||
|
});
|
||||||
|
client.onMessage('GetState', () => {
|
||||||
|
sendUpdate();
|
||||||
|
});
|
||||||
|
sendUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
handleReset() {
|
return {
|
||||||
this.setState(initialState(), () => this.sendUpdate());
|
makeMove,
|
||||||
}
|
reset,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
const desktopPlayer = 'O';
|
||||||
const {winner, turn, cells} = this.state;
|
|
||||||
return (
|
|
||||||
<CenteredView>
|
|
||||||
<RoundedSection title="React Native Tic-Tac-Toe">
|
|
||||||
<Info type="info">
|
|
||||||
This plugin demonstrates how to create pure JavaScript Flipper
|
|
||||||
plugins for React Native. Find out how to create a similar plugin at{' '}
|
|
||||||
<a href="https://fbflipper.com/docs/tutorial/intro" target="blank">
|
|
||||||
fbflipper.com
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</Info>
|
|
||||||
<Container>
|
|
||||||
<Text size={24}>Flipper Tic-Tac-Toe</Text>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<Text size={18}>
|
|
||||||
{winner !== ' '
|
|
||||||
? `Winner! ${winner}`
|
|
||||||
: turn === 'O'
|
|
||||||
? 'Your turn'
|
|
||||||
: 'Mobile players turn..'}
|
|
||||||
</Text>
|
|
||||||
<GameBoard>
|
|
||||||
{cells.map((c, idx) => (
|
|
||||||
<Cell
|
|
||||||
key={idx}
|
|
||||||
disabled={c !== ' ' || turn != 'O' || winner !== ' '}
|
|
||||||
onClick={() => this.handleCellClick(idx)}>
|
|
||||||
{c}
|
|
||||||
</Cell>
|
|
||||||
))}
|
|
||||||
</GameBoard>
|
|
||||||
<Button onClick={() => this.handleReset()}>Start new game</Button>
|
|
||||||
</Container>
|
|
||||||
</RoundedSection>
|
|
||||||
</CenteredView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Container = styled('div')({
|
export const Component = () => {
|
||||||
border: `4px solid ${brandColors.Flipper}`,
|
const pluginInstance = usePlugin(plugin);
|
||||||
borderRadius: 4,
|
const {winner, turn, cells} = useValue(pluginInstance.state);
|
||||||
padding: 20,
|
|
||||||
marginTop: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
const GameBoard = styled(FlexRow)({
|
return (
|
||||||
flexWrap: 'wrap',
|
<Layout.Container>
|
||||||
justifyContent: 'space-between',
|
<Space direction="vertical" align="center">
|
||||||
marginTop: 20,
|
<Alert
|
||||||
marginBottom: 20,
|
message={
|
||||||
|
<>
|
||||||
|
This plugin demonstrates how to create pure JavaScript Flipper
|
||||||
|
plugins for React Native. Find out how to create a similar plugin
|
||||||
|
at{' '}
|
||||||
|
<a
|
||||||
|
href="https://fbflipper.com/docs/tutorial/intro"
|
||||||
|
target="blank">
|
||||||
|
fbflipper.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
type="info"
|
||||||
|
/>
|
||||||
|
<Typography.Title>Flipper Tic-Tac-Toe</Typography.Title>
|
||||||
|
<Typography.Text>
|
||||||
|
{winner !== ' '
|
||||||
|
? `Winner! ${winner}`
|
||||||
|
: turn === 'O'
|
||||||
|
? 'Your turn'
|
||||||
|
: 'Mobile players turn..'}
|
||||||
|
</Typography.Text>
|
||||||
|
<GameBoard>
|
||||||
|
{cells.map((c, idx) => (
|
||||||
|
<Cell
|
||||||
|
key={idx}
|
||||||
|
disabled={c !== ' ' || turn != desktopPlayer || winner !== ' '}
|
||||||
|
onClick={() => pluginInstance.makeMove(desktopPlayer, idx)}>
|
||||||
|
{c}
|
||||||
|
</Cell>
|
||||||
|
))}
|
||||||
|
</GameBoard>
|
||||||
|
<Button onClick={() => pluginInstance.reset()}>Start new game</Button>
|
||||||
|
</Space>
|
||||||
|
</Layout.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GameBoard = styled('div')({
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(3, 80px)',
|
||||||
|
gridTemplateRows: 'repeat(3, 80px)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Cell = styled('button')({
|
const Cell = styled('button')({
|
||||||
padding: 20,
|
padding: 20,
|
||||||
height: 80,
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
margin: 20,
|
|
||||||
flex: 0,
|
flex: 0,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
backgroundColor: colors.highlight,
|
backgroundColor: theme.backgroundDefault,
|
||||||
color: 'white',
|
color: 'white',
|
||||||
':disabled': {
|
':disabled': {
|
||||||
backgroundColor: colors.grayTint2,
|
backgroundColor: theme.disabledColor,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,5 +14,8 @@
|
|||||||
"category": "Examples",
|
"category": "Examples",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/facebook/flipper/issues"
|
"url": "https://github.com/facebook/flipper/issues"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"flipper-plugin": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user