Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e616eec246 | |||
| 205678b3d7 | |||
| f41430984c | |||
| 7da9ab00b1 | |||
| 2ab0457568 |
7 changed files with 677 additions and 438 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "has",
|
"name": "has",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.15",
|
"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.15"
|
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.15",
|
"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.15");
|
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);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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