simple but decent multiboard view
parent
d21f602a73
commit
e2f04bd4a5
|
@ -120,7 +120,7 @@ object JsonView {
|
|||
implicit val chapterIdWrites: Writes[Chapter.Id] = stringIsoWriter(Chapter.idIso)
|
||||
implicit val chapterNameWrites: Writes[Chapter.Name] = stringIsoWriter(Chapter.nameIso)
|
||||
|
||||
private implicit val uciWrites: Writes[Uci] = Writes[Uci] { u =>
|
||||
private[study] implicit val uciWrites: Writes[Uci] = Writes[Uci] { u =>
|
||||
JsString(u.uci)
|
||||
}
|
||||
private implicit val uciCharPairWrites: Writes[UciCharPair] = Writes[UciCharPair] { u =>
|
||||
|
|
|
@ -24,6 +24,7 @@ sealed trait RootOrNode {
|
|||
def fullMoveNumber = 1 + ply / 2
|
||||
def mainline: List[Node]
|
||||
def color = chess.Color(ply % 2 == 0)
|
||||
def moveOption: Option[Uci.WithSan]
|
||||
}
|
||||
|
||||
case class Node(
|
||||
|
@ -97,6 +98,8 @@ case class Node(
|
|||
forceVariation = n.forceVariation || forceVariation
|
||||
)
|
||||
|
||||
def moveOption = move.some
|
||||
|
||||
override def toString = s"$ply.${move.san} ${children.nodes}"
|
||||
}
|
||||
|
||||
|
@ -295,6 +298,8 @@ object Node {
|
|||
def mainlinePath = Path(mainline.map(_.id))
|
||||
|
||||
def lastMainlineNode: RootOrNode = children.lastMainlineNode getOrElse this
|
||||
|
||||
def moveOption = none
|
||||
}
|
||||
|
||||
object Root {
|
||||
|
|
|
@ -4,8 +4,8 @@ import play.api.libs.json._
|
|||
import reactivemongo.bson._
|
||||
|
||||
import chess.Color
|
||||
import chess.format.FEN
|
||||
import chess.format.pgn.{ Tag, Tags }
|
||||
import chess.format.{ FEN, Uci }
|
||||
|
||||
import BSONHandlers._
|
||||
import JsonView._
|
||||
|
@ -33,15 +33,19 @@ final class StudyMultiBoard(
|
|||
)
|
||||
|
||||
private implicit val previewBSONReader = new BSONDocumentReader[ChapterPreview] {
|
||||
def read(doc: BSONDocument) = ChapterPreview(
|
||||
id = doc.getAs[Chapter.Id]("_id") err "Preview missing id",
|
||||
name = doc.getAs[Chapter.Name]("name") err "Preview missing name",
|
||||
players = doc.getAs[Tags]("tags") flatMap ChapterPreview.players,
|
||||
orientation = doc.getAs[Bdoc]("setup") flatMap { setup =>
|
||||
setup.getAs[Color]("orientation")
|
||||
} getOrElse Color.White,
|
||||
fen = doc.getAs[Node.Root]("root").err("Preview missing root").lastMainlineNode.fen
|
||||
)
|
||||
def read(doc: BSONDocument) = {
|
||||
val node = doc.getAs[Node.Root]("root").err("Preview missing root").lastMainlineNode
|
||||
ChapterPreview(
|
||||
id = doc.getAs[Chapter.Id]("_id") err "Preview missing id",
|
||||
name = doc.getAs[Chapter.Name]("name") err "Preview missing name",
|
||||
players = doc.getAs[Tags]("tags") flatMap ChapterPreview.players,
|
||||
orientation = doc.getAs[Bdoc]("setup") flatMap { setup =>
|
||||
setup.getAs[Color]("orientation")
|
||||
} getOrElse Color.White,
|
||||
fen = node.fen,
|
||||
lastMove = node.moveOption.map(_.uci)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private implicit val previewPlayerWriter: Writes[ChapterPreview.Player] = Writes[ChapterPreview.Player] { p =>
|
||||
|
@ -67,7 +71,8 @@ object StudyMultiBoard {
|
|||
name: Chapter.Name,
|
||||
players: Option[ChapterPreview.Players],
|
||||
orientation: Color,
|
||||
fen: FEN
|
||||
fen: FEN,
|
||||
lastMove: Option[Uci]
|
||||
)
|
||||
|
||||
object ChapterPreview {
|
||||
|
|
|
@ -575,25 +575,32 @@ body.base .study_buttons .fbt.active {
|
|||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.multi_board {
|
||||
padding: 4px 0 0 4px;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
#now_playing {
|
||||
padding-left: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.multi_board .spinner {
|
||||
#now_playing .spinner {
|
||||
margin: 50px auto;
|
||||
}
|
||||
.multi_board > a {
|
||||
padding: 4px;
|
||||
#now_playing > a {
|
||||
flex: 0 0 160px;
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
transition: background 0.13s;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.multi_board > a:hover {
|
||||
#now_playing > a:nth-child(even) {
|
||||
background: rgba(128, 128, 128, 0.2);
|
||||
}
|
||||
#now_playing > a:hover {
|
||||
background: rgba(191, 231, 255, 0.7);
|
||||
}
|
||||
.multi_board .cg-board-wrap {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
#now_playing a.active {
|
||||
background: rgba(99,155,36,0.8);
|
||||
color: #fff;
|
||||
}
|
||||
#now_playing .player {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
div.advice_summary {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import { Chessground } from 'chessground';
|
||||
import { ChapterPreview } from './interfaces';
|
||||
import { opposite } from 'chessground/util';
|
||||
import { StudyCtrl, ChapterPreview, ChapterPreviewPlayer } from './interfaces';
|
||||
import { multiBoard as xhrLoad } from './studyXhr';
|
||||
import { spinner } from '../util';
|
||||
import { bind, spinner } from '../util';
|
||||
|
||||
interface MultiBoardData {
|
||||
previews: [ChapterPreview]
|
||||
|
@ -16,30 +17,45 @@ export class MultiBoardCtrl {
|
|||
constructor(readonly studyId: string, readonly redraw: () => void) {
|
||||
}
|
||||
|
||||
getData() {
|
||||
if (!this.data) xhrLoad(this.studyId).then(d => {
|
||||
reload() {
|
||||
xhrLoad(this.studyId).then(d => {
|
||||
this.data = d;
|
||||
this.redraw();
|
||||
});
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
export function view(ctrl: MultiBoardCtrl, study: StudyCtrl): VNode | undefined {
|
||||
|
||||
return h('div#now_playing', {
|
||||
hook: {
|
||||
insert() { ctrl.reload(); }
|
||||
}
|
||||
}, ctrl.data ? ctrl.data.previews.map(makePreview(study)) : [spinner()]);
|
||||
}
|
||||
|
||||
function makePreview(study: StudyCtrl) {
|
||||
return (preview: ChapterPreview) => {
|
||||
const contents = preview.players ? [
|
||||
makePlayer(preview.players[opposite(preview.orientation)]),
|
||||
makeCg(preview),
|
||||
makePlayer(preview.players[preview.orientation])
|
||||
] : [makeCg(preview)];
|
||||
return h('a.mini_board', {
|
||||
class: { active: study.vm.chapterId == preview.id },
|
||||
hook: bind('mousedown', _ => study.setChapter(preview.id))
|
||||
}, contents);
|
||||
};
|
||||
}
|
||||
|
||||
export function view(ctrl: MultiBoardCtrl): VNode | undefined {
|
||||
|
||||
const data = ctrl.getData();
|
||||
if (!data) return h('div.multi_board', spinner());
|
||||
|
||||
return h('div.multi_board',
|
||||
data.previews.map(preview => {
|
||||
return h('a.mini_board', [
|
||||
makeCg(preview)
|
||||
])
|
||||
})
|
||||
);
|
||||
function makePlayer(player: ChapterPreviewPlayer): VNode {
|
||||
return h('div.player', [
|
||||
player.title ? `${player.title} ${player.name}` : player.name,
|
||||
player.rating && h('span', '' + player.rating)
|
||||
]);
|
||||
}
|
||||
|
||||
function makeCg(preview: ChapterPreview) {
|
||||
function makeCg(preview: ChapterPreview): VNode {
|
||||
return h('div.cg-board-wrap', {
|
||||
hook: {
|
||||
insert(vnode) {
|
||||
|
@ -51,7 +67,7 @@ function makeCg(preview: ChapterPreview) {
|
|||
viewOnly: true,
|
||||
orientation: preview.orientation,
|
||||
fen: preview.fen,
|
||||
lastMove: lm && ([lm[0] + lm[1], lm[2] + lm[3]] as Key[])
|
||||
lastMove: lm ? ([lm[0] + lm[1], lm[2] + lm[3]] as Key[]) : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -537,11 +537,11 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
|
|||
vm.justSetChapterId = id;
|
||||
redraw();
|
||||
},
|
||||
toggleSticky: function() {
|
||||
toggleSticky() {
|
||||
vm.mode.sticky = !vm.mode.sticky && data.features.sticky;
|
||||
xhrReload();
|
||||
},
|
||||
toggleWrite: function() {
|
||||
toggleWrite() {
|
||||
vm.mode.write = !vm.mode.write && members.canContribute();
|
||||
xhrReload();
|
||||
},
|
||||
|
|
|
@ -230,7 +230,7 @@ export function underboard(ctrl: AnalyseCtrl): MaybeVNodes {
|
|||
panel = studyShareView(study.share);
|
||||
break;
|
||||
case 'multiBoard':
|
||||
panel = multiBoardView(study.multiBoard);
|
||||
panel = multiBoardView(study.multiBoard, study);
|
||||
break;
|
||||
}
|
||||
return [
|
||||
|
|
Loading…
Reference in New Issue