mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-05 20:59:49 +00:00
fix(GamepadSupport): fix issue with multiple handler events being fired
This commit is contained in:
parent
b36ebcb4c6
commit
41865276d5
3 changed files with 56 additions and 20 deletions
|
|
@ -29,6 +29,8 @@ const useVideo = require('./useVideo');
|
|||
const styles = require('./styles');
|
||||
const Video = require('./Video');
|
||||
|
||||
const GAMEPAD_HANDLER_ID = 'player';
|
||||
|
||||
const Player = ({ urlParams, queryParams }) => {
|
||||
const { t } = useTranslation();
|
||||
const { chromecast, shell, core } = useServices();
|
||||
|
|
@ -344,12 +346,12 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
}, [onSeekPrev, onSeekNext, onVolumeUp, onVolumeDown]);
|
||||
|
||||
React.useEffect(() => {
|
||||
gamepad.on('buttonA', onPlayPause);
|
||||
gamepad.on('analog', onGamepadSeekAndVol);
|
||||
gamepad.on('buttonA', GAMEPAD_HANDLER_ID, onPlayPause);
|
||||
gamepad.on('analog', GAMEPAD_HANDLER_ID, onGamepadSeekAndVol);
|
||||
|
||||
return () => {
|
||||
gamepad.off('buttonA', onPlayPause);
|
||||
gamepad.off('analog', onGamepadSeekAndVol);
|
||||
gamepad.off('buttonA', GAMEPAD_HANDLER_ID);
|
||||
gamepad.off('analog', GAMEPAD_HANDLER_ID);
|
||||
};
|
||||
}, [onPlayPause, onGamepadSeekAndVol]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { createContext } from 'react';
|
||||
|
||||
const GamepadContext = createContext<{
|
||||
on: (event: string, callback: (data?: any) => void) => void;
|
||||
off: (event: string, callback: (data?: any) => void) => void;
|
||||
on: (event: string, id: string, callback: (data?: any) => void) => void;
|
||||
off: (event: string, id: string) => void;
|
||||
} | null>(null);
|
||||
|
||||
export default GamepadContext;
|
||||
|
|
|
|||
|
|
@ -1,39 +1,73 @@
|
|||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import useToast from 'stremio/common/Toast/useToast';
|
||||
import GamepadContext from './GamepadContext';
|
||||
|
||||
type GamepadEventHandlers = Record<string, ((data?: any) => void)[]>;
|
||||
type GamepadEventHandlers = Map<string, Map<string, (data?: any) => void>>;
|
||||
|
||||
const GamepadProvider: React.FC<{
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}> = ({ enabled, children }) => {
|
||||
const toast = useToast();
|
||||
const [connectedGamepads, setConnectedGamepads] = useState<number>(0);
|
||||
const lastButtonState = useRef<number[]>([]);
|
||||
const lastButtonPressedTime = useRef<number>(0);
|
||||
const axisTimer = useRef<number>(0);
|
||||
const eventHandlers = useRef<GamepadEventHandlers>({});
|
||||
const eventHandlers = useRef<GamepadEventHandlers>(new Map());
|
||||
|
||||
const on = useCallback((event: string, callback: (data?: any) => void) => {
|
||||
if (!eventHandlers.current[event]) {
|
||||
eventHandlers.current[event] = [];
|
||||
const on = useCallback((event: string, id: string, callback: (data?: any) => void) => {
|
||||
if (!eventHandlers.current.has(event)) {
|
||||
eventHandlers.current.set(event, new Map());
|
||||
}
|
||||
eventHandlers.current[event].push(callback);
|
||||
|
||||
const handlers = eventHandlers.current.get(event)!;
|
||||
|
||||
// Ensure only one handler per component
|
||||
handlers.set(id, callback);
|
||||
}, []);
|
||||
|
||||
const off = useCallback((event: string, callback: (data?: any) => void) => {
|
||||
if (eventHandlers.current[event]) {
|
||||
eventHandlers.current[event] = eventHandlers.current[event].filter(
|
||||
(cb) => cb !== callback
|
||||
);
|
||||
}
|
||||
const off = useCallback((event: string, id: string) => {
|
||||
eventHandlers.current.get(event)?.delete(id);
|
||||
}, []);
|
||||
|
||||
const emit = (event: string, data?: any) => {
|
||||
if (eventHandlers.current[event]) {
|
||||
eventHandlers.current[event].forEach((callback) => callback(data));
|
||||
if (eventHandlers.current.has(event)) {
|
||||
eventHandlers.current.get(event)!.forEach((callback) => callback(data));
|
||||
}
|
||||
};
|
||||
|
||||
const onGamepadConnected = () => {
|
||||
// @ts-ignore
|
||||
toast.show({
|
||||
type: 'info',
|
||||
title: 'Gamepad detected',
|
||||
timeout: 4000,
|
||||
});
|
||||
};
|
||||
|
||||
const onGamepadDisconnected = () => {
|
||||
// @ts-ignore
|
||||
toast.show({
|
||||
type: 'info',
|
||||
title: 'Gamepad disconnected',
|
||||
timeout: 4000,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
window.addEventListener('gamepadconnected', onGamepadConnected);
|
||||
window.addEventListener('gamepaddisconnected', onGamepadDisconnected);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (enabled) {
|
||||
window.removeEventListener('gamepadconnected', onGamepadConnected);
|
||||
window.removeEventListener('gamepaddisconnected', onGamepadDisconnected);
|
||||
}
|
||||
};
|
||||
}, [enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof navigator.getGamepads !== 'function') return;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue