more POE tweaks, validate dates

openingexplorer3
Thibault Duplessis 2021-10-19 18:25:07 +02:00
parent 8ec273219c
commit 07b44ba69e
6 changed files with 37 additions and 21 deletions

View File

@ -84,6 +84,9 @@
border: none; border: none;
background: none; background: none;
padding: 0.1em 0.5em; padding: 0.1em 0.5em;
&:invalid {
background: mix($c-error, $c-bg-box, 30%);
}
} }
} }

View File

@ -39,7 +39,7 @@ export class ExplorerConfigCtrl {
rating: storedJsonProp('explorer.rating', () => allRatings), rating: storedJsonProp('explorer.rating', () => allRatings),
speed: storedJsonProp<ExplorerSpeed[]>('explorer.speed', () => allSpeeds), speed: storedJsonProp<ExplorerSpeed[]>('explorer.speed', () => allSpeeds),
mode: storedJsonProp<ExplorerMode[]>('explorer.mode', () => allModes), mode: storedJsonProp<ExplorerMode[]>('explorer.mode', () => allModes),
since: storedProp('explorer.since', ''), since: storedProp('explorer.since', '2010-01'),
until: storedProp('explorer.until', ''), until: storedProp('explorer.until', ''),
playerName: { playerName: {
open: prop(false), open: prop(false),
@ -176,9 +176,13 @@ const monthInput = (prop: StoredProp<Month>) =>
type: 'month', type: 'month',
pattern: '^20[12][0-9]-(0[1-9]|1[012])$', pattern: '^20[12][0-9]-(0[1-9]|1[012])$',
min: '2010-01', min: '2010-01',
max: '2030-01',
value: prop() || '', value: prop() || '',
}, },
hook: bind('change', e => prop((e.target as HTMLInputElement).value)), hook: bind('change', e => {
const input = e.target as HTMLInputElement;
if (input.checkValidity()) prop(input.value);
}),
}); });
const monthSection = (ctrl: ExplorerConfigCtrl) => const monthSection = (ctrl: ExplorerConfigCtrl) =>

View File

@ -113,7 +113,7 @@ export default class ExplorerCtrl {
processData processData
) )
.then(stream => { .then(stream => {
stream.end.promise.then(this.root.redraw); stream.end.promise.then(err => (err ? onError(err) : this.root.redraw()));
return stream; return stream;
}) })
) )

View File

@ -454,15 +454,13 @@ export default function (ctrl: AnalyseCtrl): VNode | undefined {
[ [
h('div.overlay'), h('div.overlay'),
content, content,
!content || explorer.failing() h('button.fbt.toconf', {
? null attrs: {
: h('button.fbt.toconf', { 'aria-label': configOpened ? 'Close configuration' : 'Open configuration',
attrs: { ...dataIcon(configOpened ? '' : ''),
'aria-label': configOpened ? 'Close configuration' : 'Open configuration', },
...dataIcon(configOpened ? '' : ''), hook: bind('click', () => ctrl.explorer.config.toggleOpen(), ctrl.redraw),
}, }),
hook: bind('click', () => ctrl.explorer.config.toggleOpen(), ctrl.redraw),
}),
] ]
); );
} }

View File

@ -1,7 +1,8 @@
import { ExplorerData, ExplorerDb, ExplorerMode, ExplorerSpeed, OpeningData, TablebaseData } from './interfaces'; import { ExplorerData, ExplorerDb, OpeningData, TablebaseData } from './interfaces';
import * as xhr from 'common/xhr'; import * as xhr from 'common/xhr';
import { readNdJson, CancellableStream } from 'common/ndjson'; import { readNdJson, CancellableStream } from 'common/ndjson';
import { ExplorerConfigData } from './explorerConfig'; import { ExplorerConfigData } from './explorerConfig';
import { sync } from 'common/sync';
interface OpeningXhrOpts { interface OpeningXhrOpts {
endpoint: string; endpoint: string;
@ -16,7 +17,10 @@ interface OpeningXhrOpts {
withGames?: boolean; withGames?: boolean;
} }
export function opening(opts: OpeningXhrOpts, processData: (data: ExplorerData) => void): Promise<CancellableStream> { export async function opening(
opts: OpeningXhrOpts,
processData: (data: ExplorerData) => void
): Promise<CancellableStream> {
const conf = opts.config; const conf = opts.config;
const endpoint = opts.db == 'player' ? opts.endpoint3 : opts.endpoint; const endpoint = opts.db == 'player' ? opts.endpoint3 : opts.endpoint;
const url = new URL(opts.db === 'lichess' ? '/lichess' : opts.db == 'player' ? '/personal' : '/master', endpoint); const url = new URL(opts.db === 'lichess' ? '/lichess' : opts.db == 'player' ? '/personal' : '/master', endpoint);
@ -34,25 +38,32 @@ export function opening(opts: OpeningXhrOpts, processData: (data: ExplorerData)
params.set('update', 'true'); params.set('update', 'true');
params.set('speeds', conf.speed().join(',')); params.set('speeds', conf.speed().join(','));
params.set('modes', conf.mode().join(',')); params.set('modes', conf.mode().join(','));
if (conf.since()) params.set('since', conf.since().replace('-', '/')); if (conf.since()) params.set('since', conf.since());
if (conf.until()) params.set('until', conf.until().replace('-', '/')); if (conf.until()) params.set('until', conf.until());
} }
if (!opts.withGames) { if (!opts.withGames) {
params.set('topGames', '0'); params.set('topGames', '0');
params.set('recentGames', '0'); params.set('recentGames', '0');
} }
const stream = fetch(url.href, { const res = await fetch(url.href, {
cache: 'default', cache: 'default',
headers: {}, // avoid default headers for cors headers: {}, // avoid default headers for cors
credentials: 'omit', credentials: 'omit',
}); });
const onMessage = (line: any) => { const onMessage = (line: any) => {
const data = line as Partial<OpeningData>; const data = line as Partial<OpeningData>;
data.isOpening = true; data.isOpening = true;
data.fen = opts.fen; data.fen = opts.fen;
processData(data as OpeningData); processData(data as OpeningData);
}; };
return stream.then(readNdJson(onMessage));
if (res.ok) return readNdJson(onMessage)(res);
return {
cancel() {},
end: sync(Promise.resolve(new Error(`Explorer error: ${res.status}`))),
};
} }
export async function tablebase(endpoint: string, variant: VariantKey, fen: Fen): Promise<TablebaseData> { export async function tablebase(endpoint: string, variant: VariantKey, fen: Fen): Promise<TablebaseData> {

View File

@ -4,7 +4,7 @@ export type ProcessLine = (line: any) => void;
export interface CancellableStream { export interface CancellableStream {
cancel(): void; cancel(): void;
end: Sync<boolean>; end: Sync<Error | undefined>;
} }
/* /*
@ -21,11 +21,11 @@ export const readNdJson =
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buf = ''; let buf = '';
const loop = (): Promise<boolean> => const loop = (): Promise<Error | undefined> =>
stream.read().then(({ done, value }) => { stream.read().then(({ done, value }) => {
if (done) { if (done) {
if (buf.length > 0) processLine(JSON.parse(buf)); if (buf.length > 0) processLine(JSON.parse(buf));
return Promise.resolve(true); return Promise.resolve(undefined);
} else { } else {
const chunk = decoder.decode(value, { stream: true }); const chunk = decoder.decode(value, { stream: true });
buf += chunk; buf += chunk;