|
| 1 | +/* eslint-disable jsx-a11y/click-events-have-key-events */ |
1 | 2 | import { Chess } from 'chess.ts' |
2 | | -import { useEffect, useState } from 'react' |
| 3 | +import { useContext, useEffect, useState } from 'react' |
3 | 4 |
|
4 | | -import { Move } from 'src/types' |
| 5 | +import { Move, PlayedGame } from 'src/types' |
| 6 | +import { GameControllerContext } from 'src/contexts' |
5 | 7 |
|
6 | 8 | interface Props { |
7 | | - moves: Move[] |
| 9 | + game: PlayedGame |
| 10 | + whitePlayer: string |
| 11 | + blackPlayer: string |
| 12 | + maiaVersion: string |
8 | 13 | } |
9 | 14 |
|
10 | | -export const ExportGame: React.FC<Props> = ({ moves }) => { |
| 15 | +export const ExportGame: React.FC<Props> = ({ |
| 16 | + game, |
| 17 | + whitePlayer, |
| 18 | + blackPlayer, |
| 19 | + maiaVersion, |
| 20 | +}) => { |
| 21 | + const [fen, setFen] = useState('') |
11 | 22 | const [pgn, setPgn] = useState('') |
| 23 | + const { currentIndex } = useContext(GameControllerContext) |
12 | 24 |
|
13 | 25 | useEffect(() => { |
14 | 26 | const chess = new Chess() |
15 | | - moves.forEach((move) => { |
| 27 | + game.moves.forEach((move) => { |
16 | 28 | if (move.san) { |
17 | 29 | chess.move(move.san) |
18 | 30 | } |
19 | 31 | }) |
20 | 32 | setPgn(chess.pgn()) |
21 | | - }, [moves]) |
| 33 | + }, [game.moves]) |
| 34 | + |
| 35 | + useEffect(() => { |
| 36 | + const initial = new Chess(game.moves[0].board) |
| 37 | + initial.addHeader('ID', game.id) |
| 38 | + initial.addHeader('Event', `Play v. ${maiaVersion}`) |
| 39 | + initial.addHeader('Site', `https://maiachess.com/`) |
| 40 | + initial.addHeader('White', whitePlayer) |
| 41 | + initial.addHeader('Black', blackPlayer) |
| 42 | + if (game.termination) { |
| 43 | + initial.addHeader('Result', game.termination.result) |
| 44 | + if (game.termination.condition) { |
| 45 | + initial.addHeader('Termination', game.termination.condition) |
| 46 | + } |
| 47 | + } |
| 48 | + game.moves.forEach((move, index) => { |
| 49 | + if (!move.san || index > currentIndex) { |
| 50 | + return |
| 51 | + } |
| 52 | + |
| 53 | + initial.move(move.san) |
| 54 | + }) |
| 55 | + setFen(game.moves[currentIndex].board) |
| 56 | + setPgn(initial.pgn()) |
| 57 | + }, [currentIndex, game.moves]) |
22 | 58 |
|
23 | 59 | const copy = (content: string) => { |
24 | 60 | navigator.clipboard.writeText(content) |
25 | 61 | } |
26 | 62 |
|
27 | 63 | return ( |
28 | | - <div className="flex flex-row items-center justify-center gap-2"> |
29 | | - <div className="flex h-10 items-center overflow-hidden rounded border border-primary/10 bg-background-1"> |
30 | | - <div className="flex w-[500px] items-center justify-start bg-background-1 px-4"> |
31 | | - <p className="whitespace-nowrap text-sm text-secondary"> |
32 | | - {moves[moves.length - 1].board} |
| 64 | + <div className="flex flex-col gap-1"> |
| 65 | + <div className="flex flex-col gap-0.5"> |
| 66 | + <div className="flex items-center justify-between"> |
| 67 | + <p className="select-none text-sm font-semibold tracking-wider text-secondary"> |
| 68 | + FEN |
33 | 69 | </p> |
| 70 | + <i |
| 71 | + tabIndex={0} |
| 72 | + role="button" |
| 73 | + onClick={() => copy(fen)} |
| 74 | + className="material-symbols-outlined select-none text-base text-secondary hover:text-primary" |
| 75 | + > |
| 76 | + content_copy |
| 77 | + </i> |
34 | 78 | </div> |
35 | | - <button |
36 | | - onClick={() => copy(moves[moves.length - 1].board)} |
37 | | - className="flex h-10 w-10 min-w-10 items-center justify-center border-l border-primary/10 bg-background-2 transition duration-200 hover:bg-background-3" |
| 79 | + <div |
| 80 | + role="button" |
| 81 | + tabIndex={0} |
| 82 | + onClick={() => copy(fen)} |
| 83 | + className="border-1 group flex cursor-pointer overflow-x-hidden rounded border border-white/5 bg-background-1/50 px-4 py-2" |
38 | 84 | > |
39 | | - <span className="material-symbols-outlined text-xl"> |
| 85 | + <p className="whitespace-nowrap text-xs text-secondary group-hover:text-secondary/80"> |
| 86 | + {fen} |
| 87 | + </p> |
| 88 | + </div> |
| 89 | + </div> |
| 90 | + <div className="flex flex-col gap-0.5"> |
| 91 | + <div className="flex items-center justify-between"> |
| 92 | + <p className="select-none text-sm font-semibold tracking-wider text-secondary"> |
| 93 | + PGN |
| 94 | + </p> |
| 95 | + <i |
| 96 | + tabIndex={0} |
| 97 | + role="button" |
| 98 | + onClick={() => copy(pgn)} |
| 99 | + className="material-symbols-outlined select-none text-base text-secondary hover:text-primary" |
| 100 | + > |
40 | 101 | content_copy |
41 | | - </span> |
42 | | - </button> |
| 102 | + </i> |
| 103 | + </div> |
| 104 | + <div |
| 105 | + role="button" |
| 106 | + tabIndex={0} |
| 107 | + onClick={() => copy(pgn)} |
| 108 | + className="border-1 group flex cursor-pointer overflow-x-hidden rounded border border-white/5 bg-background-1/50 px-4 py-2" |
| 109 | + > |
| 110 | + <p className="whitespace-pre-wrap text-xs text-secondary group-hover:text-secondary/80"> |
| 111 | + {pgn} |
| 112 | + </p> |
| 113 | + </div> |
43 | 114 | </div> |
44 | | - <button |
45 | | - onClick={() => copy(pgn)} |
46 | | - className="flex h-10 items-center justify-center rounded border border-primary/10 bg-background-2 px-4 py-1 transition duration-300 hover:bg-background-3" |
47 | | - > |
48 | | - Copy PGN |
49 | | - </button> |
50 | 115 | </div> |
51 | 116 | ) |
52 | 117 | } |
0 commit comments