251 lines
6.8 KiB
TypeScript
251 lines
6.8 KiB
TypeScript
import variantConfirm from './variant';
|
|
import * as hookRepo from './hookRepo';
|
|
import * as seekRepo from './seekRepo';
|
|
import { make as makeStores, Stores } from './store';
|
|
import * as xhr from './xhr';
|
|
import * as poolRangeStorage from './poolRangeStorage';
|
|
import { LobbyOpts, LobbyData, Tab, Mode, Sort, Filter, Hook, Seek, Pool, PoolMember } from './interfaces';
|
|
import LobbySocket from './socket';
|
|
|
|
const li = window.lichess;
|
|
|
|
export default class LobbyController {
|
|
|
|
opts: LobbyOpts;
|
|
data: LobbyData;
|
|
playban: any;
|
|
isBot: boolean;
|
|
socket: LobbySocket;
|
|
stores: Stores;
|
|
tab: Tab;
|
|
mode: Mode;
|
|
sort: Sort;
|
|
filterOpen: boolean = false;
|
|
stepHooks: Hook[] = [];
|
|
stepping: boolean = false;
|
|
redirecting: boolean = false;
|
|
poolMember?: PoolMember;
|
|
trans: Trans;
|
|
redraw: () => void;
|
|
pools: Pool[];
|
|
|
|
private poolInStorage: LichessStorage;
|
|
private flushHooksTimeout?: number;
|
|
private alreadyWatching: string[] = [];
|
|
|
|
constructor(opts: LobbyOpts, redraw: () => void) {
|
|
this.opts = opts;
|
|
this.data = opts.data;
|
|
this.data.hooks = [];
|
|
this.pools = opts.pools;
|
|
this.playban = opts.playban;
|
|
this.isBot = opts.data.me && opts.data.me.isBot;
|
|
this.redraw = redraw;
|
|
|
|
hookRepo.initAll(this);
|
|
seekRepo.initAll(this);
|
|
this.socket = new LobbySocket(opts.socketSend, this);
|
|
|
|
this.stores = makeStores(this.data.me ? this.data.me.username.toLowerCase() : null);
|
|
this.tab = this.isBot ? 'now_playing' : this.stores.tab.get(),
|
|
this.mode = this.stores.mode.get(),
|
|
this.sort = this.stores.sort.get(),
|
|
this.trans = opts.trans;
|
|
|
|
this.poolInStorage = li.storage.make('lobby.pool-in');
|
|
this.poolInStorage.listen(_ => { // when another tab joins a pool
|
|
this.leavePool();
|
|
redraw();
|
|
});
|
|
this.flushHooksSchedule();
|
|
|
|
this.startWatching();
|
|
|
|
if (this.playban) {
|
|
if (this.playban.remainingSecond < 86400) setTimeout(li.reload, this.playban.remainingSeconds * 1000);
|
|
}
|
|
else {
|
|
setInterval(() => {
|
|
if (this.poolMember) this.poolIn();
|
|
else if (this.tab === 'real_time' && !this.data.hooks.length) this.socket.realTimeIn();
|
|
}, 10 * 1000);
|
|
this.joinPoolFromLocationHash();
|
|
}
|
|
|
|
li.pubsub.on('socket.open', () => {
|
|
if (this.tab === 'real_time') {
|
|
this.data.hooks = [];
|
|
this.socket.realTimeIn();
|
|
} else if (this.tab === 'pools' && this.poolMember) this.poolIn();
|
|
});
|
|
|
|
window.addEventListener('beforeunload', () => {
|
|
if (this.poolMember) this.socket.poolOut(this.poolMember);
|
|
});
|
|
}
|
|
|
|
private doFlushHooks() {
|
|
this.stepHooks = this.data.hooks.slice(0);
|
|
if (this.tab === 'real_time') this.redraw();
|
|
};
|
|
|
|
flushHooks = (now: boolean) => {
|
|
if (this.flushHooksTimeout) clearTimeout(this.flushHooksTimeout);
|
|
if (now) this.doFlushHooks();
|
|
else {
|
|
this.stepping = true;
|
|
if (this.tab === 'real_time') this.redraw();
|
|
setTimeout(() => {
|
|
this.stepping = false;
|
|
this.doFlushHooks();
|
|
}, 500);
|
|
}
|
|
this.flushHooksTimeout = this.flushHooksSchedule();
|
|
};
|
|
|
|
private flushHooksSchedule(): number {
|
|
return setTimeout(this.flushHooks, 8000);
|
|
}
|
|
|
|
setTab = (tab: Tab) => {
|
|
if (tab !== this.tab) {
|
|
if (tab === 'seeks') xhr.seeks().then(this.setSeeks);
|
|
else if (tab === 'real_time') this.socket.realTimeIn();
|
|
else if (this.tab === 'real_time') {
|
|
this.socket.realTimeOut();
|
|
this.data.hooks = [];
|
|
}
|
|
this.tab = this.stores.tab.set(tab);
|
|
}
|
|
this.filterOpen = false;
|
|
};
|
|
|
|
setMode = (mode: Mode) => {
|
|
this.mode = this.stores.mode.set(mode);
|
|
this.filterOpen = false;
|
|
};
|
|
|
|
setSort = (sort: Sort) => {
|
|
this.sort = this.stores.sort.set(sort);
|
|
};
|
|
|
|
toggleFilter = () => {
|
|
this.filterOpen = !this.filterOpen;
|
|
};
|
|
|
|
setFilter = (filter: Filter) => {
|
|
this.data.filter = filter;
|
|
this.flushHooks(true);
|
|
if (this.tab !== 'real_time') this.redraw();
|
|
};
|
|
|
|
clickHook = (id: string) => {
|
|
const hook = hookRepo.find(this, id);
|
|
if (!hook || hook.disabled || this.stepping || this.redirecting) return;
|
|
if (hook.action === 'cancel' || variantConfirm(hook.variant)) this.socket.send(hook.action, hook.id);
|
|
};
|
|
|
|
clickSeek = (id: string) => {
|
|
const seek = seekRepo.find(this, id);
|
|
if (!seek || this.redirecting) return;
|
|
if (seek.action === 'cancelSeek' || variantConfirm(seek.variant)) this.socket.send(seek.action, seek.id);
|
|
};
|
|
|
|
setSeeks = (seeks: Seek[]) => {
|
|
this.data.seeks = seeks;
|
|
seekRepo.initAll(this);
|
|
this.redraw();
|
|
};
|
|
|
|
clickPool = (id: string) => {
|
|
if (!this.data.me) {
|
|
xhr.anonPoolSeek(this.pools.find(function(p) {
|
|
return p.id === id;
|
|
}));
|
|
this.setTab('real_time');
|
|
} else if (this.poolMember && this.poolMember.id === id) this.leavePool();
|
|
else {
|
|
this.enterPool({ id });
|
|
this.redraw();
|
|
}
|
|
};
|
|
|
|
enterPool = (member: PoolMember) => {
|
|
poolRangeStorage.set(member.id, member.range);
|
|
this.setTab('pools');
|
|
this.poolMember = member;
|
|
this.poolIn();
|
|
};
|
|
|
|
leavePool = () => {
|
|
if (!this.poolMember) return;
|
|
this.socket.poolOut(this.poolMember);
|
|
this.poolMember = undefined;
|
|
this.redraw();
|
|
};
|
|
|
|
poolIn = () => {
|
|
if (!this.poolMember) return;
|
|
this.poolInStorage.fire();
|
|
this.socket.poolIn(this.poolMember);
|
|
};
|
|
|
|
gameActivity = gameId => {
|
|
if (this.data.nowPlaying.find(p => p.gameId === gameId))
|
|
xhr.nowPlaying().then(povs => {
|
|
this.data.nowPlaying = povs;
|
|
this.startWatching();
|
|
this.redraw();
|
|
});
|
|
};
|
|
|
|
private startWatching() {
|
|
const newIds = this.data.nowPlaying
|
|
.map(p => p.gameId)
|
|
.filter(id => !this.alreadyWatching.includes(id));
|
|
if (newIds.length) {
|
|
setTimeout(() => this.socket.send("startWatching", newIds.join(' ')), 2000);
|
|
newIds.forEach(id => this.alreadyWatching.push(id));
|
|
}
|
|
};
|
|
|
|
setRedirecting = () => {
|
|
this.redirecting = true;
|
|
setTimeout(() => {
|
|
this.redirecting = false;
|
|
this.redraw();
|
|
}, 4000);
|
|
this.redraw();
|
|
};
|
|
|
|
awake = () => {
|
|
switch (this.tab) {
|
|
case 'real_time':
|
|
this.data.hooks = [];
|
|
this.socket.realTimeIn();
|
|
break;
|
|
case 'seeks':
|
|
xhr.seeks().then(this.setSeeks);
|
|
break;
|
|
}
|
|
};
|
|
|
|
// after click on round "new opponent" button
|
|
// also handles onboardink link for anon users
|
|
private joinPoolFromLocationHash() {
|
|
if (location.hash.startsWith('#pool/')) {
|
|
const regex = /^#pool\/(\d+\+\d+)(?:\/(.+))?$/,
|
|
match = regex.exec(location.hash),
|
|
member: any = { id: match![1], blocking: match![2] },
|
|
range = poolRangeStorage.get(member.id);
|
|
if (range) member.range = range;
|
|
if (match) {
|
|
this.setTab('pools');
|
|
if (this.data.me) this.enterPool(member);
|
|
else setTimeout(() => this.clickPool(member.id), 1500);
|
|
history.replaceState(null, '', '/');
|
|
}
|
|
}
|
|
}
|
|
}
|