fix: tests + stale callbacks

This commit is contained in:
Timothy Z. 2026-04-28 22:57:33 +03:00
parent f95273b8ce
commit 0b601010e2
4 changed files with 46 additions and 35 deletions

View file

@ -8,6 +8,7 @@ import styles from './styles.less';
type ActiveButton = string | null;
const CX = 400;
const BTN = { L1: 'L1', L2: 'L2', R1: 'R1', R2: 'R2' };
const GamepadDiagram = () => {
const { t } = useTranslation();
@ -79,12 +80,12 @@ const GamepadDiagram = () => {
d={`M${CX - SX - 38},68 Q${CX - SX - 40},48 ${CX - SX - 28},42 L${CX - SX + 28},42 Q${CX - SX + 40},48 ${CX - SX + 38},68 Z`}
fill={'url(#triggerGrad)'} stroke={'#3d3660'} strokeWidth={'1'} opacity={'0.7'}
/>
<text x={CX - SX} y={'58'} textAnchor={'middle'} fill={'#8b7faa'} fontSize={'8'} fontWeight={'500'}>L2</text>
<text x={CX - SX} y={'58'} textAnchor={'middle'} fill={'#8b7faa'} fontSize={'8'} fontWeight={'500'}>{BTN.L2}</text>
<path
d={`M${CX + SX - 38},68 Q${CX + SX - 40},48 ${CX + SX - 28},42 L${CX + SX + 28},42 Q${CX + SX + 40},48 ${CX + SX + 38},68 Z`}
fill={'url(#triggerGrad)'} stroke={'#3d3660'} strokeWidth={'1'} opacity={'0.7'}
/>
<text x={CX + SX} y={'58'} textAnchor={'middle'} fill={'#8b7faa'} fontSize={'8'} fontWeight={'500'}>R2</text>
<text x={CX + SX} y={'58'} textAnchor={'middle'} fill={'#8b7faa'} fontSize={'8'} fontWeight={'500'}>{BTN.R2}</text>
{/* ===== CONTROLLER BODY ===== */}
<path
@ -120,14 +121,14 @@ const GamepadDiagram = () => {
d={`M${CX - SX - 40},74 Q${CX - SX - 38},66 ${CX - SX - 30},64 L${CX - SX + 30},64 Q${CX - SX + 38},66 ${CX - SX + 40},74 L${CX - SX + 36},82 Q${CX - SX + 34},85 ${CX - SX + 28},85 L${CX - SX - 28},85 Q${CX - SX - 34},85 ${CX - SX - 36},82 Z`}
fill={'url(#bumperGrad)'} stroke={glow('l1') || '#5848a0'} strokeWidth={'1.2'} opacity={glowOp('l1') || 0.9}
/>
<text x={CX - SX} y={'78'} textAnchor={'middle'} fill={'#a89ecc'} fontSize={'9'} fontWeight={'600'}>L1</text>
<text x={CX - SX} y={'78'} textAnchor={'middle'} fill={'#a89ecc'} fontSize={'9'} fontWeight={'600'}>{BTN.L1}</text>
</g>
<g filter={active === 'r1' ? 'url(#glow)' : undefined}>
<path
d={`M${CX + SX - 40},74 Q${CX + SX - 38},66 ${CX + SX - 30},64 L${CX + SX + 30},64 Q${CX + SX + 38},66 ${CX + SX + 40},74 L${CX + SX + 36},82 Q${CX + SX + 34},85 ${CX + SX + 28},85 L${CX + SX - 28},85 Q${CX + SX - 34},85 ${CX + SX - 36},82 Z`}
fill={'url(#bumperGrad)'} stroke={glow('r1') || '#5848a0'} strokeWidth={'1.2'} opacity={glowOp('r1') || 0.9}
/>
<text x={CX + SX} y={'78'} textAnchor={'middle'} fill={'#a89ecc'} fontSize={'9'} fontWeight={'600'}>R1</text>
<text x={CX + SX} y={'78'} textAnchor={'middle'} fill={'#a89ecc'} fontSize={'9'} fontWeight={'600'}>{BTN.R1}</text>
</g>
{/* ===== FACE BUTTONS (right, centered at CX+BX) ===== */}

View file

@ -9,6 +9,18 @@ import { useGamepad } from 'stremio/services';
import GamepadDiagram from './GamepadDiagram';
import styles from './styles.less';
const CROSS = '✕';
const CIRCLE = '○';
const TRIANGLE = '△';
const SQUARE = '□';
const L_STICK = 'L stick';
const L1 = 'L1';
const R1 = 'R1';
const LEFT = '←';
const RIGHT = '→';
const UP = '↑';
const DOWN = '↓';
type Props = {
onClose: () => void,
};
@ -52,37 +64,37 @@ const GamepadModal = ({ onClose }: Props) => {
<div className={styles['section']}>
<div className={styles['section-title']}>{t('GAMEPAD_SECTION_NAVIGATION')}</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L stick</kbd>
<kbd className={styles['kbd']}>{L_STICK}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_NAVIGATE')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}></kbd>
<kbd className={styles['kbd']}>{CROSS}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_SELECT')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}></kbd>
<kbd className={styles['kbd']}>{CIRCLE}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_BACK')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}></kbd>
<kbd className={styles['kbd']}>{TRIANGLE}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_FULLSCREEN')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}></kbd>
<kbd className={styles['kbd']}>{SQUARE}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_GUIDE')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L1</kbd>
<kbd className={styles['kbd']}>{L1}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_PREV_TAB')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>R1</kbd>
<kbd className={styles['kbd']}>{R1}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_NEXT_TAB')}</span>
</div>
@ -91,28 +103,28 @@ const GamepadModal = ({ onClose }: Props) => {
<div className={styles['section']}>
<div className={styles['section-title']}>{t('GAMEPAD_SECTION_PLAYER')}</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}></kbd>
<kbd className={styles['kbd']}>{CROSS}</kbd>
<span className={styles['dir']} />
<span className={styles['action']}>{t('GAMEPAD_ACTION_PLAY_PAUSE')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L stick</kbd>
<span className={styles['dir']}>{'←'}</span>
<kbd className={styles['kbd']}>{L_STICK}</kbd>
<span className={styles['dir']}>{LEFT}</span>
<span className={styles['action']}>{t('GAMEPAD_ACTION_SEEK_BACK')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L stick</kbd>
<span className={styles['dir']}>{'→'}</span>
<kbd className={styles['kbd']}>{L_STICK}</kbd>
<span className={styles['dir']}>{RIGHT}</span>
<span className={styles['action']}>{t('GAMEPAD_ACTION_SEEK_FWD')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L stick</kbd>
<span className={styles['dir']}>{'↑'}</span>
<kbd className={styles['kbd']}>{L_STICK}</kbd>
<span className={styles['dir']}>{UP}</span>
<span className={styles['action']}>{t('GAMEPAD_ACTION_VOL_UP')}</span>
</div>
<div className={styles['mapping']}>
<kbd className={styles['kbd']}>L stick</kbd>
<span className={styles['dir']}>{'↓'}</span>
<kbd className={styles['kbd']}>{L_STICK}</kbd>
<span className={styles['dir']}>{DOWN}</span>
<span className={styles['action']}>{t('GAMEPAD_ACTION_VOL_DOWN')}</span>
</div>
</div>

View file

@ -52,37 +52,35 @@ const GamepadProvider: React.FC<{
}
};
const onGamepadConnected = () => {
const onGamepadConnected = useCallback(() => {
// @ts-expect-error show() expects no arguments
toast.show({
type: 'info',
title: t('GAMEPAD_CONNECTED'),
timeout: 4000,
});
};
}, [toast, t]);
const onGamepadDisconnected = () => {
const onGamepadDisconnected = useCallback(() => {
// @ts-expect-error show() expects no arguments
toast.show({
type: 'info',
title: t('GAMEPAD_DISCONNECTED'),
timeout: 4000,
});
};
}, [toast, t]);
useEffect(() => {
if (enabled) {
window.addEventListener('gamepadconnected', onGamepadConnected);
window.addEventListener('gamepaddisconnected', onGamepadDisconnected);
}
if (!enabled) return;
window.addEventListener('gamepadconnected', onGamepadConnected);
window.addEventListener('gamepaddisconnected', onGamepadDisconnected);
return () => {
if (enabled) {
window.removeEventListener('gamepadconnected', onGamepadConnected);
window.removeEventListener('gamepaddisconnected', onGamepadDisconnected);
}
window.removeEventListener('gamepadconnected', onGamepadConnected);
window.removeEventListener('gamepaddisconnected', onGamepadDisconnected);
};
}, [enabled]);
}, [enabled, onGamepadConnected, onGamepadDisconnected]);
useEffect(() => {
if (onGuide) {

View file

@ -103,8 +103,8 @@ const useContentGamepadNavigation = (
elements[0].focus();
return;
}
const isActiveSelectElement = [activeElement.classList].some((className) => /^select-input/.test(className.toString()));
if (!isActiveSelectElement) {
const isSelect = Array.from(activeElement.classList).some((cls) => cls.startsWith('select-input'));
if (!isSelect) {
activeElement?.click();
}
};