study multiboard: playing filter

studyMultiboard
Thibault Duplessis 2018-11-22 17:06:27 +07:00
parent b92189d42d
commit dbec03ed04
8 changed files with 144 additions and 43 deletions

View File

@ -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
}
}

View File

@ -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)

View File

@ -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(

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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))

View File

@ -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
});
}