Keep variant when opening editor (#9508)

* Keep variant when opening editor

* Prettier

* Handle underscores in initial editor fen

* Prettier
russelldavis-analysis-material
Benedikt Werner 2021-08-09 08:50:40 +02:00 committed by GitHub
parent fc12d3d4c7
commit 21d5085c7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 69 deletions

View File

@ -33,17 +33,14 @@ final class Editor(env: Env) extends LilaController(env) {
def load(urlFen: String) =
Open { implicit ctx =>
val fenStr = lila.common.String
val fen = lila.common.String
.decodeUriPath(urlFen)
.map(_.replace('_', ' ').trim)
.filter(_.nonEmpty)
.orElse(get("fen"))
fuccess {
val situation = readFen(fenStr)
Ok(
html.board.editor(
sit = situation,
fen = Forsyth >> situation,
fen,
positionsJson,
endgamePositionsJson
)
@ -54,20 +51,12 @@ final class Editor(env: Env) extends LilaController(env) {
def data =
Open { implicit ctx =>
fuccess {
val situation = readFen(get("fen"))
JsonOk(
html.board.bits.jsData(
sit = situation,
fen = Forsyth >> situation
)
html.board.bits.jsData()
)
}
}
private def readFen(fen: Option[String]): Situation =
fen.map(_.trim).filter(_.nonEmpty).map(FEN.clean).flatMap(Forsyth.<<<).map(_.situation) |
Situation(chess.variant.Standard)
def game(id: String) =
Open { implicit ctx =>
OptionResult(env.game.gameRepo game id) { game =>

View File

@ -32,24 +32,14 @@ object bits {
def miniSpan(fen: chess.format.FEN, color: chess.Color = chess.White, lastMove: String = "") =
mini(fen, color, lastMove)(span)
def jsData(
sit: chess.Situation,
fen: FEN
)(implicit ctx: Context) =
def jsData(fen: Option[String] = None)(implicit ctx: Context) =
Json.obj(
"fen" -> fen.value.split(" ").take(4).mkString(" "),
"baseUrl" -> s"$netBaseUrl${routes.Editor.load("")}",
"color" -> sit.color.letter.toString,
"castles" -> Json.obj(
"K" -> (sit canCastle chess.White on chess.KingSide),
"Q" -> (sit canCastle chess.White on chess.QueenSide),
"k" -> (sit canCastle chess.Black on chess.KingSide),
"q" -> (sit canCastle chess.Black on chess.QueenSide)
),
"baseUrl" -> s"$netBaseUrl${routes.Editor.load("")}",
"animation" -> Json.obj("duration" -> ctx.pref.animationMillis),
"is3d" -> ctx.pref.is3d,
"i18n" -> i18nJsObject(i18nKeyes)
)
.add("fen" -> fen)
private val i18nKeyes = List(
trans.setTheBoard,

View File

@ -1,6 +1,5 @@
package views.html.board
import chess.format.FEN
import controllers.routes
import lila.api.Context
@ -11,8 +10,7 @@ import lila.common.String.html.safeJsonValue
object editor {
def apply(
sit: chess.Situation,
fen: FEN,
fen: Option[String],
positionsJson: String,
endgamePositionsJson: String
)(implicit ctx: Context) =
@ -21,7 +19,7 @@ object editor {
moreJs = frag(
jsModule("editor"),
embedJsUnsafeLoadThen(
s"""const data=${safeJsonValue(bits.jsData(sit, fen))};data.positions=$positionsJson;
s"""const data=${safeJsonValue(bits.jsData(fen))};data.positions=$positionsJson;
data.endgamePositions=$endgamePositionsJson;LichessEditor(document.getElementById('board-editor'), data);"""
)
),

View File

@ -206,7 +206,7 @@ interface LichessEditor {
declare namespace Editor {
export interface Config {
baseUrl: string;
fen: string;
fen?: string;
options?: Editor.Options;
is3d: boolean;
animation: {

View File

@ -183,7 +183,9 @@ export function view(ctrl: AnalyseCtrl): VNode {
'a.button.button-empty',
{
attrs: {
href: d.userAnalysis ? '/editor?fen=' + ctrl.node.fen : '/' + d.game.id + '/edit?fen=' + ctrl.node.fen,
href: d.userAnalysis
? '/editor?' + new URLSearchParams({ fen: ctrl.node.fen, variant: d.game.variant.key })
: '/' + d.game.id + '/edit?fen=' + ctrl.node.fen,
'data-icon': '',
...(ctrl.embed
? {

View File

@ -213,19 +213,19 @@ export function view(ctrl: StudyChapterNewFormCtrl): VNode {
{
hook: {
insert(vnode) {
Promise.all([
lichess.loadModule('editor'),
xhr.json(xhr.url('/editor.json', { fen: ctrl.root.node.fen })),
]).then(([_, data]) => {
data.embed = true;
data.options = {
inlineCastling: true,
orientation: currentChapter.setup.orientation,
onChange: ctrl.vm.editorFen,
};
ctrl.vm.editor = window.LichessEditor!(vnode.elm as HTMLElement, data);
ctrl.vm.editorFen(ctrl.vm.editor.getFen());
});
Promise.all([lichess.loadModule('editor'), xhr.json('/editor.json')]).then(
([_, data]: [unknown, Editor.Config]) => {
data.fen = ctrl.root.node.fen;
data.embed = true;
data.options = {
inlineCastling: true,
orientation: currentChapter.setup.orientation,
onChange: ctrl.vm.editorFen,
};
ctrl.vm.editor = window.LichessEditor!(vnode.elm as HTMLElement, data);
ctrl.vm.editorFen(ctrl.vm.editor.getFen());
}
);
},
destroy: _ => {
ctrl.vm.editor = null;

View File

@ -119,7 +119,7 @@ function deletePiece(ctrl: EditorCtrl, key: Key): void {
function makeConfig(ctrl: EditorCtrl): CgConfig {
return {
fen: ctrl.cfg.fen,
fen: ctrl.initialFen,
orientation: ctrl.options.orientation || 'white',
coordinates: !ctrl.cfg.embed,
autoCastle: false,

View File

@ -0,0 +1,37 @@
import { Rules } from 'chessops/types';
export function variantToRules(variant?: string | null): Rules {
switch (variant) {
case 'threeCheck':
return '3check';
case 'kingOfTheHill':
return 'kingofthehill';
case 'racingKings':
return 'racingkings';
case 'antichess':
case 'atomic':
case 'horde':
case 'crazyhouse':
return variant;
default:
return 'chess';
}
}
export function rulesToVariant(rules: Rules): VariantKey {
switch (rules) {
case 'chess':
return 'standard';
case '3check':
return 'threeCheck';
case 'kingofthehill':
return 'kingOfTheHill';
case 'racingkings':
return 'racingKings';
case 'antichess':
case 'atomic':
case 'horde':
case 'crazyhouse':
return rules;
}
}

View File

@ -7,6 +7,7 @@ import { Setup, Material, RemainingChecks } from 'chessops/setup';
import { Castles, setupPosition } from 'chessops/variant';
import { makeFen, parseFen, parseCastlingFen, INITIAL_FEN, EMPTY_FEN, INITIAL_EPD } from 'chessops/fen';
import { defined, prop, Prop } from 'common';
import { rulesToVariant, variantToRules } from './chessops';
export default class EditorCtrl {
cfg: Editor.Config;
@ -18,6 +19,7 @@ export default class EditorCtrl {
selected: Prop<Selected>;
initialFen: string;
pockets: Material | undefined;
turn: Color;
unmovedRooks: SquareSet | undefined;
@ -62,20 +64,23 @@ export default class EditorCtrl {
});
this.castlingToggles = { K: false, Q: false, k: false, q: false };
this.rules =
!this.cfg.embed && window.history.state && window.history.state.rules ? window.history.state.rules : 'chess';
const params = new URLSearchParams(location.search);
this.rules = this.cfg.embed ? 'chess' : variantToRules(params.get('variant'));
this.initialFen = (cfg.fen || params.get('fen') || INITIAL_FEN).replace(/_/g, ' ');
this.redraw = () => {};
this.setFen(cfg.fen);
this.setFen(this.initialFen);
this.redraw = redraw;
}
onChange(): void {
const fen = this.getFen();
if (!this.cfg.embed) {
const state = { rules: this.rules };
if (fen == INITIAL_FEN) window.history.replaceState(state, '', '/editor');
else window.history.replaceState(state, '', this.makeUrl('/editor/', fen));
const params = new URLSearchParams();
if (fen !== INITIAL_FEN || this.rules !== 'chess') params.set('fen', fen);
if (this.rules !== 'chess') params.set('variant', rulesToVariant(this.rules));
const paramsString = params.toString();
window.history.replaceState(null, '', '/editor' + (paramsString ? '?' + paramsString : ''));
}
this.options.onChange?.(fen);
this.redraw();
@ -90,7 +95,7 @@ export default class EditorCtrl {
}
private getSetup(): Setup {
const boardFen = this.chessground ? this.chessground.getFen() : this.cfg.fen;
const boardFen = this.chessground?.getFen() || this.initialFen;
const board = parseFen(boardFen).unwrap(
setup => setup.board,
_ => Board.empty()
@ -136,21 +141,8 @@ export default class EditorCtrl {
}
makeAnalysisUrl(legalFen: string): string {
switch (this.rules) {
case 'chess':
return this.makeUrl('/analysis/', legalFen);
case '3check':
return this.makeUrl('/analysis/threeCheck/', legalFen);
case 'kingofthehill':
return this.makeUrl('/analysis/kingOfTheHill/', legalFen);
case 'racingkings':
return this.makeUrl('/analysis/racingKings/', legalFen);
case 'antichess':
case 'atomic':
case 'horde':
case 'crazyhouse':
return this.makeUrl(`/analysis/${this.rules}/`, legalFen);
}
const variant = this.rules === 'chess' ? '' : rulesToVariant(this.rules) + '/';
return this.makeUrl(`/analysis/${variant}`, legalFen);
}
makeUrl(baseUrl: string, fen: string): string {