Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment with a public stable browser API that extensions can use #16294

Merged
merged 15 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions ui/@types/lichess/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,26 @@ interface Fipr {
x64hash128(input: string, seed: number): string;
}

interface Api {
ornicar marked this conversation as resolved.
Show resolved Hide resolved
initializeDom: (root?: HTMLElement) => void;
onlineFriends: {
request: () => void;
events: {
on(key: string, cb: (...args: any[]) => void): void;
off(key: string, cb: (...args: any[]) => void): void;
};
};
chat: {
post: (text: string) => void;
};
overrides: {
[key: string]: (...args: any[]) => unknown;
};
}

interface Window {
site: Site;
lichess: Api;
fipr: Fipr;
i18n: I18n;
$as<T>(cash: Cash): T;
Expand Down
2 changes: 1 addition & 1 deletion ui/analyse/src/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const bind = (ctrl: AnalyseCtrl) => {
.bind('f', ctrl.flip)
.bind('?', () => {
ctrl.keyboardHelp = !ctrl.keyboardHelp;
if (ctrl.keyboardHelp) pubsub.emit('analyse.close-all');
if (ctrl.keyboardHelp) pubsub.emit('analysis.closeAll');
ctrl.redraw();
})
.bind('l', ctrl.toggleCeval)
Expand Down
2 changes: 1 addition & 1 deletion ui/analyse/src/plugins/analyse.study.tour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class TourCtrl {
},
exitOnEsc: true,
});
pubsub.on('analyse.close-all', this.tour.cancel);
pubsub.on('analysis.closeAll', this.tour.cancel);
}

