import React, { useState, useEffect, useRef } from 'react' import Settings from './Settings' import { AppEntry } from './Settings' interface HeaderProps { activeApp: string setActiveApp: (name: string) => void onAppsChange: (apps: AppEntry[]) => void onMovieSearch: (query: string) => void onBookmark: (title: string, url: string, poster: string, source: string, siteIcon?: string) => void onBookmarkRemove: (index: number) => void bookmarks: import('./Settings').Bookmark[] openedFromSearch?: boolean onBackToSearch?: () => void } const Header: React.FC = ({ activeApp, setActiveApp, onAppsChange, onMovieSearch, onBookmark, onBookmarkRemove, bookmarks, openedFromSearch, onBackToSearch }) => { const [isCollapsed, setIsCollapsed] = useState(false) const [isHovered, setIsHovered] = useState(false) const [leftDisabled, setLeftDisabled] = useState(true) const [rightDisabled, setRightDisabled] = useState(true) const [refreshDisabled, setRefreshDisabled] = useState(true) const [showSettings, setShowSettings] = useState(false) const [currentUrl, setCurrentUrl] = useState('') const timeoutRef = useRef | null>(null) const activeAppRef = useRef(activeApp) useEffect(() => { activeAppRef.current = activeApp }, [activeApp]) useEffect(() => { if (!window.electron) return const offCloseApp = window.electron.on('closeApp', () => { window.electron!.removeView(activeAppRef.current) setIsCollapsed(false) setActiveApp('home') }) const offWebButtons = window.electron.on('updateWebButtons', (app: { historyPosition: number; history: string[] }) => { setLeftDisabled(app.historyPosition === 0) setRightDisabled(app.historyPosition === app.history.length - 1) setRefreshDisabled(false) setCurrentUrl(app.history[app.historyPosition] || '') }) return () => { offCloseApp(); offWebButtons() } }, [setActiveApp]) const closeCurrentApp = () => { window.electron?.confirm('Закрыть приложение?', 'closeApp') } const openSettings = () => { if (appOpen) window.electron?.hideView() setShowSettings(true) } const closeSettings = () => { setShowSettings(false) if (appOpen) window.electron?.showView(activeApp) } const toggleCollapse = () => { if (isCollapsed) { setIsCollapsed(false) setIsHovered(false) window.electron?.expandWithHeader() } else { setIsCollapsed(true) window.electron?.collapseWithHeader() } } const handleMouseEnter = () => { if (timeoutRef.current) clearTimeout(timeoutRef.current) timeoutRef.current = setTimeout(() => { if (isCollapsed) { setIsHovered(true) window.electron?.expandWithHeader() } }, 150) } const handleMouseLeave = () => { if (timeoutRef.current) clearTimeout(timeoutRef.current) timeoutRef.current = setTimeout(() => { if (isCollapsed) { setIsHovered(false) window.electron?.collapseWithHeader() } }, 150) } const backwardPage = () => { setLeftDisabled(true) setRightDisabled(true) setRefreshDisabled(true) window.electron?.backwardPage() } const forwardPage = () => { setLeftDisabled(true) setRightDisabled(true) setRefreshDisabled(true) window.electron?.forwardPage() } const refreshPage = () => { setRefreshDisabled(true) window.electron?.refreshPage() } const appOpen = activeApp !== 'home' && activeApp !== 'movie-search' const showSearchIcon = activeApp === 'home' || activeApp === 'movie-search' const [isKiosk, setIsKiosk] = useState(true) type UpdateStatus = | { state: 'available'; version: string; currentVersion?: string } | { state: 'downloading'; percent: number; bytesPerSecond?: number; transferred?: number; total?: number; version?: string; currentVersion?: string } | { state: 'ready'; version: string; currentVersion?: string } | { state: 'manual'; version: string; currentVersion?: string; installerUrl: string; installerName?: string } | { state: 'error'; message: string } const [updateStatus, setUpdateStatus] = useState(null) useEffect(() => { if (!window.electron) return const off = window.electron.on('update-status', (info: UpdateStatus) => { setUpdateStatus(prev => { // Preserve version across state transitions when payload omits it (download-progress) if (info.state === 'downloading' && prev && 'version' in prev && prev.version) { return { ...info, version: info.version || prev.version, currentVersion: info.currentVersion || ('currentVersion' in prev ? prev.currentVersion : undefined) } } return info }) }) return off }, []) useEffect(() => { window.electron?.isKiosk().then(k => setIsKiosk(k)) }, []) const toggleKiosk = () => { window.electron?.toggleKiosk().then((newState: boolean) => setIsKiosk(newState)) } const [isBookmarked, setIsBookmarked] = useState(false) const handleBookmark = async () => { const page = await window.electron?.getCurrentPage() if (!page) return let pageHost = '' try { pageHost = new URL(page.url).hostname } catch (_) {} // Match by full URL — different movies on same site must not collide. const existingIdx = bookmarks.findIndex(b => b.url === page.url) if (existingIdx !== -1) { onBookmarkRemove(existingIdx) setIsBookmarked(false) return } // Pull og:image / JSON-LD poster from the live page (specific to this movie). const meta = await window.electron?.getPageMeta?.().catch(() => null) const poster = meta?.poster || '' const title = (meta?.title || page.name || '').trim() || page.name onBookmark(title, page.url, poster, pageHost, page.imageUrl || '') setIsBookmarked(true) } useEffect(() => { if (!appOpen || !currentUrl) { setIsBookmarked(false); return } setIsBookmarked(bookmarks.some(b => b.url === currentUrl)) }, [currentUrl, bookmarks, appOpen]) return ( <>
{(!isCollapsed || isHovered) && ( <>
{isKiosk ? ( <> ) : ( <> )}
{appOpen && openedFromSearch && onBackToSearch && ( )} {appOpen && ( <> )}
{showSearchIcon && ( )} {appOpen && ( )}
)}
{updateStatus && (
{updateStatus.state === 'available' && ( <> Загружается обновление {updateStatus.version}{updateStatus.currentVersion ? ` (текущая ${updateStatus.currentVersion})` : ''}… )} {updateStatus.state === 'error' && ( <> Ошибка обновления: {updateStatus.message} )} {updateStatus.state === 'downloading' && ( <> Скачивается {updateStatus.version || 'обновление'}: {updateStatus.percent}%
)} {updateStatus.state === 'ready' && ( <> Версия {updateStatus.version} готова к установке )} {updateStatus.state === 'manual' && ( <> Доступна {updateStatus.version}{updateStatus.currentVersion ? ` (текущая ${updateStatus.currentVersion})` : ''} )}
)} {showSettings && ( )} ) } export default Header