From 07b44ba69e482baca537c33c0dd1db51560d3d2a Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 19 Oct 2021 18:25:07 +0200 Subject: [PATCH] more POE tweaks, validate dates --- ui/analyse/css/explorer/_config.scss | 3 +++ ui/analyse/src/explorer/explorerConfig.ts | 8 ++++++-- ui/analyse/src/explorer/explorerCtrl.ts | 2 +- ui/analyse/src/explorer/explorerView.ts | 16 +++++++--------- ui/analyse/src/explorer/explorerXhr.ts | 23 +++++++++++++++++------ ui/common/src/ndjson.ts | 6 +++--- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/ui/analyse/css/explorer/_config.scss b/ui/analyse/css/explorer/_config.scss index a38b655d4b..1375579053 100644 --- a/ui/analyse/css/explorer/_config.scss +++ b/ui/analyse/css/explorer/_config.scss @@ -84,6 +84,9 @@ border: none; background: none; padding: 0.1em 0.5em; + &:invalid { + background: mix($c-error, $c-bg-box, 30%); + } } } diff --git a/ui/analyse/src/explorer/explorerConfig.ts b/ui/analyse/src/explorer/explorerConfig.ts index f5a511ad5e..c0c2516dfb 100644 --- a/ui/analyse/src/explorer/explorerConfig.ts +++ b/ui/analyse/src/explorer/explorerConfig.ts @@ -39,7 +39,7 @@ export class ExplorerConfigCtrl { rating: storedJsonProp('explorer.rating', () => allRatings), speed: storedJsonProp('explorer.speed', () => allSpeeds), mode: storedJsonProp('explorer.mode', () => allModes), - since: storedProp('explorer.since', ''), + since: storedProp('explorer.since', '2010-01'), until: storedProp('explorer.until', ''), playerName: { open: prop(false), @@ -176,9 +176,13 @@ const monthInput = (prop: StoredProp) => type: 'month', pattern: '^20[12][0-9]-(0[1-9]|1[012])$', min: '2010-01', + max: '2030-01', 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) => diff --git a/ui/analyse/src/explorer/explorerCtrl.ts b/ui/analyse/src/explorer/explorerCtrl.ts index f85d9986a0..c48f57da36 100644 --- a/ui/analyse/src/explorer/explorerCtrl.ts +++ b/ui/analyse/src/explorer/explorerCtrl.ts @@ -113,7 +113,7 @@ export default class ExplorerCtrl { processData ) .then(stream => { - stream.end.promise.then(this.root.redraw); + stream.end.promise.then(err => (err ? onError(err) : this.root.redraw())); return stream; }) ) diff --git a/ui/analyse/src/explorer/explorerView.ts b/ui/analyse/src/explorer/explorerView.ts index 517ed53f90..ce3b3a9471 100644 --- a/ui/analyse/src/explorer/explorerView.ts +++ b/ui/analyse/src/explorer/explorerView.ts @@ -454,15 +454,13 @@ export default function (ctrl: AnalyseCtrl): VNode | undefined { [ h('div.overlay'), content, - !content || explorer.failing() - ? null - : h('button.fbt.toconf', { - attrs: { - 'aria-label': configOpened ? 'Close configuration' : 'Open configuration', - ...dataIcon(configOpened ? '' : ''), - }, - hook: bind('click', () => ctrl.explorer.config.toggleOpen(), ctrl.redraw), - }), + h('button.fbt.toconf', { + attrs: { + 'aria-label': configOpened ? 'Close configuration' : 'Open configuration', + ...dataIcon(configOpened ? '' : ''), + }, + hook: bind('click', () => ctrl.explorer.config.toggleOpen(), ctrl.redraw), + }), ] ); } diff --git a/ui/analyse/src/explorer/explorerXhr.ts b/ui/analyse/src/explorer/explorerXhr.ts index 5b535069c3..4d27f070bf 100644 --- a/ui/analyse/src/explorer/explorerXhr.ts +++ b/ui/analyse/src/explorer/explorerXhr.ts @@ -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 { readNdJson, CancellableStream } from 'common/ndjson'; import { ExplorerConfigData } from './explorerConfig'; +import { sync } from 'common/sync'; interface OpeningXhrOpts { endpoint: string; @@ -16,7 +17,10 @@ interface OpeningXhrOpts { withGames?: boolean; } -export function opening(opts: OpeningXhrOpts, processData: (data: ExplorerData) => void): Promise { +export async function opening( + opts: OpeningXhrOpts, + processData: (data: ExplorerData) => void +): Promise { const conf = opts.config; const endpoint = opts.db == 'player' ? opts.endpoint3 : opts.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('speeds', conf.speed().join(',')); params.set('modes', conf.mode().join(',')); - if (conf.since()) params.set('since', conf.since().replace('-', '/')); - if (conf.until()) params.set('until', conf.until().replace('-', '/')); + if (conf.since()) params.set('since', conf.since()); + if (conf.until()) params.set('until', conf.until()); } if (!opts.withGames) { params.set('topGames', '0'); params.set('recentGames', '0'); } - const stream = fetch(url.href, { + const res = await fetch(url.href, { cache: 'default', headers: {}, // avoid default headers for cors credentials: 'omit', }); + const onMessage = (line: any) => { const data = line as Partial; data.isOpening = true; data.fen = opts.fen; 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 { diff --git a/ui/common/src/ndjson.ts b/ui/common/src/ndjson.ts index 76e7326fef..c9aa820062 100644 --- a/ui/common/src/ndjson.ts +++ b/ui/common/src/ndjson.ts @@ -4,7 +4,7 @@ export type ProcessLine = (line: any) => void; export interface CancellableStream { cancel(): void; - end: Sync; + end: Sync; } /* @@ -21,11 +21,11 @@ export const readNdJson = const decoder = new TextDecoder(); let buf = ''; - const loop = (): Promise => + const loop = (): Promise => stream.read().then(({ done, value }) => { if (done) { if (buf.length > 0) processLine(JSON.parse(buf)); - return Promise.resolve(true); + return Promise.resolve(undefined); } else { const chunk = decoder.decode(value, { stream: true }); buf += chunk;