buildTour(steps: Shepherd.Step.StepOptions[]) {
Expand Down
4 changes: 2 additions & 2 deletions ui/analyse/src/study/chapterNewForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ export class StudyChapterNewForm {
readonly setTab: () => void,
readonly root: AnalyseCtrl,
) {
pubsub.on('analyse.close-all', () => this.isOpen(false));
pubsub.on('analysis.closeAll', () => this.isOpen(false));
}

open = () => {
pubsub.emit('analyse.close-all');
pubsub.emit('analysis.closeAll');
this.isOpen(true);
this.loadVariants();
this.initial(false);
Expand Down
4 changes: 2 additions & 2 deletions ui/analyse/src/study/inviteForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export function makeCtrl(
spectators = prop<string[]>([]);

const toggle = () => {
if (!open()) pubsub.emit('analyse.close-all');
if (!open()) pubsub.emit('analysis.closeAll');
open(!open());
redraw();
};

pubsub.on('analyse.close-all', () => open(false));
pubsub.on('analysis.closeAll', () => open(false));

const previouslyInvited = storedSet<string>('study.previouslyInvited', 10);
return {
Expand Down
3 changes: 1 addition & 2 deletions ui/bits/src/bits.challengePage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as xhr from 'common/xhr';
import StrongSocket from 'common/socket';
import { userComplete } from 'common/userComplete';
import { pubsub } from 'common/pubsub';

interface ChallengeOpts {
xhrUrl: string;
Expand All @@ -19,7 +18,7 @@ export function initModule(opts: ChallengeOpts): void {
xhr.text(opts.xhrUrl).then(html => {
$(selector).replaceWith($(html).find(selector));
init();
pubsub.emit('content-loaded', $(selector)[0]);
window.lichess.initializeDom($(selector)[0]);
});
},
},
Expand Down
3 changes: 1 addition & 2 deletions ui/bits/src/bits.infiniteScroll.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as xhr from 'common/xhr';
import { spinnerHtml } from 'common/spinner';
import { pubsub } from 'common/pubsub';

export function initModule(selector: string = '.infinite-scroll'): void {
$(selector).each(function (this: HTMLElement) {
Expand Down Expand Up @@ -37,7 +36,7 @@ function register(el: HTMLElement, selector: string, backoff = 500) {
nav.remove();
$(el).append(($(html).is(selector) ? $(html) : $(html).find(selector)).html());
dedupEntries(el);
pubsub.emit('content-loaded', el);
window.lichess.initializeDom(el);
setTimeout(() => register(el, selector, backoff * 1.05), backoff); // recursion with backoff
},
e => {
Expand Down
10 changes: 5 additions & 5 deletions ui/bits/src/bits.tvGames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ const requestReplacementGame = () => {
.json(url.toString())
.then((data: ReplacementResponse) => {
main.find(`.mini-game[href^="/${oldId}"]`).replaceWith(data.html);
if (data.html.includes('mini-game__result')) onFinish(data.id);
pubsub.emit('content-loaded');
if (data.html.includes('mini-game__result')) window.lichess.overrides.tvGamesOnFinish(data.id);
window.lichess.initializeDom();
})
.then(done, done);
});
Expand All @@ -40,17 +40,17 @@ const done = () => {
requestReplacementGame();
};

const onFinish = (id: string) =>
window.lichess.overrides.tvGamesOnFinish = (id: string) =>
setTimeout(() => {
finishedIdQueue.push(id);
requestReplacementGame();
}, 7000); // 7000 matches the rematch wait duration in /modules/tv/main/Tv.scala

site.load.then(() => {
pubsub.on('socket.in.finish', ({ id }) => onFinish(id));
pubsub.on('socket.in.finish', ({ id }) => window.lichess.overrides.tvGamesOnFinish(id));
$('main.tv-games')
.find('.mini-game')
.each((_i, el) => {
if ($(el).find('.mini-game__result').length > 0) onFinish(getId(el)!);
if ($(el).find('.mini-game__result').length > 0) window.lichess.overrides.tvGamesOnFinish(getId(el)!);
});
});
3 changes: 1 addition & 2 deletions ui/bits/src/bits.user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as xhr from 'common/xhr';
import { makeLinkPopups } from 'common/linkPopup';
import { pubsub } from 'common/pubsub';
import { alert } from 'common/dialog';

export function initModule(): void {
Expand Down Expand Up @@ -48,7 +47,7 @@ export function initModule(): void {
browseTo = (path: string) =>
xhr.text(path).then(html => {
$content.html(html);
pubsub.emit('content-loaded', $content[0]);
window.lichess.initializeDom($content[0]);
history.replaceState({}, '', path);
site.asset.loadEsm('bits.infiniteScroll');
});
Expand Down
2 changes: 1 addition & 1 deletion ui/chat/src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default class ChatCtrl {
alert('Max length: 140 chars. ' + text.length + ' chars used.');
return false;
}
pubsub.emit('socket.send', 'talk', text);
window.lichess.chat.post(text);
return true;
};

Expand Down
2 changes: 1 addition & 1 deletion ui/chat/src/moderation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export function moderationView(ctrl?: ModerationCtrl): VNode[] | undefined {
{
hook: {
insert() {
pubsub.emit('content-loaded');
window.lichess.initializeDom();
},
},
},
Expand Down
7 changes: 1 addition & 6 deletions ui/common/src/pubsub.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type PubsubEvent =
| 'ab.rep'
| 'analyse.close-all'
| 'analysis.closeAll'
| 'analysis.change'
| 'analysis.chart.click'
| 'analysis.comp.toggle'
Expand Down Expand Up @@ -29,11 +29,6 @@ export type PubsubEvent =
| 'socket.in.announce'
| 'socket.in.fen'
| 'socket.in.finish'
| 'socket.in.following_enters'
| 'socket.in.following_leaves'
| 'socket.in.following_onlines'
| 'socket.in.following_playing'
| 'socket.in.following_stopped_playing'
| 'socket.in.message'
| 'socket.in.mlat'
| 'socket.in.msgNew'
Expand Down
4 changes: 2 additions & 2 deletions ui/lobby/src/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export function initModule(opts: LobbyOpts) {
reload_timeline() {
xhr.text('/timeline').then(html => {
$('.timeline').html(html);
pubsub.emit('content-loaded');
window.lichess.initializeDom();
});
},
featured(o: { html: string }) {
$('.lobby__tv').html(o.html);
pubsub.emit('content-loaded');
window.lichess.initializeDom();
},
redirect(e: RedirectTo) {
lobbyCtrl.setRedirecting();
Expand Down
3 changes: 1 addition & 2 deletions ui/mod/src/mod.user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import extendTablesortNumber from 'common/tablesortNumber';
import tablesort from 'tablesort';
import { expandCheckboxZone, shiftClickCheckboxRange, selector } from './checkBoxes';
import { spinnerHtml } from 'common/spinner';
import { pubsub } from 'common/pubsub';
import { confirm } from 'common/dialog';

site.load.then(() => {
Expand Down Expand Up @@ -64,7 +63,7 @@ site.load.then(() => {
const getLocationHash = (a: HTMLAnchorElement) => a.href.replace(/.+(#\w+)$/, '$1');

function userMod($inZone: Cash) {
pubsub.emit('content-loaded', $inZone[0]);
window.lichess.initializeDom($inZone[0]);

const makeReady = (selector: string, f: (el: HTMLElement, i: number) => void, cls = 'ready') => {
$inZone.find(selector + `:not(.${cls})`).each(function (this: HTMLElement, i: number) {
Expand Down
3 changes: 1 addition & 2 deletions ui/notify/src/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { h, VNode } from 'snabbdom';
import * as licon from 'common/licon';
import { spinnerVdom as spinner } from 'common/spinner';
import makeRenderers from './renderers';
import { pubsub } from 'common/pubsub';

const renderers = makeRenderers();

Expand Down Expand Up @@ -74,7 +73,7 @@ function clickHook(f: () => void) {
};
}

const contentLoaded = (vnode: VNode) => pubsub.emit('content-loaded', vnode.elm);
const contentLoaded = (vnode: VNode) => window.lichess.initializeDom(vnode.elm as HTMLElement);

function recentNotifications(d: NotifyData, scrolling: boolean): VNode {
return h(
Expand Down
2 changes: 1 addition & 1 deletion ui/round/src/round.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ async function boot(
$meta.length && $('.game__meta').replaceWith($meta);
$('.crosstable').replaceWith($html.find('.crosstable'));
startTournamentClock();
pubsub.emit('content-loaded');
window.lichess.initializeDom();
});
},
tourStanding(s: TourPlayer[]) {
Expand Down
2 changes: 1 addition & 1 deletion ui/simul/src/simul.home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ site.load.then(() => {
const rsp = await fetch('/simul/reload');
const html = await rsp.text();
$('.simul-list__content').html(html);
pubsub.emit('content-loaded');
window.lichess.initializeDom();
});
});
3 changes: 1 addition & 2 deletions ui/site/src/announce.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { escapeHtml } from 'common';
import { pubsub } from 'common/pubsub';

let timeout: Timeout | undefined;

Expand All @@ -25,7 +24,7 @@ const announce = (d: LichessAnnouncement) => {
const millis = d.date ? new Date(d.date).getTime() - Date.now() : 5000;
if (millis > 0) timeout = setTimeout(kill, millis);
else kill();
if (d.date) pubsub.emit('content-loaded');
if (d.date) window.lichess.initializeDom();
}
};

Expand Down
54 changes: 54 additions & 0 deletions ui/site/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Pubsub, PubsubCallback, PubsubEvent } from 'common/pubsub';

// #TODO document these somewhere
const publicEvents = ['ply', 'analysis.change', 'chat.resize', 'analysis.closeAll'];
const socketEvents = ['lag', 'close'];
ornicar marked this conversation as resolved.
Show resolved Hide resolved
const socketInEvents = ['mlat', 'fen', 'notifications', 'endData'];
const friendsEvents = ['playing', 'stopped_playing', 'onlines', 'enters', 'leaves'];

export const api = (pubsub: Pubsub) => ({
initializeDom: (root?: HTMLElement) => pubsub.emit('content-loaded', root),
events: {
on(name: PubsubEvent, cb: PubsubCallback): void {
if (!publicEvents.includes(name)) throw 'This event is not part of the public API';
pubsub.on(name, cb);
},
off(name: PubsubEvent, cb: PubsubCallback): void {
pubsub.off(name, cb);
},
},
socket: {
subscribeToMoveLatency: () => pubsub.emit('socket.send', 'moveLat', true),
events: {
on(key: string, cb: PubsubCallback): void {
if (socketInEvents.includes(key)) pubsub.on(`socket.in.${key}` as PubsubEvent, cb);
else if (socketEvents.includes(key)) pubsub.on(`socket.${key}` as PubsubEvent, cb);
else throw 'This event is not part of the public API';
},
off(key: string, cb: PubsubCallback): void {
const ev = socketInEvents.includes(key)
? (`socket.in.${key}` as PubsubEvent)
: (`socket.${key}` as PubsubEvent);
pubsub.off(ev, cb);
},
},
},
onlineFriends: {
request: () => pubsub.emit('socket.send', 'following_onlines'),
events: {
on(key: string, cb: PubsubCallback): void {
if (!friendsEvents.includes(key)) throw 'This event is not part of the public API';
pubsub.on(`socket.in.following_${key}` as PubsubEvent, cb);
},
off(key: string, cb: PubsubCallback): void {
pubsub.off(`socket.in.following_${key}` as PubsubEvent, cb);
},
},
},
chat: {
post: (text: string) => pubsub.emit('socket.send', 'talk', text),
},
// some functions will be exposed here
// to be overriden by browser extensions
overrides: {},
});
14 changes: 7 additions & 7 deletions ui/site/src/friends.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { notNull } from 'common/common';
import * as licon from 'common/licon';
import { pubsub } from 'common/pubsub';

type TitleName = string;

Expand All @@ -18,20 +17,21 @@ export default class OnlineFriends {
users: Map<string, Friend>;

constructor(readonly el: HTMLElement) {
const api = window.lichess.onlineFriends;
this.titleEl = this.el.querySelector('.friend_box_title') as HTMLElement;
this.titleEl.addEventListener('click', () => {
this.el.querySelector('.content_wrap')?.classList.toggle('none');
if (!this.loaded) {
this.loaded = true;
pubsub.emit('socket.send', 'following_onlines');
api.request();
}
});
this.users = new Map();
pubsub.on('socket.in.following_onlines', this.receive);
pubsub.on('socket.in.following_enters', this.enters);
pubsub.on('socket.in.following_leaves', this.leaves);
pubsub.on('socket.in.following_playing', this.playing);
pubsub.on('socket.in.following_stopped_playing', this.stopped_playing);
api.events.on('onlines', this.receive);
api.events.on('enters', this.enters);
api.events.on('leaves', this.leaves);
api.events.on('playing', this.playing);
api.events.on('stopped_playing', this.stopped_playing);
}
receive = (friends: TitleName[], msg: { playing: string[]; patrons: string[] }) => {
this.users.clear();
Expand Down
3 changes: 1 addition & 2 deletions ui/site/src/powertip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as licon from 'common/licon';
import * as xhr from 'common/xhr';
import { requestIdleCallback, $as } from 'common';
import { spinnerHtml } from 'common/spinner';
import { pubsub } from 'common/pubsub';

// Thanks Steven Benner! - adapted from https://github.com/stevenbenner/jquery-powertip

Expand All @@ -14,7 +13,7 @@ const onPowertipPreRender = (id: string, preload?: (url: string) => void) => (el
xhr.text(url + '/mini').then(html => {
const el = document.getElementById(id) as HTMLElement;
el.innerHTML = html;
pubsub.emit('content-loaded', el);
window.lichess.initializeDom(el);
});
};

Expand Down
Loading