Keep variant when opening editor (#9508)
* Keep variant when opening editor * Prettier * Handle underscores in initial editor fen * Prettierrusselldavis-analysis-material
parent
fc12d3d4c7
commit
21d5085c7d
|
@ -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 =>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);"""
|
||||
)
|
||||
),
|
||||
|
|
|
@ -206,7 +206,7 @@ interface LichessEditor {
|
|||
declare namespace Editor {
|
||||
export interface Config {
|
||||
baseUrl: string;
|
||||
fen: string;
|
||||
fen?: string;
|
||||
options?: Editor.Options;
|
||||
is3d: boolean;
|
||||
animation: {
|
||||
|
|
|
@ -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
|
||||
? {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue