Electron-based kiosk desktop app: large-tile launcher for YouTube, RuTube, movie sites and Google services, designed for low-tech grandparent use. Features: - WebContentsView-per-app tabbed browsing with session persistence - per-app proxy routing (Clash/V2Ray friendly, useProxy flag) - cliqz-electron adblocker with whitelist for OAuth/integrity domains - TMDB-backed movie search across kinogo / hdrezka / filmix - bookmark posters auto-fetched from og:image / JSON-LD - electron-updater wired to Gitea releases API (latest.yml + .blockmap) - cross-domain navigation confirms via custom WebContentsView dialogs - kiosk window with hidden menu, Ctrl+Shift+I devtools shortcut - Trusted Types disabled engine-wide so adblocker scriptlets work on YouTube Google OAuth handling (the hard-won part): Google's anti-abuse JS rejects WebContentsView + custom session settings as "embedded browser". So accounts.google.com opens in a top-level BrowserWindow popup in a dedicated persist:google-login partition that we never call setProxy/setUserAgent on — it inherits Windows system proxy and the default Electron-tagged UA, both of which Google accepts. After login, .google.com/.youtube.com cookies migrate into the parent view's session and the view reloads to pick up the logged-in state. Session restore: only the last-active tab attaches to the window; other tabs load silently in the background and become instantly visible when the user clicks them in the sidebar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
import React from 'react'
|
||
import AppCard, { AppCardProps } from './AppCard'
|
||
import BookmarksBar from './BookmarksBar'
|
||
import { Bookmark } from './Settings'
|
||
|
||
interface AppListProps {
|
||
apps: AppCardProps[]
|
||
bookmarks: Bookmark[]
|
||
onBookmarkOpen: (b: Bookmark) => void
|
||
onBookmarkRemove: (index: number) => void
|
||
}
|
||
|
||
const AppList: React.FC<AppListProps> = ({ apps, bookmarks, onBookmarkOpen, onBookmarkRemove }) => {
|
||
return (
|
||
<div className="app-list">
|
||
<BookmarksBar bookmarks={bookmarks} onOpen={onBookmarkOpen} onRemove={onBookmarkRemove} />
|
||
{apps.length === 0 ? (
|
||
<div className="app-list-empty">
|
||
<p>Нет приложений.</p>
|
||
<p>Откройте настройки (шестерёнка) и добавьте сайты.</p>
|
||
</div>
|
||
) : (
|
||
apps.map((card, i) => (
|
||
<AppCard
|
||
key={i}
|
||
name={card.name}
|
||
imageUrl={card.imageUrl}
|
||
url={card.url}
|
||
useProxy={card.useProxy}
|
||
/>
|
||
))
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default AppList
|