diff --git a/main.js b/main.js index b973621..e4b05fd 100644 --- a/main.js +++ b/main.js @@ -157,6 +157,27 @@ async function loadExtensions() { } } +// --- Updates --- + +async function checkForUpdates() { + try { + const res = await getDirectSession().fetch( + 'https://gitea.esh-service.ru/api/v1/repos/public/ESH-Media/releases/latest' + ); + if (!res.ok) return; + const data = await res.json(); + const latest = (data.tag_name || '').replace(/^v/, ''); + const current = app.getVersion(); + if (latest && latest !== current) { + mainWindow.webContents.send('update-available', { + version: latest, + url: data.html_url, + assets: (data.assets || []).map(a => ({ name: a.name, url: a.browser_download_url })), + }); + } + } catch (_) {} +} + // --- Window --- async function createWindow() { @@ -862,6 +883,10 @@ app.whenReady().then(async () => { getDirectSession(); await loadExtensions(); await createWindow(); + + mainWindow.webContents.once('did-finish-load', () => { + setTimeout(checkForUpdates, 4000); + }); }); app.on('window-all-closed', () => { diff --git a/package.json b/package.json index d2e7832..1c85c88 100644 --- a/package.json +++ b/package.json @@ -46,14 +46,12 @@ "extensions/**/*" ], "win": { - "target": ["zip"], + "target": ["nsis", "zip"], "icon": "public/favicon.ico" }, "nsis": { "oneClick": false, - "allowToChangeInstallationDirectory": true, - "installerLanguages": ["Russian", "English"], - "language": "1049" + "allowToChangeInstallationDirectory": true }, "linux": { "target": ["AppImage", "deb"], diff --git a/src/components/Header.tsx b/src/components/Header.tsx index bea7c7f..6cf73e0 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -10,9 +10,11 @@ interface HeaderProps { onBookmark: (title: string, url: string, poster: string, source: 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 }) => { +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) @@ -106,6 +108,15 @@ const Header: React.FC = ({ activeApp, setActiveApp, onAppsChange, const showSearchIcon = activeApp === 'home' || activeApp === 'movie-search' const [isKiosk, setIsKiosk] = useState(true) + const [updateInfo, setUpdateInfo] = useState<{ version: string; url: string } | null>(null) + + useEffect(() => { + if (!window.electron) return + const off = window.electron.on('update-available', (info: { version: string; url: string }) => { + setUpdateInfo(info) + }) + return off + }, []) useEffect(() => { window.electron?.isKiosk().then(k => setIsKiosk(k)) @@ -185,6 +196,14 @@ const Header: React.FC = ({ activeApp, setActiveApp, onAppsChange,
+ {appOpen && openedFromSearch && onBackToSearch && ( + + )} {appOpen && ( <>
+ {updateInfo && ( +
+ Доступна версия {updateInfo.version} + window.electron?.createView('Обновление', updateInfo.url, '', 1.0, false)}> + Скачать + + +
+ )} + {showSettings && ( )} diff --git a/src/components/MovieSearch.tsx b/src/components/MovieSearch.tsx index 07fc687..84db79c 100644 --- a/src/components/MovieSearch.tsx +++ b/src/components/MovieSearch.tsx @@ -468,7 +468,21 @@ const MovieSearch: React.FC = ({ onOpenUrl, onBookmark, initia
{sitesLoading &&

Ищем на {sites.length} сайтах...

} {!sitesLoading && !siteResults.length && !message &&

Поиск...

} - {siteResults.length > 0 &&
Найдено на сайтах
} + {!sitesLoading && siteResults.length > 0 && ( +
+ Найдено на сайтах + +
+ )} + {!sitesLoading && !siteResults.length && ( + + )} {siteResults.map((r, i) => (
onOpenUrl(r.title, r.url)}> {r.source} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 98c3d2c..7d8b1a3 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -17,6 +17,8 @@ const HomePage: React.FC = () => { const [activeApp, setActiveApp] = useState('home') const [appCardList, setAppCardList] = useState([]) const [movieQuery, setMovieQuery] = useState(null) + const [movieSearchKey, setMovieSearchKey] = useState(0) + const [openedFromSearch, setOpenedFromSearch] = useState(false) const [bookmarks, setBookmarks] = useState([]) const configRef = useRef({}) @@ -46,14 +48,22 @@ const HomePage: React.FC = () => { const handleMovieSearch = (query: string) => { window.electron?.hideView() setMovieQuery(query) + setMovieSearchKey(k => k + 1) + setOpenedFromSearch(false) setActiveApp('movie-search') } const handleMovieSearchOpen = (name: string, url: string) => { window.electron?.createView(name, url, '', 1.0, resolveUseProxy(url)) + setOpenedFromSearch(true) setActiveApp(name) } + const handleBackToSearch = () => { + window.electron?.hideView() + setActiveApp('movie-search') + } + const handleBookmarkAdd = (title: string, url: string, poster: string, source: string) => { const updated = [...bookmarks, { title, url, poster, source }] setBookmarks(updated) @@ -89,12 +99,14 @@ const HomePage: React.FC = () => { return ( <> -
+
- {activeApp === 'movie-search' - ? - : - } +
+ +
+ {activeApp !== 'movie-search' && ( + + )} ) } diff --git a/src/styles/main.css b/src/styles/main.css index bbfc6c8..0380a15 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1323,3 +1323,64 @@ body { .modal-btn-yes { background: #E50914; color: #fff; } .modal-btn-no { background: rgba(255,255,255,0.1); color: #ccc; } .modal-btn-ok { background: rgba(255,255,255,0.1); color: #ccc; } + +/* ---- Update banner ---- */ +.update-banner { + position: fixed; + bottom: 16px; + right: 16px; + z-index: 9999; + background: #1a1a1a; + border: 1px solid rgba(229,9,20,0.4); + border-radius: 8px; + padding: 10px 14px; + display: flex; + align-items: center; + gap: 10px; + font-size: 13px; + color: #fff; + box-shadow: 0 4px 20px rgba(0,0,0,0.5); +} +.update-banner-btn { + background: #E50914; + color: #fff; + border: none; + border-radius: 5px; + padding: 4px 12px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + text-decoration: none; +} +.update-banner-close { + background: none; + border: none; + color: #777; + cursor: pointer; + font-size: 13px; + padding: 0; + line-height: 1; +} +.update-banner-close:hover { color: #fff; } + +/* ---- Retry btn ---- */ +.ms-retry-btn { + background: none; + border: none; + color: #aaa; + cursor: pointer; + padding: 2px 6px; + display: inline-flex; + align-items: center; + gap: 4px; + border-radius: 4px; + transition: color 0.15s; +} +.ms-retry-btn:hover { color: #fff; } +.ms-retry-standalone { + margin-top: 12px; + font-size: 13px; + color: #E50914; + display: block; +} +.ms-retry-standalone:hover { color: #ff4444; }