study multiboard: playing filter
parent
b92189d42d
commit
dbec03ed04
|
@ -389,10 +389,10 @@ object Study extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def multiBoard(id: String) = Open { implicit ctx =>
|
||||
def multiBoard(id: String, page: Int) = Open { implicit ctx =>
|
||||
OptionFuResult(env.api byId id) { study =>
|
||||
CanViewResult(study) {
|
||||
env.multiBoard.json(study) map { json =>
|
||||
env.multiBoard.json(study, page, getBool("playing")) map { json =>
|
||||
Ok(json) as JSON
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ GET /study/$id<\w{8}>/$chapterId<\w{8}>/meta controllers.Study.chapterMeta(i
|
|||
GET /study/embed/$id<\w{8}>/$chapterId<\w{8}> controllers.Study.embed(id: String, chapterId: String)
|
||||
POST /study/$id<\w{8}>/clear-chat controllers.Study.clearChat(id: String)
|
||||
POST /study/$id<\w{8}>/import-pgn controllers.Study.importPgn(id: String)
|
||||
GET /study/$id<\w{8}>/multi-board controllers.Study.multiBoard(id: String)
|
||||
GET /study/$id<\w{8}>/multi-board controllers.Study.multiBoard(id: String, page: Int ?= 1)
|
||||
|
||||
# Relay
|
||||
GET /broadcast controllers.Relay.index(page: Int ?= 1)
|
||||
|
|
|
@ -165,7 +165,8 @@ final class Env(
|
|||
)
|
||||
|
||||
lazy val multiBoard = new StudyMultiBoard(
|
||||
chapterColl = chapterColl
|
||||
chapterColl = chapterColl,
|
||||
maxPerPage = lila.common.MaxPerPage(9)
|
||||
)
|
||||
|
||||
lazy val pgnDump = new PgnDump(
|
||||
|
|
|
@ -10,21 +10,34 @@ import chess.format.{ FEN, Uci }
|
|||
import BSONHandlers._
|
||||
import JsonView._
|
||||
import lila.db.dsl._
|
||||
import lila.common.MaxPerPage
|
||||
import lila.common.paginator.{ Paginator, PaginatorJson }
|
||||
import lila.db.paginator.Adapter
|
||||
|
||||
final class StudyMultiBoard(
|
||||
chapterColl: Coll
|
||||
chapterColl: Coll,
|
||||
maxPerPage: MaxPerPage
|
||||
) {
|
||||
|
||||
val max = 9
|
||||
|
||||
import StudyMultiBoard._
|
||||
|
||||
def json(study: Study): Fu[JsObject] = get(study) map { previews =>
|
||||
Json.obj(
|
||||
"previews" -> previews
|
||||
)
|
||||
def json(study: Study, page: Int, playing: Boolean): Fu[JsObject] = get(study, page, playing) map { p =>
|
||||
PaginatorJson(p)
|
||||
}
|
||||
|
||||
private def get(study: Study, page: Int, playing: Boolean): Fu[Paginator[ChapterPreview]] = Paginator(
|
||||
adapter = new Adapter[ChapterPreview](
|
||||
collection = chapterColl,
|
||||
selector = $doc("studyId" -> study.id) ++ playing.??(playingSelector),
|
||||
projection = projection,
|
||||
sort = $sort asc "order"
|
||||
),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
)
|
||||
|
||||
private val playingSelector = $doc("tags" -> "Result:*")
|
||||
|
||||
private val projection = $doc(
|
||||
"name" -> true,
|
||||
"tags" -> true,
|
||||
|
@ -61,11 +74,6 @@ final class StudyMultiBoard(
|
|||
}
|
||||
|
||||
private implicit val previewWriter: Writes[ChapterPreview] = Json.writes[ChapterPreview]
|
||||
|
||||
private def get(study: Study): Fu[List[ChapterPreview]] = chapterColl
|
||||
.find($doc("studyId" -> study.id), projection)
|
||||
.sort($sort asc "order")
|
||||
.list[ChapterPreview](max)
|
||||
}
|
||||
|
||||
object StudyMultiBoard {
|
||||
|
|
|
@ -575,6 +575,21 @@ body.base .study_buttons .fbt.active {
|
|||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.multi_board.loading {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.multi_board .top {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.multi_board .playing {
|
||||
cursor: pointer;
|
||||
}
|
||||
.multi_board .playing input {
|
||||
vertical-align: middle;
|
||||
margin-right: 3px;
|
||||
}
|
||||
#now_playing {
|
||||
padding: 0 0 0 1px;
|
||||
}
|
||||
|
@ -610,6 +625,9 @@ body.base .study_buttons .fbt.active {
|
|||
justify-content: center;
|
||||
height: 3em;
|
||||
}
|
||||
#now_playing .pager .page {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
div.advice_summary {
|
||||
margin-top: 42px;
|
||||
|
|
|
@ -112,16 +112,6 @@ interface Window {
|
|||
[key: string]: any; // TODO
|
||||
}
|
||||
|
||||
interface Paginator<T> {
|
||||
currentPage: number
|
||||
maxPerPage: number
|
||||
currentPageResults: Array<T>
|
||||
nbResults: number
|
||||
previousPage: number
|
||||
nextPage: number
|
||||
nbPages: number
|
||||
}
|
||||
|
||||
interface LightUser {
|
||||
id: string
|
||||
name: string
|
||||
|
@ -149,14 +139,14 @@ declare var Atomics: any | undefined;
|
|||
|
||||
declare type VariantKey = 'standard' | 'chess960' | 'antichess' | 'fromPosition' | 'kingOfTheHill' | 'threeCheck' | 'atomic' | 'horde' | 'racingKings' | 'crazyhouse'
|
||||
|
||||
declare type Speed = 'bullet' | 'blitz' | 'classical' | 'correspondence' | 'unlimited'
|
||||
declare type Speed = 'bullet' | 'blitz' | 'classical' | 'correspondence' | 'unlimited'
|
||||
|
||||
declare type Perf = 'bullet' | 'blitz' | 'classical' | 'correspondence' | 'chess960' | 'antichess' | 'fromPosition' | 'kingOfTheHill' | 'threeCheck' | 'atomic' | 'horde' | 'racingKings' | 'crazyhouse'
|
||||
declare type Perf = 'bullet' | 'blitz' | 'classical' | 'correspondence' | 'chess960' | 'antichess' | 'fromPosition' | 'kingOfTheHill' | 'threeCheck' | 'atomic' | 'horde' | 'racingKings' | 'crazyhouse'
|
||||
|
||||
declare type Color = 'white' | 'black';
|
||||
declare type Color = 'white' | 'black';
|
||||
|
||||
declare type Key = 'a0' | 'a1' | 'b1' | 'c1' | 'd1' | 'e1' | 'f1' | 'g1' | 'h1' | 'a2' | 'b2' | 'c2' | 'd2' | 'e2' | 'f2' | 'g2' | 'h2' | 'a3' | 'b3' | 'c3' | 'd3' | 'e3' | 'f3' | 'g3' | 'h3' | 'a4' | 'b4' | 'c4' | 'd4' | 'e4' | 'f4' | 'g4' | 'h4' | 'a5' | 'b5' | 'c5' | 'd5' | 'e5' | 'f5' | 'g5' | 'h5' | 'a6' | 'b6' | 'c6' | 'd6' | 'e6' | 'f6' | 'g6' | 'h6' | 'a7' | 'b7' | 'c7' | 'd7' | 'e7' | 'f7' | 'g7' | 'h7' | 'a8' | 'b8' | 'c8' | 'd8' | 'e8' | 'f8' | 'g8' | 'h8';
|
||||
declare type Uci = string;
|
||||
declare type Key = 'a0' | 'a1' | 'b1' | 'c1' | 'd1' | 'e1' | 'f1' | 'g1' | 'h1' | 'a2' | 'b2' | 'c2' | 'd2' | 'e2' | 'f2' | 'g2' | 'h2' | 'a3' | 'b3' | 'c3' | 'd3' | 'e3' | 'f3' | 'g3' | 'h3' | 'a4' | 'b4' | 'c4' | 'd4' | 'e4' | 'f4' | 'g4' | 'h4' | 'a5' | 'b5' | 'c5' | 'd5' | 'e5' | 'f5' | 'g5' | 'h5' | 'a6' | 'b6' | 'c6' | 'd6' | 'e6' | 'f6' | 'g6' | 'h6' | 'a7' | 'b7' | 'c7' | 'd7' | 'e7' | 'f7' | 'g7' | 'h7' | 'a8' | 'b8' | 'c8' | 'd8' | 'e8' | 'f8' | 'g8' | 'h8';
|
||||
declare type Uci = string;
|
||||
declare type San = string;
|
||||
declare type Fen = string;
|
||||
declare type Ply = number;
|
||||
|
@ -168,6 +158,16 @@ interface Variant {
|
|||
title?: string
|
||||
}
|
||||
|
||||
interface Paginator<A> {
|
||||
currentPage: number
|
||||
maxPerPage: number
|
||||
currentPageResults: [A]
|
||||
nbResults: number
|
||||
previousPage?: number
|
||||
nextPage?: number
|
||||
nbPages: number
|
||||
}
|
||||
|
||||
declare namespace Tree {
|
||||
export type Path = string;
|
||||
|
||||
|
|
|
@ -3,35 +3,109 @@ import { VNode } from 'snabbdom/vnode'
|
|||
import { Chessground } from 'chessground';
|
||||
import { opposite } from 'chessground/util';
|
||||
import { StudyCtrl, ChapterPreview, ChapterPreviewPlayer } from './interfaces';
|
||||
import { MaybeVNodes } from '../interfaces';
|
||||
import { multiBoard as xhrLoad } from './studyXhr';
|
||||
import { bind, spinner } from '../util';
|
||||
|
||||
interface MultiBoardData {
|
||||
previews: [ChapterPreview]
|
||||
}
|
||||
|
||||
export class MultiBoardCtrl {
|
||||
|
||||
data?: MultiBoardData;
|
||||
loading: boolean = true;
|
||||
page: number = 1;
|
||||
pager?: Paginator<ChapterPreview>;
|
||||
playing: boolean = false;
|
||||
|
||||
constructor(readonly studyId: string, readonly redraw: () => void) {
|
||||
}
|
||||
|
||||
reload() {
|
||||
xhrLoad(this.studyId).then(d => {
|
||||
this.data = d;
|
||||
if (!this.loading) {
|
||||
this.loading = true;
|
||||
this.redraw();
|
||||
}
|
||||
xhrLoad(this.studyId, this.page, this.playing).then(p => {
|
||||
this.pager = p;
|
||||
if (p.nbPages < this.page) {
|
||||
if (!p.nbPages) this.page = 1;
|
||||
else this.setPage(p.nbPages);
|
||||
}
|
||||
this.loading = false;
|
||||
this.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
setPage = (page: number) => {
|
||||
if (this.page != page) {
|
||||
this.page = page;
|
||||
this.reload();
|
||||
}
|
||||
};
|
||||
nextPage = () => this.setPage(this.page + 1);
|
||||
prevPage = () => this.setPage(this.page - 1);
|
||||
lastPage = () => { if (this.pager) this.setPage(this.pager.nbPages); };
|
||||
|
||||
setPlaying = (v: boolean) => {
|
||||
this.playing = v;
|
||||
this.reload();
|
||||
};
|
||||
}
|
||||
|
||||
export function view(ctrl: MultiBoardCtrl, study: StudyCtrl): VNode | undefined {
|
||||
|
||||
return h('div#now_playing', {
|
||||
return h('div.multi_board', {
|
||||
class: { loading: ctrl.loading },
|
||||
hook: {
|
||||
insert() { ctrl.reload(); }
|
||||
}
|
||||
}, ctrl.data ? ctrl.data.previews.map(makePreview(study)) : [spinner()]);
|
||||
}, ctrl.pager ? renderPager(ctrl.pager, study) : [spinner()]);
|
||||
}
|
||||
|
||||
function renderPager(pager: Paginator<ChapterPreview>, study: StudyCtrl): MaybeVNodes {
|
||||
const ctrl = study.multiBoard;
|
||||
return [
|
||||
h('div.top', [
|
||||
renderPagerNav(pager, ctrl),
|
||||
renderPlayingToggle(ctrl)
|
||||
]),
|
||||
h('div#now_playing', pager.currentPageResults.map(makePreview(study)))
|
||||
];
|
||||
}
|
||||
|
||||
function renderPlayingToggle(ctrl: MultiBoardCtrl): VNode {
|
||||
return h('label.playing', {
|
||||
attrs: { title: 'Only ongoing games' }
|
||||
}, [
|
||||
h('input', {
|
||||
attrs: { type: 'checkbox' },
|
||||
hook: bind('change', e => {
|
||||
ctrl.setPlaying((e.target as HTMLInputElement).checked);
|
||||
})
|
||||
}),
|
||||
'Playing'
|
||||
]);
|
||||
}
|
||||
|
||||
function renderPagerNav(pager: Paginator<ChapterPreview>, ctrl: MultiBoardCtrl): VNode {
|
||||
const page = ctrl.page,
|
||||
from = Math.min(pager.nbResults, (page - 1) * pager.maxPerPage + 1),
|
||||
to = Math.min(pager.nbResults, page * pager.maxPerPage);
|
||||
return h('div.pager', [
|
||||
pagerButton('First', 'W', () => ctrl.setPage(1), page > 1, ctrl),
|
||||
pagerButton('Prev', 'Y', ctrl.prevPage, page > 1, ctrl),
|
||||
h('span.page', `${from}-${to} / ${pager.nbResults}`),
|
||||
pagerButton('Next', 'X', ctrl.nextPage, page < pager.nbPages, ctrl),
|
||||
pagerButton('Last', 'V', ctrl.lastPage, page < pager.nbPages, ctrl)
|
||||
]);
|
||||
}
|
||||
|
||||
function pagerButton(text: string, icon: string, click: () => void, enable: boolean, ctrl: MultiBoardCtrl): VNode {
|
||||
return h('button.fbt.is', {
|
||||
attrs: {
|
||||
'data-icon': icon,
|
||||
disabled: !enable,
|
||||
title: text
|
||||
},
|
||||
hook: bind('mousedown', click, ctrl.redraw)
|
||||
});
|
||||
}
|
||||
|
||||
function makePreview(study: StudyCtrl) {
|
||||
|
@ -44,7 +118,7 @@ function makePreview(study: StudyCtrl) {
|
|||
h('div.name', preview.name),
|
||||
makeCg(preview)
|
||||
];
|
||||
return h('a.mini_board', {
|
||||
return h('a.mini_board.' + preview.id, {
|
||||
attrs: { title: preview.name },
|
||||
class: { active: study.vm.chapterId == preview.id },
|
||||
hook: bind('mousedown', _ => study.setChapter(preview.id))
|
||||
|
|
|
@ -51,9 +51,9 @@ export function importPgn(studyId: string, data: any) {
|
|||
});
|
||||
}
|
||||
|
||||
export function multiBoard(studyId: string) {
|
||||
export function multiBoard(studyId: string, page: number, playing: boolean) {
|
||||
return $.ajax({
|
||||
url: `/study/${studyId}/multi-board`,
|
||||
url: `/study/${studyId}/multi-board?page=${page}&playing=${playing}`,
|
||||
headers
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue