feat(release): v0.0.19 - fix PC keyboard R key triggering TV navigation drawer, disable TV mode auto-activation on desktop
This commit is contained in:
parent
205678b3d7
commit
c4590ca3b5
7 changed files with 97 additions and 75 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "has",
|
"name": "has",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.18",
|
"version": "0.0.19",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "has"
|
name = "has"
|
||||||
version = "0.0.18"
|
version = "0.0.19"
|
||||||
description = "has"
|
description = "has"
|
||||||
authors = ["autli"]
|
authors = ["autli"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "has",
|
"productName": "has",
|
||||||
"version": "0.0.18",
|
"version": "0.0.19",
|
||||||
"identifier": "com.has.tauri-app",
|
"identifier": "com.has.tauri-app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|
|
||||||
31
src/App.tsx
31
src/App.tsx
|
|
@ -27,8 +27,6 @@ const isTauri = () => {
|
||||||
// Helper for Smart TV / Android TV detection
|
// Helper for Smart TV / Android TV detection
|
||||||
const detectTvEnvironment = (): boolean => {
|
const detectTvEnvironment = (): boolean => {
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
const saved = localStorage.getItem("has_tv_mode");
|
|
||||||
if (saved !== null) return saved === "true";
|
|
||||||
const ua = (navigator.userAgent || "").toLowerCase();
|
const ua = (navigator.userAgent || "").toLowerCase();
|
||||||
const isTvUa =
|
const isTvUa =
|
||||||
ua.includes("android tv") ||
|
ua.includes("android tv") ||
|
||||||
|
|
@ -55,6 +53,17 @@ const detectTvEnvironment = (): boolean => {
|
||||||
const isAndroid = ua.includes("android");
|
const isAndroid = ua.includes("android");
|
||||||
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
||||||
if (isAndroid && noTouch) return true;
|
if (isAndroid && noTouch) return true;
|
||||||
|
|
||||||
|
// On pure desktop (macOS / Windows / Desktop Linux), default to desktop PC mode
|
||||||
|
const isDesktop =
|
||||||
|
(ua.includes("windows nt") || ua.includes("macintosh") || (ua.includes("linux") && !ua.includes("android"))) &&
|
||||||
|
!isTvUa;
|
||||||
|
if (isDesktop) {
|
||||||
|
return localStorage.getItem("has_tv_mode_manual") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
const saved = localStorage.getItem("has_tv_mode");
|
||||||
|
if (saved !== null) return saved === "true";
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -144,21 +153,6 @@ export default function App() {
|
||||||
checkDatabase();
|
checkDatabase();
|
||||||
}, [isTvEnvironment]);
|
}, [isTvEnvironment]);
|
||||||
|
|
||||||
// Activate TV mode immediately upon any remote control key press
|
|
||||||
useEffect(() => {
|
|
||||||
const handleRemoteKey = (e: KeyboardEvent) => {
|
|
||||||
const isRemoteKey =
|
|
||||||
[19, 20, 21, 22, 23, 4, 82, 85, 179].includes(e.keyCode) ||
|
|
||||||
["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key);
|
|
||||||
if (isRemoteKey && !isTvEnvironment) {
|
|
||||||
setIsTvEnvironment(true);
|
|
||||||
localStorage.setItem("has_tv_mode", "true");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener("keydown", handleRemoteKey);
|
|
||||||
return () => window.removeEventListener("keydown", handleRemoteKey);
|
|
||||||
}, [isTvEnvironment]);
|
|
||||||
|
|
||||||
// Countdown timer for OTP re-send in resetStage 2
|
// Countdown timer for OTP re-send in resetStage 2
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (resetStage !== 2) return;
|
if (resetStage !== 2) return;
|
||||||
|
|
@ -525,6 +519,7 @@ export default function App() {
|
||||||
dbInfo={dbInfo}
|
dbInfo={dbInfo}
|
||||||
dbStatus={dbStatus}
|
dbStatus={dbStatus}
|
||||||
onLogout={handleLogout}
|
onLogout={handleLogout}
|
||||||
|
isTvEnvironment={isTvEnvironment}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
@ -542,6 +537,7 @@ export default function App() {
|
||||||
onSwitchToDesktop={() => {
|
onSwitchToDesktop={() => {
|
||||||
setIsTvEnvironment(false);
|
setIsTvEnvironment(false);
|
||||||
localStorage.setItem("has_tv_mode", "false");
|
localStorage.setItem("has_tv_mode", "false");
|
||||||
|
localStorage.setItem("has_tv_mode_manual", "false");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
@ -812,6 +808,7 @@ export default function App() {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsTvEnvironment(true);
|
setIsTvEnvironment(true);
|
||||||
localStorage.setItem("has_tv_mode", "true");
|
localStorage.setItem("has_tv_mode", "true");
|
||||||
|
localStorage.setItem("has_tv_mode_manual", "true");
|
||||||
}}
|
}}
|
||||||
title="Перейти в режим Android TV (выбор профилей)"
|
title="Перейти в режим Android TV (выбор профилей)"
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ interface UpdaterBannerProps {
|
||||||
// Helper for Smart TV / Android TV detection
|
// Helper for Smart TV / Android TV detection
|
||||||
const detectTvEnvironment = (): boolean => {
|
const detectTvEnvironment = (): boolean => {
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
const saved = localStorage.getItem("has_tv_mode");
|
|
||||||
if (saved !== null) return saved === "true";
|
|
||||||
const ua = (navigator.userAgent || "").toLowerCase();
|
const ua = (navigator.userAgent || "").toLowerCase();
|
||||||
const isTvUa =
|
const isTvUa =
|
||||||
ua.includes("android tv") ||
|
ua.includes("android tv") ||
|
||||||
|
|
@ -50,6 +48,17 @@ const detectTvEnvironment = (): boolean => {
|
||||||
const isAndroid = ua.includes("android");
|
const isAndroid = ua.includes("android");
|
||||||
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
||||||
if (isAndroid && noTouch) return true;
|
if (isAndroid && noTouch) return true;
|
||||||
|
|
||||||
|
// On pure desktop (macOS / Windows / Desktop Linux), default to desktop PC mode
|
||||||
|
const isDesktop =
|
||||||
|
(ua.includes("windows nt") || ua.includes("macintosh") || (ua.includes("linux") && !ua.includes("android"))) &&
|
||||||
|
!isTvUa;
|
||||||
|
if (isDesktop) {
|
||||||
|
return localStorage.getItem("has_tv_mode_manual") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
const saved = localStorage.getItem("has_tv_mode");
|
||||||
|
if (saved !== null) return saved === "true";
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -88,7 +97,7 @@ const isNewerSemver = (remote: string, current: string): boolean => {
|
||||||
|
|
||||||
export function UpdaterBanner({ isTvEnvironment }: UpdaterBannerProps = {}) {
|
export function UpdaterBanner({ isTvEnvironment }: UpdaterBannerProps = {}) {
|
||||||
const [isTv, setIsTv] = useState<boolean>(() => isTvEnvironment ?? detectTvEnvironment());
|
const [isTv, setIsTv] = useState<boolean>(() => isTvEnvironment ?? detectTvEnvironment());
|
||||||
const [currentAppVersion, setCurrentAppVersion] = useState<string>("0.0.18");
|
const [currentAppVersion, setCurrentAppVersion] = useState<string>("0.0.19");
|
||||||
const [updateAvailable, setUpdateAvailable] = useState(false);
|
const [updateAvailable, setUpdateAvailable] = useState(false);
|
||||||
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
|
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
|
||||||
const [downloading, setDownloading] = useState(false);
|
const [downloading, setDownloading] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -493,8 +493,6 @@ const formatMediaImg = (path: string | undefined, size: "w500" | "original" = "w
|
||||||
// Detect Android TV / Smart TV user agent or previous user preference
|
// Detect Android TV / Smart TV user agent or previous user preference
|
||||||
const detectTvEnvironment = (): boolean => {
|
const detectTvEnvironment = (): boolean => {
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
const saved = localStorage.getItem("has_tv_mode");
|
|
||||||
if (saved !== null) return saved === "true";
|
|
||||||
const ua = (navigator.userAgent || "").toLowerCase();
|
const ua = (navigator.userAgent || "").toLowerCase();
|
||||||
const isTvUa =
|
const isTvUa =
|
||||||
ua.includes("android tv") ||
|
ua.includes("android tv") ||
|
||||||
|
|
@ -521,6 +519,17 @@ const detectTvEnvironment = (): boolean => {
|
||||||
const isAndroid = ua.includes("android");
|
const isAndroid = ua.includes("android");
|
||||||
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
||||||
if (isAndroid && noTouch) return true;
|
if (isAndroid && noTouch) return true;
|
||||||
|
|
||||||
|
// On pure desktop (macOS / Windows / Desktop Linux), default to desktop PC mode
|
||||||
|
const isDesktop =
|
||||||
|
(ua.includes("windows nt") || ua.includes("macintosh") || (ua.includes("linux") && !ua.includes("android"))) &&
|
||||||
|
!isTvUa;
|
||||||
|
if (isDesktop) {
|
||||||
|
return localStorage.getItem("has_tv_mode_manual") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
const saved = localStorage.getItem("has_tv_mode");
|
||||||
|
if (saved !== null) return saved === "true";
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -540,10 +549,10 @@ export default function Cinema({
|
||||||
const lastFocusedCardRef = useRef<HTMLElement | null>(null);
|
const lastFocusedCardRef = useRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isTvEnvironment && !isTvMode) {
|
if (isTvEnvironment !== isTvMode) {
|
||||||
setIsTvMode(true);
|
setIsTvMode(isTvEnvironment);
|
||||||
}
|
}
|
||||||
}, [isTvEnvironment, isTvMode]);
|
}, [isTvEnvironment]);
|
||||||
const [tvToast, setTvToast] = useState<{ text: string; icon?: string } | null>(null);
|
const [tvToast, setTvToast] = useState<{ text: string; icon?: string } | null>(null);
|
||||||
const tvToastTimerRef = useRef<any>(null);
|
const tvToastTimerRef = useRef<any>(null);
|
||||||
|
|
||||||
|
|
@ -563,6 +572,7 @@ export default function Cinema({
|
||||||
setIsTvMode((prev) => {
|
setIsTvMode((prev) => {
|
||||||
const next = !prev;
|
const next = !prev;
|
||||||
localStorage.setItem("has_tv_mode", String(next));
|
localStorage.setItem("has_tv_mode", String(next));
|
||||||
|
localStorage.setItem("has_tv_mode_manual", String(next));
|
||||||
showTvToast(next ? "ТВ-Режим включен (10-Foot UI)" : "ПК-Режим включен", "📺", 2500);
|
showTvToast(next ? "ТВ-Режим включен (10-Foot UI)" : "ПК-Режим включен", "📺", 2500);
|
||||||
playTvBeep(next ? 750 : 400, 0.05);
|
playTvBeep(next ? 750 : 400, 0.05);
|
||||||
return next;
|
return next;
|
||||||
|
|
@ -2250,40 +2260,18 @@ export default function Cinema({
|
||||||
// D-Pad Spatial Navigation & TV Remote Key Handler
|
// D-Pad Spatial Navigation & TV Remote Key Handler
|
||||||
const handleRemoteKeyDown = useCallback(
|
const handleRemoteKeyDown = useCallback(
|
||||||
(e: KeyboardEvent) => {
|
(e: KeyboardEvent) => {
|
||||||
// Auto-activate TV mode immediately on ANY remote control button press
|
|
||||||
const isRemoteKey =
|
|
||||||
[19, 20, 21, 22, 23, 4, 82, 85, 179].includes(e.keyCode) ||
|
|
||||||
["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key);
|
|
||||||
if (isRemoteKey && !isTvMode) {
|
|
||||||
setIsTvMode(true);
|
|
||||||
localStorage.setItem("has_tv_mode", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
const isInput =
|
const isInput =
|
||||||
document.activeElement instanceof HTMLInputElement ||
|
document.activeElement instanceof HTMLInputElement ||
|
||||||
document.activeElement instanceof HTMLTextAreaElement;
|
document.activeElement instanceof HTMLTextAreaElement;
|
||||||
|
|
||||||
// Android TV / Remote key mappings
|
// Remote Menu key (ONLY in TV Mode, and NEVER for the 'R' key on PC keyboard!)
|
||||||
const isBack =
|
const isMenuKey =
|
||||||
e.key === "Escape" ||
|
!isInput &&
|
||||||
e.key === "GoBack" ||
|
(e.key === "Menu" ||
|
||||||
e.key === "BrowserBack" ||
|
e.key === "ContextMenu" ||
|
||||||
e.keyCode === 4 ||
|
(isTvMode && e.keyCode === 82 && e.key !== "r" && e.key !== "R" && e.code !== "KeyR"));
|
||||||
e.keyCode === 27;
|
|
||||||
const isOk =
|
|
||||||
e.key === "Enter" ||
|
|
||||||
e.key === "Select" ||
|
|
||||||
e.key === "Accept" ||
|
|
||||||
e.keyCode === 13 ||
|
|
||||||
e.keyCode === 23 ||
|
|
||||||
e.keyCode === 66;
|
|
||||||
const isPlayPause =
|
|
||||||
e.key === "MediaPlayPause" || e.keyCode === 85 || e.keyCode === 179;
|
|
||||||
const isFastForward = e.key === "MediaFastForward" || e.keyCode === 90;
|
|
||||||
const isRewind = e.key === "MediaRewind" || e.keyCode === 89;
|
|
||||||
|
|
||||||
// Remote Menu key (Android TV remote Menu button / ContextMenu)
|
if (isTvMode && isMenuKey) {
|
||||||
if (e.key === "Menu" || e.keyCode === 82 || e.key === "ContextMenu") {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsTvNavOpen((prev) => {
|
setIsTvNavOpen((prev) => {
|
||||||
const next = !prev;
|
const next = !prev;
|
||||||
|
|
@ -2310,6 +2298,29 @@ export default function Cinema({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Android TV / Remote key mappings
|
||||||
|
const isBack =
|
||||||
|
e.key === "Escape" ||
|
||||||
|
e.key === "GoBack" ||
|
||||||
|
e.key === "BrowserBack" ||
|
||||||
|
(isTvMode && e.keyCode === 4) ||
|
||||||
|
e.keyCode === 27;
|
||||||
|
const isOk =
|
||||||
|
e.key === "Enter" ||
|
||||||
|
e.key === "Select" ||
|
||||||
|
e.key === "Accept" ||
|
||||||
|
e.keyCode === 13 ||
|
||||||
|
(isTvMode && (e.keyCode === 23 || (e.keyCode === 66 && e.code !== "KeyB")));
|
||||||
|
const isPlayPause =
|
||||||
|
e.key === "MediaPlayPause" ||
|
||||||
|
(isTvMode && (e.keyCode === 85 || e.keyCode === 179) && e.code !== "KeyU");
|
||||||
|
const isFastForward =
|
||||||
|
e.key === "MediaFastForward" ||
|
||||||
|
(isTvMode && e.keyCode === 90 && e.code !== "KeyZ");
|
||||||
|
const isRewind =
|
||||||
|
e.key === "MediaRewind" ||
|
||||||
|
(isTvMode && e.keyCode === 89 && e.code !== "KeyY");
|
||||||
|
|
||||||
// 1. In-player remote control
|
// 1. In-player remote control
|
||||||
if (playingEpisode !== null) {
|
if (playingEpisode !== null) {
|
||||||
resetHudTimer();
|
resetHudTimer();
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ interface DashboardProps {
|
||||||
dbInfo: SafeDbInfo | null;
|
dbInfo: SafeDbInfo | null;
|
||||||
dbStatus: DbStatus | null;
|
dbStatus: DbStatus | null;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
|
isTvEnvironment?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type NavTab = "cinema" | "home" | "users" | "settings" | "profile" | "help";
|
type NavTab = "cinema" | "home" | "users" | "settings" | "profile" | "help";
|
||||||
|
|
@ -62,8 +63,6 @@ type NavTab = "cinema" | "home" | "users" | "settings" | "profile" | "help";
|
||||||
// Helper for Smart TV / Android TV detection
|
// Helper for Smart TV / Android TV detection
|
||||||
const detectTvEnvironment = (): boolean => {
|
const detectTvEnvironment = (): boolean => {
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
const saved = localStorage.getItem("has_tv_mode");
|
|
||||||
if (saved !== null) return saved === "true";
|
|
||||||
const ua = (navigator.userAgent || "").toLowerCase();
|
const ua = (navigator.userAgent || "").toLowerCase();
|
||||||
const isTvUa =
|
const isTvUa =
|
||||||
ua.includes("android tv") ||
|
ua.includes("android tv") ||
|
||||||
|
|
@ -90,6 +89,17 @@ const detectTvEnvironment = (): boolean => {
|
||||||
const isAndroid = ua.includes("android");
|
const isAndroid = ua.includes("android");
|
||||||
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
const noTouch = !("ontouchstart" in window) && navigator.maxTouchPoints === 0;
|
||||||
if (isAndroid && noTouch) return true;
|
if (isAndroid && noTouch) return true;
|
||||||
|
|
||||||
|
// On pure desktop (macOS / Windows / Desktop Linux), default to desktop PC mode
|
||||||
|
const isDesktop =
|
||||||
|
(ua.includes("windows nt") || ua.includes("macintosh") || (ua.includes("linux") && !ua.includes("android"))) &&
|
||||||
|
!isTvUa;
|
||||||
|
if (isDesktop) {
|
||||||
|
return localStorage.getItem("has_tv_mode_manual") === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
const saved = localStorage.getItem("has_tv_mode");
|
||||||
|
if (saved !== null) return saved === "true";
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -98,13 +108,23 @@ export default function Dashboard({
|
||||||
dbInfo,
|
dbInfo,
|
||||||
dbStatus,
|
dbStatus,
|
||||||
onLogout,
|
onLogout,
|
||||||
|
isTvEnvironment: propIsTv,
|
||||||
}: DashboardProps) {
|
}: DashboardProps) {
|
||||||
const [currentUser, setCurrentUser] = useState<UserDto>(user);
|
const [currentUser, setCurrentUser] = useState<UserDto>(user);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentUser(user);
|
setCurrentUser(user);
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const [isTvEnvironment, setIsTvEnvironment] = useState<boolean>(detectTvEnvironment);
|
const [isTvEnvironment, setIsTvEnvironment] = useState<boolean>(() => {
|
||||||
|
return propIsTv !== undefined ? propIsTv : detectTvEnvironment();
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (propIsTv !== undefined && propIsTv !== isTvEnvironment) {
|
||||||
|
setIsTvEnvironment(propIsTv);
|
||||||
|
}
|
||||||
|
}, [propIsTv, isTvEnvironment]);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<NavTab>("cinema");
|
const [activeTab, setActiveTab] = useState<NavTab>("cinema");
|
||||||
const [serverUsers, setServerUsers] = useState<ServerUserDto[]>([]);
|
const [serverUsers, setServerUsers] = useState<ServerUserDto[]>([]);
|
||||||
const [loadingUsers, setLoadingUsers] = useState(false);
|
const [loadingUsers, setLoadingUsers] = useState(false);
|
||||||
|
|
@ -113,21 +133,6 @@ export default function Dashboard({
|
||||||
});
|
});
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
|
||||||
// Dynamic D-Pad Remote Control Key Listener (Auto-detects TV when user presses remote buttons)
|
|
||||||
useEffect(() => {
|
|
||||||
const handleRemoteDpadKey = (e: KeyboardEvent) => {
|
|
||||||
const isRemoteKey =
|
|
||||||
[19, 20, 21, 22, 23, 4, 82, 85, 179].includes(e.keyCode) ||
|
|
||||||
["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key);
|
|
||||||
if (isRemoteKey && !isTvEnvironment) {
|
|
||||||
setIsTvEnvironment(true);
|
|
||||||
localStorage.setItem("has_tv_mode", "true");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener("keydown", handleRemoteDpadKey);
|
|
||||||
return () => window.removeEventListener("keydown", handleRemoteDpadKey);
|
|
||||||
}, [isTvEnvironment]);
|
|
||||||
|
|
||||||
const handleNavClick = (tab: NavTab) => {
|
const handleNavClick = (tab: NavTab) => {
|
||||||
setActiveTab(tab);
|
setActiveTab(tab);
|
||||||
setMobileMenuOpen(false);
|
setMobileMenuOpen(false);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue