feat: trusted-domains OAuth popups, og:image bookmark posters, periodic updater
- main.js: trusted-domains list with default Google/Yandex/GitHub/etc.; cross-domain confirmation skipped for trusted; setWindowOpenHandler returns action:'allow' for trusted so OAuth popups work (postMessage back to opener, popup self-closes). Fixes YouTube/Google login reset. - main.js: get-page-meta IPC extracts og:image / twitter:image / JSON-LD image from current view; HDRezka also tries .b-sidecover img for hi-res. - Header: bookmark button pulls og:image as poster and the page's title; duplicate detection switched from hostname to full URL so multiple movies from same site can coexist. - BookmarksBar: site icon rendered next to source domain when distinct from poster; img onerror falls back to placeholder. - Settings: trusted domains chip list with add/remove/reset. - Updater: proper semver compare (only show if latest > current), direct installer URL detection per platform, hourly re-check. Bookmark schema gains optional siteIcon; existing bookmarks remain valid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ interface HeaderProps {
|
||||
setActiveApp: (name: string) => void
|
||||
onAppsChange: (apps: AppEntry[]) => void
|
||||
onMovieSearch: (query: string) => void
|
||||
onBookmark: (title: string, url: string, poster: string, source: string) => void
|
||||
onBookmark: (title: string, url: string, poster: string, source: string, siteIcon?: string) => void
|
||||
onBookmarkRemove: (index: number) => void
|
||||
bookmarks: import('./Settings').Bookmark[]
|
||||
openedFromSearch?: boolean
|
||||
@@ -108,11 +108,11 @@ const Header: React.FC<HeaderProps> = ({ 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)
|
||||
const [updateInfo, setUpdateInfo] = useState<{ version: string; currentVersion?: string; releaseUrl: string; installerUrl: string; installerName?: string } | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!window.electron) return
|
||||
const off = window.electron.on('update-available', (info: { version: string; url: string }) => {
|
||||
const off = window.electron.on('update-available', (info: { version: string; currentVersion?: string; releaseUrl: string; installerUrl: string; installerName?: string }) => {
|
||||
setUpdateInfo(info)
|
||||
})
|
||||
return off
|
||||
@@ -128,33 +128,31 @@ const Header: React.FC<HeaderProps> = ({ activeApp, setActiveApp, onAppsChange,
|
||||
|
||||
const [isBookmarked, setIsBookmarked] = useState(false)
|
||||
|
||||
const handleBookmark = () => {
|
||||
window.electron?.getCurrentPage().then((page: any) => {
|
||||
if (!page) return
|
||||
let pageHost = ''
|
||||
try { pageHost = new URL(page.url).hostname } catch (_) {}
|
||||
const existingIdx = bookmarks.findIndex(b => {
|
||||
try { return new URL(b.url).hostname === pageHost } catch (_) { return false }
|
||||
})
|
||||
if (existingIdx !== -1) {
|
||||
onBookmarkRemove(existingIdx)
|
||||
setIsBookmarked(false)
|
||||
} else {
|
||||
onBookmark(page.name, page.url, page.imageUrl || '', '')
|
||||
setIsBookmarked(true)
|
||||
}
|
||||
})
|
||||
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) { setIsBookmarked(false); return }
|
||||
window.electron?.getCurrentPage().then((page: any) => {
|
||||
if (!page) return
|
||||
let pageHost = ''
|
||||
try { pageHost = new URL(page.url).hostname } catch (_) {}
|
||||
setIsBookmarked(bookmarks.some(b => {
|
||||
try { return new URL(b.url).hostname === pageHost } catch (_) { return false }
|
||||
}))
|
||||
setIsBookmarked(bookmarks.some(b => b.url === page.url))
|
||||
})
|
||||
}, [activeApp, bookmarks, appOpen])
|
||||
|
||||
@@ -283,10 +281,10 @@ const Header: React.FC<HeaderProps> = ({ activeApp, setActiveApp, onAppsChange,
|
||||
|
||||
{updateInfo && (
|
||||
<div className="update-banner">
|
||||
<span>Доступна версия {updateInfo.version}</span>
|
||||
<a href={updateInfo.url} target="_blank" rel="noreferrer" className="update-banner-btn" onClick={() => window.electron?.createView('Обновление', updateInfo.url, '', 1.0, false)}>
|
||||
Скачать
|
||||
</a>
|
||||
<span>Доступна версия {updateInfo.version}{updateInfo.currentVersion ? ` (текущая ${updateInfo.currentVersion})` : ''}</span>
|
||||
<button className="update-banner-btn" onClick={() => window.electron?.createView('Обновление', updateInfo.installerUrl, '', 1.0, false)}>
|
||||
{updateInfo.installerName ? 'Скачать установщик' : 'Открыть релиз'}
|
||||
</button>
|
||||
<button className="update-banner-close" onClick={() => setUpdateInfo(null)}>✕</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user