more ui rewrite

refactor-site-js
Thibault Duplessis 2020-09-04 10:45:09 +02:00
parent 327e16a177
commit 9176439660
16 changed files with 84 additions and 92 deletions

View File

@ -2,7 +2,6 @@ package views.html.base
import controllers.routes
import play.api.i18n.Lang
import play.api.libs.json.Json
import lila.api.{ AnnounceStore, Context }
import lila.app.templating.Environment._

View File

@ -23,7 +23,7 @@ object home {
moreJs = frag(
jsModule("lobby", defer = true),
embedJsUnsafe(
s"""lichess=window.lichess||{};customWS=true;lichess_lobby=${safeJsonValue(
s"""window.lichess.load.then(()=>window.LichessLobby(${safeJsonValue(
Json.obj(
"data" -> data,
"playban" -> playban.map { pb =>
@ -34,7 +34,7 @@ object home {
},
"i18n" -> i18nJsObject(i18nKeys)
)
)}"""
)}))"""
)
),
moreCss = cssTag("lobby"),

View File

@ -15,7 +15,7 @@ object msg {
moreJs = frag(
jsModule("msg"),
embedJsUnsafe(
s"""$$(() =>LichessMsg(document.querySelector('.msg-app'), ${safeJsonValue(
s"""window.lichess.load.then(()=>LichessMsg(${safeJsonValue(
Json.obj(
"data" -> json,
"i18n" -> jsI18n

View File

@ -29,7 +29,7 @@ interface Lichess {
unload: {
expected: boolean;
};
redirect(o: string | { url: string, cookie: Cookie }): void;
redirect(o: RedirectTo): void;
reload(): void;
escapeHtml(str: string): string;
announce(d: LichessAnnouncement): void;
@ -55,7 +55,7 @@ interface Lichess {
// socket.js
StrongSocket: {
(url: string, version: number | false, cfg: any): any;
new(url: string, version: number | false, cfg: any): any;
firstConnect: Promise<(tpe: string, data: any) => void>
}
@ -88,6 +88,8 @@ interface Lichess {
}
}
type RedirectTo = string | { url: string, cookie: Cookie };
interface SoundBoxI {
loadOggOrMp3(name: string, path: string): void;
play(name: string): void;

View File

@ -3,7 +3,7 @@ import { rollupProject } from '@build/rollupProject';
export default rollupProject({
main: {
name: 'LichessLobby',
input: 'src/main.ts',
input: 'src/boot.ts',
output: 'lobby',
},
});

View File

@ -1,5 +1,10 @@
export default function boot(cfg, element) {
cfg.pools = [ // mirrors modules/pool/src/main/PoolList.scala
import { LobbyOpts } from './interfaces';
import main from './main';
export default function LichessLobby(opts: LobbyOpts) {
opts.element = document.querySelector('.lobby__app') as HTMLElement;
opts.pools = [ // mirrors modules/pool/src/main/PoolList.scala
{ id: "1+0", lim: 1, inc: 0, perf: "Bullet" },
{ id: "2+1", lim: 2, inc: 1, perf: "Bullet" },
{ id: "3+0", lim: 3, inc: 0, perf: "Blitz" },
@ -12,9 +17,8 @@ export default function boot(cfg, element) {
{ id: "30+0", lim: 30, inc: 0, perf: "Classical" },
{ id: "30+20", lim: 30, inc: 20, perf: "Classical" }
];
const li = window.lichess;
let lobby;
const nbRoundSpread = spreadNumber(
const li = window.lichess,
nbRoundSpread = spreadNumber(
document.querySelector('#nb_games_in_play > strong') as HTMLElement,
8),
nbUserSpread = spreadNumber(
@ -24,12 +28,11 @@ export default function boot(cfg, element) {
const match = RegExp('[?&]' + name + '=([^&]*)').exec(location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
};
li.socket = li.StrongSocket(
let lobby: any;
li.socket = new li.StrongSocket(
'/lobby/socket/v5',
false, {
receive(t: string, d: any) {
lobby.socketReceive(t, d);
},
receive(t: string, d: any) { lobby.socketReceive(t, d) },
events: {
n(_: string, msg: any) {
nbUserSpread(msg.d);
@ -44,16 +47,16 @@ export default function boot(cfg, element) {
}
});
},
featured(o) {
featured(o: { html: string}) {
$('.lobby__tv').html(o.html);
li.pubsub.emit('content_loaded');
},
redirect(e) {
redirect(e: RedirectTo) {
lobby.leavePool();
lobby.setRedirecting();
li.redirect(e);
},
fen(e) {
fen(e: any) {
lobby.gameActivity(e.id);
}
}
@ -66,16 +69,15 @@ export default function boot(cfg, element) {
history.replaceState(null, '', '/');
});
cfg.blindMode = $('body').hasClass('blind-mode');
cfg.trans = li.trans(cfg.i18n);
cfg.socketSend = li.socket.send;
cfg.element = element;
lobby = window.LichessLobby.start(cfg);
opts.blindMode = $('body').hasClass('blind-mode');
opts.trans = li.trans(opts.i18n);
opts.socketSend = li.socket.send;
lobby = main(opts);
const $startButtons = $('.lobby__start'),
clickEvent = cfg.blindMode ? 'click' : 'mousedown';
clickEvent = opts.blindMode ? 'click' : 'mousedown';
$startButtons.find('a:not(.disabled)').on(clickEvent, function() {
$startButtons.find('a:not(.disabled)').on(clickEvent, function(this: HTMLElement) {
$(this).addClass('active').siblings().removeClass('active');
li.loadCssPath('lobby.setup');
lobby.leavePool();
@ -100,7 +102,7 @@ export default function boot(cfg, element) {
if (['#ai', '#friend', '#hook'].includes(location.hash)) {
$startButtons
.find('.config_' + location.hash.replace('#', ''))
.each(function() {
.each(function(this: HTMLElement) {
$(this).attr("href", $(this).attr("href") + location.search);
}).trigger(clickEvent);
@ -113,7 +115,7 @@ export default function boot(cfg, element) {
history.replaceState(null, '', '/');
}
};
}
function spreadNumber(el: HTMLElement, nbSteps: number) {
let previous: number, displayed: string;

View File

@ -44,7 +44,7 @@ export default class Filter {
filter = (hooks: Hook[]): Filtered => {
if (!this.data) return { visible: hooks, hidden: 0 };
const f = this.data.filter,
ratingRange = f.ratingRange && f.ratingRange.split('-').map(r => parseInt(r, 10)),
ratingRange = f.ratingRange && f.ratingRange.split('-').map((r: string) => parseInt(r, 10)),
seen: string[] = [],
visible: Hook[] = [];
let variant: string, hidden = 0;

View File

@ -10,9 +10,8 @@ export const patch = init([klass, attributes]);
import makeCtrl from './ctrl';
import view from './view/main';
import boot from './boot';
export function start(opts: LobbyOpts) {
export default function main(opts: LobbyOpts) {
let vnode: VNode, ctrl: LobbyController;
@ -44,7 +43,3 @@ export function start(opts: LobbyOpts) {
// that's for the rest of lichess to access chessground
// without having to include it a second time
window.Chessground = Chessground;
window.lichess.load.then(() =>
boot(window['lichess_lobby'], document.querySelector('.lobby__app'))
);

View File

@ -1,9 +1,6 @@
{
"extends": "../tsconfig.base.json",
"include": ["src/**/*.ts"],
"compilerOptions": {
"noImplicitAny": false,
"noImplicitThis": false,
"lib": ["DOM", "ES2016", "DOM.iterable"]
"noImplicitAny": false
}
}

View File

@ -9,11 +9,11 @@ import { MsgOpts } from './interfaces'
import { upgradeData } from './network'
import MsgCtrl from './ctrl';
export default function LichessMsg(element: HTMLElement, opts: MsgOpts) {
export default function LichessMsg(opts: MsgOpts) {
const patch = init([klass, attributes]);
const appHeight = () => document.body.style.setProperty('--app-height', `${window.innerHeight}px`);
const element = document.querySelector('.msg-app') as HTMLElement,
patch = init([klass, attributes]),
appHeight = () => document.body.style.setProperty('--app-height', `${window.innerHeight}px`);
window.addEventListener('resize', appHeight);
appHeight();

View File

@ -10,7 +10,7 @@ export default function(opts: RoundOpts): void {
data: RoundData = opts.data;
let round: RoundApi;
if (data.tournament) $('body').data('tournament-id', data.tournament.id);
li.socket = li.StrongSocket(
li.socket = new li.StrongSocket(
data.url.socket,
data.player.version, {
params: { userTv: data.userTv && data.userTv.id },

View File

@ -18,7 +18,7 @@ function startTournament(cfg) {
const element = document.querySelector('main.tour') as HTMLElement;
$('body').data('tournament-id', cfg.data.id);
let tournament;
window.lichess.socket = StrongSocket(
window.lichess.socket = new StrongSocket(
`/tournament/${cfg.data.id}/socket/v5`, cfg.data.socketVersion, {
receive: (t, d) => tournament.socketReceive(t, d)
});
@ -28,7 +28,7 @@ function startTournament(cfg) {
}
function startTeam(cfg) {
window.lichess.socket = StrongSocket('/team/' + cfg.id, cfg.socketVersion);
window.lichess.socket = new StrongSocket('/team/' + cfg.id, cfg.socketVersion);
cfg.chat && makeChat(cfg.chat);
$('#team-subscribe').on('change', function(this: HTMLInputElement) {
const v = this.checked;
@ -42,7 +42,7 @@ function startUserAnalysis(cfg) {
var analyse;
cfg.initialPly = 'url';
cfg.trans = trans(cfg.i18n);
window.lichess.socket = StrongSocket('/analysis/socket/v5', false, {
window.lichess.socket = new StrongSocket('/analysis/socket/v5', false, {
receive: (t, d) => analyse.socketReceive(t, d)
});
cfg.socketSend = window.lichess.socket.send;
@ -53,7 +53,7 @@ function startUserAnalysis(cfg) {
function startStudy(cfg) {
var analyse;
cfg.initialPly = 'url';
window.lichess.socket = StrongSocket(cfg.socketUrl, cfg.socketVersion, {
window.lichess.socket = new StrongSocket(cfg.socketUrl, cfg.socketVersion, {
receive: (t, d) => analyse.socketReceive(t, d)
});
cfg.socketSend = window.lichess.socket.send;
@ -64,7 +64,7 @@ function startStudy(cfg) {
function startPractice(cfg) {
var analyse;
cfg.trans = trans(cfg.i18n);
window.lichess.socket = StrongSocket('/analysis/socket/v5', false, {
window.lichess.socket = new StrongSocket('/analysis/socket/v5', false, {
receive: (t, d) => analyse.socketReceive(t, d)
});
cfg.socketSend = window.lichess.socket.send;
@ -74,7 +74,7 @@ function startPractice(cfg) {
function startRelay(cfg) {
var analyse;
cfg.initialPly = 'url';
window.lichess.socket = StrongSocket(cfg.socketUrl, cfg.socketVersion, {
window.lichess.socket = new StrongSocket(cfg.socketUrl, cfg.socketVersion, {
receive: (t, d) => analyse.socketReceive(t, d)
});
cfg.socketSend = window.lichess.socket.send;

View File

@ -42,37 +42,6 @@ interface Settings {
options: Options;
}
class Ackable {
currentId = 1; // increment with each ackable message sent
messages: MsgAck[] = [];
constructor(readonly send: Send) {
setInterval(this.resend, 1200);
}
resend = () => {
const resendCutoff = performance.now() - 2500;
this.messages.forEach(m => {
if (m.at < resendCutoff) this.send(m.t, m.d);
});
}
register = (t: Tpe, d: Payload) => {
d.a = this.currentId++;
this.messages.push({
t: t,
d: d,
at: performance.now()
});
}
onServerAck = (id: number) => {
this.messages = this.messages.filter(m => m.d.a !== id);
}
}
// versioned events, acks, retries, resync
export default class StrongSocket {
@ -110,10 +79,10 @@ export default class StrongSocket {
StrongSocket.resolveFirstConnect = r;
});
constructor(readonly url: string, initialVersion: number | false, settings?: any) {
constructor(readonly url: string, version: number | false, settings?: any) {
this.settings = $.extend(true, {}, StrongSocket.defaults, settings);
this.options = this.settings.options;
this.version = initialVersion;
this.version = version;
this.pubsub.on('socket.send', this.send);
window.addEventListener('unload', this.destroy);
this.connect();
@ -313,3 +282,33 @@ export default class StrongSocket {
pingInterval = () => this.computePingDelay() + this.averageLag;
getVersion = () => this.version;
}
class Ackable {
currentId = 1; // increment with each ackable message sent
messages: MsgAck[] = [];
constructor(readonly send: Send) {
setInterval(this.resend, 1200);
}
resend = () => {
const resendCutoff = performance.now() - 2500;
this.messages.forEach(m => {
if (m.at < resendCutoff) this.send(m.t, m.d);
});
}
register = (t: Tpe, d: Payload) => {
d.a = this.currentId++;
this.messages.push({
t: t,
d: d,
at: performance.now()
});
}
onServerAck = (id: number) => {
this.messages = this.messages.filter(m => m.d.a !== id);
}
}

View File

@ -24,7 +24,7 @@ class SoundBox {
getVolume = () => {
// garbage has been stored here by accident (e972d5612d)
const v = parseFloat(this.volume.get());
const v = parseFloat(this.volume.get() || '');
return v >= 0 ? v : 0.7;
}
}

View File

@ -111,7 +111,7 @@ window.lichess.load.then(() => {
if (!window.customWS) setTimeout(() => {
if (!window.lichess.socket)
window.lichess.socket = StrongSocket("/socket/v5", false);
window.lichess.socket = new StrongSocket("/socket/v5", false);
}, 300);
topBar();
@ -146,12 +146,11 @@ window.lichess.load.then(() => {
});
// still bind esc even in form fields
window.Mousetrap.prototype.stopCallback = function(_, el, combo) {
return combo != 'esc' && (
window.Mousetrap.prototype.stopCallback = (_: any, el: HTMLElement, combo: string) =>
combo != 'esc' && (
el.isContentEditable || el.tagName == 'INPUT' || el.tagName == 'SELECT' || el.tagName == 'TEXTAREA'
);
};
window.Mousetrap.bind('esc', function() {
window.Mousetrap.bind('esc', () => {
const $oc = $('#modal-wrap .close');
if ($oc.length) $oc.trigger('click');
else {
@ -163,7 +162,7 @@ window.lichess.load.then(() => {
if (!storage.get('grid')) setTimeout(() => {
if (getComputedStyle(document.body).getPropertyValue('--grid'))
storage.set('grid', 1);
storage.set('grid', '1');
else
$.get(assetUrl('oops/browser.html'), html => $('body').prepend(html))
}, 3000);

View File

@ -8,11 +8,10 @@
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"noEmitOnError": false,
"moduleResolution": "node",
"target": "ES2016",
"module": "commonjs",
"lib": ["DOM", "ES2016"],
"lib": ["DOM", "ES2016", "DOM.iterable"],
"types": ["lichess", "jquery", "defer-promise"]
}
}