This commit is contained in:
Maciej Caderek
2022-02-18 04:21:35 +01:00
parent 70b2245570
commit ec4a63b59b
8 changed files with 123 additions and 31 deletions

View File

@@ -7,6 +7,7 @@ import drawPieces from "./layers/drawPieces";
import drawHeader from "./layers/drawHeader"; import drawHeader from "./layers/drawHeader";
import drawExtraInfo from "./layers/drawExtraInfo"; import drawExtraInfo from "./layers/drawExtraInfo";
import boards from "./styles-board"; import boards from "./styles-board";
import isLink from "../utils/isLink";
const defaultConfig: BoardConfig = { const defaultConfig: BoardConfig = {
size: 720, size: 720,
@@ -130,6 +131,7 @@ class Board {
setStyle(style: BoardStyle) { setStyle(style: BoardStyle) {
this.style = boards[style]; this.style = boards[style];
this.cfg.boardStyle = style;
this.refresh(); this.refresh();
return this; return this;
} }
@@ -146,6 +148,26 @@ class Board {
return this; return this;
} }
flipWhite() {
if (!this.cfg.flipped) {
return;
}
this.cfg.flipped = false;
this.refresh();
return this;
}
flipBlack() {
if (this.cfg.flipped) {
return;
}
this.cfg.flipped = true;
this.refresh();
return this;
}
hideBorder() { hideBorder() {
this.cfg.showBorder = false; this.cfg.showBorder = false;
this.setSize(this.size); this.setSize(this.size);
@@ -182,8 +204,14 @@ class Board {
Black: "Anonymous", Black: "Anonymous",
WhitePretty: "Anonymous", WhitePretty: "Anonymous",
BlackPretty: "Anonymous", BlackPretty: "Anonymous",
Site: isLink(this.header.Site) ? null : this.header.Site,
} }
: this.header; : {
...this.header,
Site: isLink(this.header.Site)
? (this.header.Site as string).replace(/^http[s]*:\/\//, "")
: this.header.Site,
};
} }
async titleFrame(header: Header) { async titleFrame(header: Header) {

View File

@@ -1,7 +1,8 @@
import { PieceType, PieceColor, BoardData, Position } from "../types"; import { PieceType, PieceColor, BoardData, Position, Header } from "../types";
import { Chess, ChessInstance } from "chess.js"; import { Chess, ChessInstance } from "chess.js";
import { cleanPGN } from "./PGNHelpers"; import { cleanPGN } from "./PGNHelpers";
import { formatDate, formatName } from "../utils/formatters"; import { formatDate, formatName } from "../utils/formatters";
import isLink from "../utils/isLink";
const MATERIAL_VALUE: Map<PieceType, number> = new Map([ const MATERIAL_VALUE: Map<PieceType, number> = new Map([
["q", 9], ["q", 9],
@@ -196,6 +197,17 @@ class Game {
return this.game.pgn(); return this.game.pgn();
} }
get anonymousPGN() {
const pgn = this.game
.pgn()
.replace(/\[White .+\]/, '[White "Anonymous"]')
.replace(/\[Black .+\]/, '[Black "Anonymous"]');
return isLink(this.header.Site)
? pgn.replace(/\[Site .+\]/, '[Site "?"]')
: pgn;
}
getPosition(ply: number) { getPosition(ply: number) {
const position = this.positions[ply]; const position = this.positions[ply];

View File

@@ -71,6 +71,13 @@ const main = async () => {
toggleAnonymous() { toggleAnonymous() {
setState("boardConfig", "anonymous", !state.boardConfig.anonymous); setState("boardConfig", "anonymous", !state.boardConfig.anonymous);
board.updateConfig({ anonymous: state.boardConfig.anonymous }); board.updateConfig({ anonymous: state.boardConfig.anonymous });
if (state.pgn !== "") {
const pgn = state.boardConfig.anonymous
? state.game.anonymousPGN
: state.game.pgn;
window.location.hash = `v1/pgn/${compressPGN(pgn)}`;
}
}, },
toggleTitleScreen() { toggleTitleScreen() {
setState("gameConfig", "titleScreen", !state.gameConfig.titleScreen); setState("gameConfig", "titleScreen", !state.gameConfig.titleScreen);
@@ -98,7 +105,7 @@ const main = async () => {
setState("boardConfig", "piecesStyle", style); setState("boardConfig", "piecesStyle", style);
saveConfig("board"); saveConfig("board");
}, },
async loadPGN(pgn: string) { async loadPGN(pgn: string, side: "w" | "b" = "w") {
const game = new Game().loadPGN(pgn); const game = new Game().loadPGN(pgn);
setState({ setState({
pgn: game.pgn, pgn: game.pgn,
@@ -111,23 +118,38 @@ const main = async () => {
await player.load(game); await player.load(game);
setState("activeTab", "game"); setState("activeTab", "game");
if (side === "w") {
board.flipWhite();
} else {
board.flipBlack();
}
setState("boardConfig", "flipped", side === "b");
document.title = `SHORTCASTLE - ${game.getTitle({ anonymous: false })}`; document.title = `SHORTCASTLE - ${game.getTitle({ anonymous: false })}`;
}, },
async loadFEN(fen: string) { async loadFEN(fen: string, hash = true) {
const game = new Game().loadFEN(fen); const game = new Game().loadFEN(fen);
setState({ pgn: "", fen, moves: game.getMoves(), ply: 0, game }); setState({ pgn: "", fen, moves: game.getMoves(), ply: 0, game });
window.location.hash = `v1/fen/${state.fen}`;
await player.load(game);
setState("activeTab", "game");
if ( await player.load(game);
game.getPosition(0).turn === "b" &&
state.boardConfig.flipped === false if (hash) {
) { window.location.hash = `v1/fen/${state.fen}`;
setState("boardConfig", "flipped", true); setState("activeTab", "game");
board.flip();
} }
const side = game.getPosition(0).turn;
if (side === "w") {
board.flipWhite();
} else {
board.flipBlack();
}
setState("boardConfig", "flipped", side === "b");
document.title = `SHORTCASTLE - FEN ${fen}`; document.title = `SHORTCASTLE - FEN ${fen}`;
}, },
async importPGN(link: string) { async importPGN(link: string) {
@@ -137,7 +159,7 @@ const main = async () => {
return; return;
} }
await this.loadPGN(result.pgn); await this.loadPGN(result.pgn, result.side);
}, },
async downloadImage() { async downloadImage() {
await new Promise((resolve) => setTimeout(resolve, 0)); await new Promise((resolve) => setTimeout(resolve, 0));
@@ -182,7 +204,8 @@ const main = async () => {
: fen : fen
? handlers.loadFEN(fen) ? handlers.loadFEN(fen)
: handlers.loadFEN( : handlers.loadFEN(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
false
)); ));
/* Register events */ /* Register events */

View File

@@ -43,7 +43,11 @@ export type State = {
}; };
const initialState: State = { const initialState: State = {
boardConfig: { ...initialBoardConfig, ...saved.boardConfig }, boardConfig: {
...initialBoardConfig,
...saved.boardConfig,
anonymous: false,
},
gameConfig: { ...initialGameConfig, ...saved.gameConfig }, gameConfig: { ...initialGameConfig, ...saved.gameConfig },
game: new Game(), game: new Game(),
pgn: "", pgn: "",

View File

@@ -162,7 +162,7 @@ a:hover {
} }
.board { .board {
border: solid 1rem var(--color-bg-block); box-shadow: 0 0 2rem #00000099;
border-radius: 5px; border-radius: 5px;
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;

View File

@@ -3,6 +3,7 @@ import { Handlers } from "../../types";
import { state } from "../../state"; import { state } from "../../state";
import "./Info.css"; import "./Info.css";
import isSafeLink from "../../utils/isSafeLink"; import isSafeLink from "../../utils/isSafeLink";
import isLink from "../../utils/isLink";
const Info: Component<{ handlers: Handlers }> = () => { const Info: Component<{ handlers: Handlers }> = () => {
return ( return (
@@ -10,17 +11,17 @@ const Info: Component<{ handlers: Handlers }> = () => {
<div className="info__players"> <div className="info__players">
<p> <p>
<button className="info__color info__color--white"></button> <button className="info__color info__color--white"></button>
{state.game.header.WhitePretty}{" "} <Show when={!state.boardConfig.anonymous} fallback="Anonymous">
<span className="info__rating"> {state.game.header.WhitePretty}{" "}
{state.game.header.WhiteElo ?? "????"} </Show>
</span> <span className="info__rating">{state.game.header.WhiteElo}</span>
</p> </p>
<p> <p>
<button className="info__color info__color--black"></button> <button className="info__color info__color--black"></button>
{state.game.header.BlackPretty}{" "} <Show when={!state.boardConfig.anonymous} fallback="Anonymous">
<span className="info__rating"> {state.game.header.BlackPretty}{" "}
{state.game.header.BlackElo ?? "????"} </Show>
</span> <span className="info__rating">{state.game.header.BlackElo}</span>
</p> </p>
</div> </div>
<div className="info__event"> <div className="info__event">
@@ -35,12 +36,18 @@ const Info: Component<{ handlers: Handlers }> = () => {
<Show when={state.game.header.Site}> <Show when={state.game.header.Site}>
<p> <p>
<Show <Show
when={isSafeLink(state.game.header.Site)} when={
fallback={state.game.header.Site} !state.boardConfig.anonymous || !isLink(state.game.header.Site)
}
> >
<a href={state.game.header.Site ?? ""}> <Show
{state.game.header.Site?.replace(/^https:\/\//, "")} when={isSafeLink(state.game.header.Site)}
</a> fallback={state.game.header.Site}
>
<a href={state.game.header.Site ?? ""}>
{state.game.header.Site?.replace(/^https:\/\//, "")}
</a>
</Show>
</Show> </Show>
</p> </p>
</Show> </Show>

View File

@@ -192,7 +192,11 @@ const Share: Component<{ handlers: Handlers }> = (props) => {
<button <button
class="share__btn" class="share__btn"
onClick={() => { onClick={() => {
navigator.clipboard.writeText(state.pgn); navigator.clipboard.writeText(
state.boardConfig.anonymous
? state.game.anonymousPGN
: state.pgn
);
blinkCopy("pgn"); blinkCopy("pgn");
}} }}
> >

14
src/utils/isLink.ts Normal file
View File

@@ -0,0 +1,14 @@
const isLink = (text: string | null) => {
if (text === null) {
return false;
}
try {
new URL(text);
return true;
} catch {
return false;
}
};
export default isLink;