init
This commit is contained in:
292
src/components/Settings.tsx
Normal file
292
src/components/Settings.tsx
Normal file
@@ -0,0 +1,292 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
export interface AppEntry {
|
||||
name: string
|
||||
imageUrl: string
|
||||
url: string
|
||||
useProxy: boolean
|
||||
}
|
||||
|
||||
export interface Bookmark {
|
||||
title: string
|
||||
url: string
|
||||
poster?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
export interface MovieSite {
|
||||
domain: string
|
||||
type: 'dle' | 'hdrezka' | 'filmix'
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
interface ProxyConfig {
|
||||
host: string
|
||||
port: string
|
||||
}
|
||||
|
||||
interface SettingsData {
|
||||
apps: AppEntry[]
|
||||
proxy: ProxyConfig
|
||||
movieSites: MovieSite[]
|
||||
tmdbApiKey: string
|
||||
bookmarks: Bookmark[]
|
||||
}
|
||||
|
||||
interface SettingsProps {
|
||||
onClose: () => void
|
||||
onAppsChange: (apps: AppEntry[]) => void
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: SettingsData = { apps: [], proxy: { host: '127.0.0.1', port: '7890' }, movieSites: [], tmdbApiKey: '', bookmarks: [] }
|
||||
|
||||
function guessMovieSiteType(domain: string): MovieSite['type'] {
|
||||
if (/rezka/.test(domain)) return 'hdrezka'
|
||||
if (/filmix/.test(domain)) return 'filmix'
|
||||
return 'dle'
|
||||
}
|
||||
|
||||
function saveSettings(data: SettingsData) {
|
||||
window.electron?.writeConfig(data)
|
||||
}
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ onClose, onAppsChange }) => {
|
||||
const [settings, setSettings] = useState<SettingsData>(DEFAULT_SETTINGS)
|
||||
const [newApp, setNewApp] = useState<AppEntry>({ name: '', imageUrl: '', url: '', useProxy: false })
|
||||
|
||||
useEffect(() => {
|
||||
window.electron?.readConfig().then((cfg: SettingsData | null) => {
|
||||
if (cfg?.apps) setSettings(cfg)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const updateProxy = (field: keyof ProxyConfig, value: string) => {
|
||||
const updated = { ...settings, proxy: { ...settings.proxy, [field]: value } }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
window.electron?.setProxy(updated.proxy.host, updated.proxy.port)
|
||||
}
|
||||
|
||||
const toggleAppProxy = (index: number) => {
|
||||
const apps = settings.apps.map((app, i) =>
|
||||
i === index ? { ...app, useProxy: !app.useProxy } : app
|
||||
)
|
||||
const updated = { ...settings, apps }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
onAppsChange(apps)
|
||||
}
|
||||
|
||||
const addApp = () => {
|
||||
if (!newApp.name || !newApp.url) return
|
||||
const apps = [...settings.apps, newApp]
|
||||
const updated = { ...settings, apps }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
onAppsChange(apps)
|
||||
setNewApp({ name: '', imageUrl: '', url: '', useProxy: false })
|
||||
}
|
||||
|
||||
const removeApp = (index: number) => {
|
||||
const apps = settings.apps.filter((_, i) => i !== index)
|
||||
const updated = { ...settings, apps }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
onAppsChange(apps)
|
||||
}
|
||||
|
||||
const [newSite, setNewSite] = useState<MovieSite>({ domain: '', type: 'dle', enabled: true })
|
||||
|
||||
const addMovieSite = () => {
|
||||
if (!newSite.domain.trim()) return
|
||||
let domain = newSite.domain.trim()
|
||||
if (domain.startsWith('https://')) domain = domain.slice(8)
|
||||
else if (domain.startsWith('http://')) domain = domain.slice(7)
|
||||
if (domain.endsWith('/')) domain = domain.slice(0, -1)
|
||||
const movieSites = [...(settings.movieSites ?? []), { ...newSite, domain }]
|
||||
const updated = { ...settings, movieSites }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
setNewSite({ domain: '', type: 'dle', enabled: true })
|
||||
}
|
||||
|
||||
const removeMovieSite = (index: number) => {
|
||||
const movieSites = (settings.movieSites ?? []).filter((_, i) => i !== index)
|
||||
const updated = { ...settings, movieSites }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
}
|
||||
|
||||
const toggleMovieSite = (index: number) => {
|
||||
const movieSites = (settings.movieSites ?? []).map((s, i) =>
|
||||
i === index ? { ...s, enabled: !s.enabled } : s
|
||||
)
|
||||
const updated = { ...settings, movieSites }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="settings-overlay" onClick={onClose}>
|
||||
<div className="settings-panel" onClick={e => e.stopPropagation()}>
|
||||
<div className="settings-header">
|
||||
<h2>Настройки</h2>
|
||||
<button className="settings-close" onClick={onClose}>✕</button>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h3>Прокси</h3>
|
||||
<div className="proxy-row">
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="Хост"
|
||||
value={settings.proxy.host}
|
||||
onChange={e => updateProxy('host', e.target.value)}
|
||||
/>
|
||||
<span className="proxy-colon">:</span>
|
||||
<input
|
||||
className="settings-input proxy-port"
|
||||
placeholder="Порт"
|
||||
value={settings.proxy.port}
|
||||
onChange={e => updateProxy('port', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="proxy-info">
|
||||
<code>http_proxy=http://{settings.proxy.host}:{settings.proxy.port}</code>
|
||||
<code>https_proxy=http://{settings.proxy.host}:{settings.proxy.port}</code>
|
||||
<code>socks5://{settings.proxy.host}:{settings.proxy.port}</code>
|
||||
</div>
|
||||
<p className="proxy-hint">
|
||||
Прокси применяется к каждому сайту индивидуально — переключатель рядом с каждым приложением.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h3>Приложения</h3>
|
||||
<div className="settings-apps-list">
|
||||
{settings.apps.map((app, i) => (
|
||||
<div key={i} className="settings-app-row">
|
||||
<div className="settings-app-info">
|
||||
{app.imageUrl && (
|
||||
<img src={app.imageUrl} alt={app.name} className="settings-app-icon" />
|
||||
)}
|
||||
<div className="settings-app-text">
|
||||
<span className="settings-app-name">{app.name}</span>
|
||||
<span className="settings-app-url">{app.url}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-app-actions">
|
||||
<label className="proxy-switch-label">
|
||||
<span>Прокси</span>
|
||||
<div
|
||||
className={`proxy-switch ${app.useProxy ? 'on' : 'off'}`}
|
||||
onClick={() => toggleAppProxy(i)}
|
||||
/>
|
||||
</label>
|
||||
<button className="settings-remove-btn" onClick={() => removeApp(i)}>✕</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{settings.apps.length === 0 && (
|
||||
<p className="settings-empty">Нет приложений. Добавьте ниже.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="add-app-form">
|
||||
<h4>Добавить приложение</h4>
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="Название"
|
||||
value={newApp.name}
|
||||
onChange={e => setNewApp({ ...newApp, name: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="URL сайта"
|
||||
value={newApp.url}
|
||||
onChange={e => setNewApp({ ...newApp, url: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="URL иконки (необязательно)"
|
||||
value={newApp.imageUrl}
|
||||
onChange={e => setNewApp({ ...newApp, imageUrl: e.target.value })}
|
||||
/>
|
||||
<label className="proxy-switch-label add-proxy-label">
|
||||
<span>Использовать прокси</span>
|
||||
<div
|
||||
className={`proxy-switch ${newApp.useProxy ? 'on' : 'off'}`}
|
||||
onClick={() => setNewApp({ ...newApp, useProxy: !newApp.useProxy })}
|
||||
/>
|
||||
</label>
|
||||
<button className="settings-add-btn" onClick={addApp}>Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h3>Поиск фильмов</h3>
|
||||
<h4>TMDB API ключ</h4>
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="Получить бесплатно на themoviedb.org"
|
||||
value={settings.tmdbApiKey ?? ''}
|
||||
onChange={e => {
|
||||
const updated = { ...settings, tmdbApiKey: e.target.value }
|
||||
setSettings(updated)
|
||||
saveSettings(updated)
|
||||
}}
|
||||
/>
|
||||
<p className="proxy-hint">Нужен для постеров и метаданных. Без ключа поиск работает напрямую по сайтам.</p>
|
||||
<h4>Сайты</h4>
|
||||
<p className="proxy-hint">Тип определяется автоматически. Поддерживаются: kinogo, lordfilm, gidonline, hdrezka, filmix и их зеркала. Домен без https://</p>
|
||||
<div className="settings-apps-list">
|
||||
{(settings.movieSites ?? []).map((site, i) => (
|
||||
<div key={i} className="settings-app-row">
|
||||
<div className="settings-app-info">
|
||||
<div className="settings-app-text">
|
||||
<span className="settings-app-name">{site.domain}</span>
|
||||
<span className="settings-app-url">{site.type}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-app-actions">
|
||||
<label className="proxy-switch-label">
|
||||
<span>Вкл</span>
|
||||
<div className={`proxy-switch ${site.enabled ? 'on' : 'off'}`} onClick={() => toggleMovieSite(i)} />
|
||||
</label>
|
||||
<button className="settings-remove-btn" onClick={() => removeMovieSite(i)}>✕</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{!(settings.movieSites ?? []).length && (
|
||||
<p className="settings-empty">Нет сайтов. Добавьте ниже.</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="add-app-form">
|
||||
<h4>Добавить сайт</h4>
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="Домен (например: kinogo.cc)"
|
||||
value={newSite.domain}
|
||||
onChange={e => {
|
||||
const domain = e.target.value
|
||||
setNewSite({ ...newSite, domain, type: guessMovieSiteType(domain) })
|
||||
}}
|
||||
/>
|
||||
<select
|
||||
className="settings-select"
|
||||
value={newSite.type}
|
||||
onChange={e => setNewSite({ ...newSite, type: e.target.value as MovieSite['type'] })}
|
||||
>
|
||||
<option value="dle">DLE (kinogo, lordfilm и зеркала)</option>
|
||||
<option value="hdrezka">HDRezka</option>
|
||||
<option value="filmix">Filmix</option>
|
||||
</select>
|
||||
<button className="settings-add-btn" onClick={addMovieSite}>Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Settings
|
||||
Reference in New Issue
Block a user