From b82705dd7b7477656921eb75aa2daf2d79859266 Mon Sep 17 00:00:00 2001 From: Jonathan Gamble Date: Tue, 12 Nov 2024 09:38:37 -0600 Subject: [PATCH 1/5] consolidate MoveMetadata types --- ui/round/src/interfaces.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/round/src/interfaces.ts b/ui/round/src/interfaces.ts index 7ca4876fef623..16c4662476874 100644 --- a/ui/round/src/interfaces.ts +++ b/ui/round/src/interfaces.ts @@ -7,6 +7,7 @@ import type { ChatCtrl, ChatPlugin } from 'chat'; import * as Prefs from 'common/prefs'; import type { EnhanceOpts } from 'common/richText'; import type { RoundSocket } from './socket'; +import type { MoveMetadata as CgMoveMetadata } from 'chessground/types'; export { type RoundSocket } from './socket'; export { type CorresClockData } from './corresClock/corresClockCtrl'; @@ -196,8 +197,8 @@ export interface Pref { resizeHandle: Prefs.ShowResizeHandle; } -export interface MoveMetadata { - premove?: boolean; +export interface MoveMetadata extends CgMoveMetadata { + preConfirmed?: boolean; justDropped?: Role; justCaptured?: Piece; } From 9eff9281bd80b125604ac1f916fa90a9a23cf2ae Mon Sep 17 00:00:00 2001 From: Jonathan Gamble Date: Tue, 12 Nov 2024 09:39:38 -0600 Subject: [PATCH 2/5] allow voice users to enable move confirmation when disabled in prefs --- ui/round/src/view/boardMenu.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/round/src/view/boardMenu.ts b/ui/round/src/view/boardMenu.ts index 6c4d6f5a2ae0c..22387c30eee23 100644 --- a/ui/round/src/view/boardMenu.ts +++ b/ui/round/src/view/boardMenu.ts @@ -19,7 +19,9 @@ export default function (ctrl: RoundController): LooseVNode { ), menu.voiceInput(boolPrefXhrToggle('voice', !!ctrl.voiceMove), !spectator), menu.keyboardInput(boolPrefXhrToggle('keyboardMove', !!ctrl.keyboardMove), !spectator), - !spectator && d.pref.submitMove ? menu.confirmMove(ctrl.confirmMoveToggle) : undefined, + !spectator && (d.pref.submitMove || ctrl.voiceMove) + ? menu.confirmMove(ctrl.confirmMoveToggle) + : undefined, ]), h('section.board-menu__links', [ h( From 9d0a6fbfca21a69f1c2d7810b550a612c7035d9d Mon Sep 17 00:00:00 2001 From: Jonathan Gamble Date: Tue, 12 Nov 2024 09:41:29 -0600 Subject: [PATCH 3/5] voice.move.ts appetite for RoundController functionality is insatiable --- ui/chess/src/moveRootCtrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/chess/src/moveRootCtrl.ts b/ui/chess/src/moveRootCtrl.ts index b81684610d024..18f7d89b72096 100644 --- a/ui/chess/src/moveRootCtrl.ts +++ b/ui/chess/src/moveRootCtrl.ts @@ -1,5 +1,5 @@ export interface MoveRootCtrl { - pluginMove: (orig: Key, dest: Key, prom: Role | undefined) => void; + pluginMove: (orig: Key, dest: Key, prom: Role | undefined, preConfirmed?: boolean /* = false */) => void; redraw: () => void; flipNow: () => void; offerDraw?: (v: boolean, immediately?: boolean) => void; @@ -12,6 +12,7 @@ export interface MoveRootCtrl { blindfold?: (v?: boolean) => boolean; speakClock?: () => void; goBerserk?: () => void; + shouldConfirmMove?: () => boolean; } export interface MoveUpdate { From 208d12d2cc335bc0bdd56794aa2b3cfa1f6424a0 Mon Sep 17 00:00:00 2001 From: Jonathan Gamble Date: Tue, 12 Nov 2024 09:42:38 -0600 Subject: [PATCH 4/5] make voice move confirm and standard move confirm complementary --- ui/round/src/ctrl.ts | 28 +++++++++++++++------------- ui/voice/src/move/voice.move.ts | 10 +++++----- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/ui/round/src/ctrl.ts b/ui/round/src/ctrl.ts index 241351a489605..3b1dd0d2f4866 100644 --- a/ui/round/src/ctrl.ts +++ b/ui/round/src/ctrl.ts @@ -11,7 +11,6 @@ import { make as makeSocket, type RoundSocket } from './socket'; import * as title from './title'; import * as blur from './blur'; import viewStatus from 'game/view/status'; -import type { MoveMetadata as CgMoveMetadata } from 'chessground/types'; import { ClockController } from './clock/clockCtrl'; import { CorresClockController } from './corresClock/corresClockCtrl'; import MoveOn from './moveOn'; @@ -72,7 +71,7 @@ export default class RoundController implements MoveRootCtrl { firstSeconds = true; flip = false; menu: Toggle; - confirmMoveToggle: Toggle = toggle(true); + confirmMoveToggle: Toggle; loading = false; loadingTimeout: number; redirecting = false; @@ -123,7 +122,7 @@ export default class RoundController implements MoveRootCtrl { ); this.setQuietMode(); - + this.confirmMoveToggle = toggle(d.pref.submitMove); this.moveOn = new MoveOn(this, 'move-on'); if (!opts.local) this.transientMove = new TransientMove(this.socket); @@ -158,12 +157,12 @@ export default class RoundController implements MoveRootCtrl { setTimeout(this.showExpiration, 250); }; - private onUserMove = (orig: Key, dest: Key, meta: CgMoveMetadata) => { + private onUserMove = (orig: Key, dest: Key, meta: MoveMetadata) => { if (!this.keyboardMove?.usedSan) ab.move(this, meta, pubsub.emit); if (!this.startPromotion(orig, dest, meta)) this.sendMove(orig, dest, undefined, meta); }; - private onUserNewPiece = (role: Role, key: Key, meta: CgMoveMetadata) => { + private onUserNewPiece = (role: Role, key: Key, meta: MoveMetadata) => { if (!this.replaying() && crazyValid(this.data, role, key)) { this.sendNewPiece(role, key, !!meta.predrop); } else this.jump(this.ply); @@ -178,7 +177,7 @@ export default class RoundController implements MoveRootCtrl { } else site.sound.move({ name: 'move', filter: 'game' }); }; - private startPromotion = (orig: Key, dest: Key, meta: CgMoveMetadata) => + private startPromotion = (orig: Key, dest: Key, meta: MoveMetadata) => this.promotion.start( orig, dest, @@ -190,7 +189,7 @@ export default class RoundController implements MoveRootCtrl { this.keyboardMove?.justSelected(), ); - private onPremove = (orig: Key, dest: Key, meta: CgMoveMetadata) => this.startPromotion(orig, dest, meta); + private onPremove = (orig: Key, dest: Key, meta: MoveMetadata) => this.startPromotion(orig, dest, meta); private onCancelPremove = () => this.promotion.cancelPrePromotion(); @@ -293,7 +292,7 @@ export default class RoundController implements MoveRootCtrl { setTitle = (): void => title.set(this); - actualSendMove = (tpe: string, data: any, meta: MoveMetadata = {}): void => { + actualSendMove = (tpe: string, data: any, meta: MoveMetadata = { premove: false }): void => { const socketOpts: SocketOpts = { sign: this.sign, ackable: true, @@ -319,7 +318,7 @@ export default class RoundController implements MoveRootCtrl { this.redraw(); }; - pluginMove = (orig: Key, dest: Key, role?: Role): void => { + pluginMove = (orig: Key, dest: Key, role?: Role, preConfirmed?: boolean): void => { if (!role) { this.chessground.move(orig, dest); this.chessground.state.movable.dests = undefined; @@ -327,7 +326,7 @@ export default class RoundController implements MoveRootCtrl { if (this.startPromotion(orig, dest, { premove: false })) return; } - this.sendMove(orig, dest, role, { premove: false }); + this.sendMove(orig, dest, role, { premove: false, preConfirmed }); }; pluginUpdate = (fen: string): void => { @@ -335,13 +334,16 @@ export default class RoundController implements MoveRootCtrl { this.keyboardMove?.update({ fen, canMove: this.canMove() }); }; - sendMove = (orig: Key, dest: Key, prom: Role | undefined, meta: CgMoveMetadata): void => { + shouldConfirmMove = (): boolean => + (this.data.pref.submitMove && this.confirmMoveToggle()) || this.confirmMoveToggle(); + + sendMove = (orig: Key, dest: Key, prom: Role | undefined, meta: MoveMetadata): void => { const move: SocketMove = { u: orig + dest }; if (prom) move.u += prom === 'knight' ? 'n' : prom[0]; if (blur.get()) move.b = 1; this.resign(false); - if (this.data.pref.submitMove && this.confirmMoveToggle() && !meta.premove) { + if (!meta.preConfirmed && this.shouldConfirmMove() && !meta.premove) { if (site.sound.speech()) { const spoken = `${speakable(sanOf(readFen(this.stepAt(this.ply).fen), move.u))}. confirm?`; site.sound.say(spoken, false, true); @@ -357,7 +359,7 @@ export default class RoundController implements MoveRootCtrl { const drop: SocketDrop = { role, pos: key }; if (blur.get()) drop.b = 1; this.resign(false); - if (this.data.pref.submitMove && this.confirmMoveToggle() && !isPredrop) { + if (this.shouldConfirmMove() && !isPredrop) { this.toSubmit = drop; this.redraw(); } else { diff --git a/ui/voice/src/move/voice.move.ts b/ui/voice/src/move/voice.move.ts index 6e6fb1a8bacfe..a2c11c4f6bdba 100644 --- a/ui/voice/src/move/voice.move.ts +++ b/ui/voice/src/move/voice.move.ts @@ -297,9 +297,9 @@ export function initModule({ // trim choices to clarity window options = options.filter(([, m]) => m.cost - lowestCost <= clarityThreshold); - if (!timer() && options.length === 1 && options[0][1].cost < 0.3) { + if (!timer() && options.length === 1 && (options[0][1].cost < 0.3 || root.shouldConfirmMove?.())) { console.info('chooseMoves', `chose '${options[0][0]}' cost=${options[0][1].cost}`); - submit(options[0][0]); + submit(options[0][0], false); return true; } return ambiguate(options); @@ -321,7 +321,7 @@ export function initModule({ if (preferred && timer()) { choiceTimeout = setTimeout( () => { - submit(options[0][0]); + submit(options[0][0], false); choiceTimeout = undefined; voice.mic.setRecognizer('default'); }, @@ -349,7 +349,7 @@ export function initModule({ buildSquares(); } - function submit(uci: Uci) { + function submit(uci: Uci, preConfirmed = true) { clearMoveProgress(); if (uci.length < 3) { const dests = [...new Set(ucis.filter(x => x.length === 4 && x.startsWith(uci)))]; @@ -362,7 +362,7 @@ export function initModule({ const role = promo(uci); cg.cancelMove(); if (role) promote(cg, dest(uci), role); - root.pluginMove(src(uci), dest(uci), role); + root.pluginMove(src(uci), dest(uci), role, preConfirmed); return true; } From 498f5c950c0ce5b919b48590311567ca5ea1c32e Mon Sep 17 00:00:00 2001 From: Jonathan Gamble Date: Wed, 13 Nov 2024 17:40:39 -0600 Subject: [PATCH 5/5] remove redundant function --- ui/chess/src/moveRootCtrl.ts | 2 +- ui/round/src/ctrl.ts | 7 ++----- ui/voice/src/move/voice.move.ts | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ui/chess/src/moveRootCtrl.ts b/ui/chess/src/moveRootCtrl.ts index 18f7d89b72096..9aa7f6fc2fdff 100644 --- a/ui/chess/src/moveRootCtrl.ts +++ b/ui/chess/src/moveRootCtrl.ts @@ -12,7 +12,7 @@ export interface MoveRootCtrl { blindfold?: (v?: boolean) => boolean; speakClock?: () => void; goBerserk?: () => void; - shouldConfirmMove?: () => boolean; + confirmMoveToggle?: () => boolean; } export interface MoveUpdate { diff --git a/ui/round/src/ctrl.ts b/ui/round/src/ctrl.ts index 3b1dd0d2f4866..a19346eb2cf49 100644 --- a/ui/round/src/ctrl.ts +++ b/ui/round/src/ctrl.ts @@ -334,16 +334,13 @@ export default class RoundController implements MoveRootCtrl { this.keyboardMove?.update({ fen, canMove: this.canMove() }); }; - shouldConfirmMove = (): boolean => - (this.data.pref.submitMove && this.confirmMoveToggle()) || this.confirmMoveToggle(); - sendMove = (orig: Key, dest: Key, prom: Role | undefined, meta: MoveMetadata): void => { const move: SocketMove = { u: orig + dest }; if (prom) move.u += prom === 'knight' ? 'n' : prom[0]; if (blur.get()) move.b = 1; this.resign(false); - if (!meta.preConfirmed && this.shouldConfirmMove() && !meta.premove) { + if (!meta.preConfirmed && this.confirmMoveToggle() && !meta.premove) { if (site.sound.speech()) { const spoken = `${speakable(sanOf(readFen(this.stepAt(this.ply).fen), move.u))}. confirm?`; site.sound.say(spoken, false, true); @@ -359,7 +356,7 @@ export default class RoundController implements MoveRootCtrl { const drop: SocketDrop = { role, pos: key }; if (blur.get()) drop.b = 1; this.resign(false); - if (this.shouldConfirmMove() && !isPredrop) { + if (this.confirmMoveToggle() && !isPredrop) { this.toSubmit = drop; this.redraw(); } else { diff --git a/ui/voice/src/move/voice.move.ts b/ui/voice/src/move/voice.move.ts index a2c11c4f6bdba..35966aaccf878 100644 --- a/ui/voice/src/move/voice.move.ts +++ b/ui/voice/src/move/voice.move.ts @@ -297,7 +297,7 @@ export function initModule({ // trim choices to clarity window options = options.filter(([, m]) => m.cost - lowestCost <= clarityThreshold); - if (!timer() && options.length === 1 && (options[0][1].cost < 0.3 || root.shouldConfirmMove?.())) { + if (!timer() && options.length === 1 && (options[0][1].cost < 0.3 || root.confirmMoveToggle?.())) { console.info('chooseMoves', `chose '${options[0][0]}' cost=${options[0][1].cost}`); submit(options[0][0], false); return true;