mirror of
https://github.com/ILoveBingLu/CipherTalk.git
synced 2026-05-22 12:30:59 +08:00
feat: 支持 GitHub 主源与自定义策略更新
This commit is contained in:
+114
@@ -88,6 +88,120 @@
|
||||
}
|
||||
}
|
||||
|
||||
.force-update-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 5000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
background: rgba(6, 10, 16, 0.82);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.force-update-card {
|
||||
width: min(720px, 100%);
|
||||
max-height: 85vh;
|
||||
overflow: auto;
|
||||
padding: 28px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: var(--bg-secondary);
|
||||
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
|
||||
|
||||
h2 {
|
||||
margin: 16px 0 10px;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.force-update-desc {
|
||||
margin: 0 0 18px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.force-update-meta {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-bottom: 18px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.force-update-notes {
|
||||
margin-bottom: 18px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-primary);
|
||||
|
||||
.force-update-notes-title {
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text-secondary);
|
||||
font-family: inherit;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.force-update-progress {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.force-update-progress-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.force-update-progress-bar {
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-primary);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.force-update-progress-fill {
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #ff7849 0%, #ff4747 100%);
|
||||
}
|
||||
|
||||
.force-update-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.force-update-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 92, 92, 0.12);
|
||||
color: #ff5c5c;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
// 独立聊天窗口容器
|
||||
.chat-window-container {
|
||||
height: 100vh;
|
||||
|
||||
+79
-2
@@ -38,6 +38,21 @@ import { useAuthStore } from './stores/authStore'
|
||||
import { X, Shield, Loader2 } from 'lucide-react'
|
||||
import './App.scss'
|
||||
|
||||
type AppUpdateInfo = {
|
||||
hasUpdate: boolean
|
||||
forceUpdate: boolean
|
||||
currentVersion: string
|
||||
version?: string
|
||||
releaseNotes?: string
|
||||
title?: string
|
||||
message?: string
|
||||
minimumSupportedVersion?: string
|
||||
reason?: 'minimum-version' | 'blocked-version'
|
||||
checkedAt: number
|
||||
updateSource: 'github' | 'custom' | 'none'
|
||||
policySource: 'github' | 'custom' | 'none'
|
||||
}
|
||||
|
||||
function App() {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
@@ -55,7 +70,7 @@ function App() {
|
||||
const [showActivation, setShowActivation] = useState(false)
|
||||
|
||||
// 更新提示状态
|
||||
const [updateInfo, setUpdateInfo] = useState<{ version: string; releaseNotes: string } | null>(null)
|
||||
const [updateInfo, setUpdateInfo] = useState<AppUpdateInfo | null>(null)
|
||||
const [downloadProgress, setDownloadProgress] = useState<number | null>(null)
|
||||
|
||||
// 加载主题配置
|
||||
@@ -136,6 +151,15 @@ function App() {
|
||||
|
||||
// 监听启动时的更新通知
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
window.electronAPI.app.getUpdateState?.().then((info) => {
|
||||
if (mounted && info?.hasUpdate) {
|
||||
setUpdateInfo(info)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('获取更新状态失败:', error)
|
||||
})
|
||||
|
||||
const removeUpdateListener = window.electronAPI.app.onUpdateAvailable?.((info) => {
|
||||
setUpdateInfo(info)
|
||||
})
|
||||
@@ -162,6 +186,7 @@ function App() {
|
||||
})
|
||||
|
||||
return () => {
|
||||
mounted = false
|
||||
removeUpdateListener?.()
|
||||
removeSessionsListener?.()
|
||||
removeUpdateAvailableListener?.()
|
||||
@@ -179,6 +204,7 @@ function App() {
|
||||
}, [])
|
||||
|
||||
const dismissUpdate = () => {
|
||||
if (updateInfo?.forceUpdate) return
|
||||
setUpdateInfo(null)
|
||||
}
|
||||
|
||||
@@ -466,12 +492,13 @@ function App() {
|
||||
return (
|
||||
<div className="app-container">
|
||||
<TitleBar />
|
||||
{updateInfo && (
|
||||
{updateInfo && !updateInfo.forceUpdate && (
|
||||
<div className="update-toast">
|
||||
<div className="update-toast-icon">🎉</div>
|
||||
<div className="update-toast-content">
|
||||
<div className="update-toast-title">发现新版本</div>
|
||||
<div className="update-toast-version">v{updateInfo.version} 已发布</div>
|
||||
<div className="update-toast-version">更新源:{updateInfo.updateSource === 'github' ? 'GitHub Release' : '未知'}</div>
|
||||
</div>
|
||||
<button className="update-toast-btn" onClick={() => {
|
||||
window.electronAPI.app.downloadAndInstall()
|
||||
@@ -484,6 +511,56 @@ function App() {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{updateInfo?.forceUpdate && (
|
||||
<div className="force-update-overlay">
|
||||
<div className="force-update-card">
|
||||
<div className="force-update-badge">
|
||||
<Shield size={18} />
|
||||
<span>强制更新</span>
|
||||
</div>
|
||||
<h2>{updateInfo.title || '必须更新后才能继续使用'}</h2>
|
||||
<p className="force-update-desc">
|
||||
{updateInfo.message || '当前版本已被标记为需要立即升级,应用将限制继续使用,直到安装最新版本。'}
|
||||
</p>
|
||||
|
||||
<div className="force-update-meta">
|
||||
<div>当前版本:v{updateInfo.currentVersion}</div>
|
||||
{updateInfo.version && <div>目标版本:v{updateInfo.version}</div>}
|
||||
{updateInfo.minimumSupportedVersion && <div>最低安全版本:v{updateInfo.minimumSupportedVersion}</div>}
|
||||
<div>更新来源:{updateInfo.updateSource === 'github' ? 'GitHub Release' : '未检测到普通更新源'}</div>
|
||||
<div>策略来源:{updateInfo.policySource === 'github' ? 'GitHub 策略源' : updateInfo.policySource === 'custom' ? '自定义策略源' : '无'}</div>
|
||||
</div>
|
||||
|
||||
{updateInfo.releaseNotes && (
|
||||
<div className="force-update-notes">
|
||||
<div className="force-update-notes-title">更新说明</div>
|
||||
<pre>{updateInfo.releaseNotes}</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{downloadProgress !== null && (
|
||||
<div className="force-update-progress">
|
||||
<div className="force-update-progress-label">
|
||||
<Loader2 size={16} className="spin" />
|
||||
<span>正在下载更新... {downloadProgress.toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="force-update-progress-bar">
|
||||
<div className="force-update-progress-fill" style={{ width: `${downloadProgress}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="force-update-actions">
|
||||
<button className="btn btn-primary" onClick={() => window.electronAPI.app.downloadAndInstall()}>
|
||||
立即更新
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={() => window.electronAPI.window.close()}>
|
||||
退出应用
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
@@ -98,7 +98,30 @@ function SettingsPage() {
|
||||
const [isDownloading, setIsDownloading] = useState(false)
|
||||
const [downloadProgress, setDownloadProgress] = useState(0)
|
||||
const [appVersion, setAppVersion] = useState('')
|
||||
const [updateInfo, setUpdateInfo] = useState<{ hasUpdate: boolean; version?: string; releaseNotes?: string } | null>(null)
|
||||
const [updateInfo, setUpdateInfo] = useState<{
|
||||
hasUpdate: boolean
|
||||
forceUpdate: boolean
|
||||
currentVersion: string
|
||||
version?: string
|
||||
releaseNotes?: string
|
||||
title?: string
|
||||
message?: string
|
||||
minimumSupportedVersion?: string
|
||||
reason?: 'minimum-version' | 'blocked-version'
|
||||
checkedAt: number
|
||||
updateSource: 'github' | 'custom' | 'none'
|
||||
policySource: 'github' | 'custom' | 'none'
|
||||
} | null>(null)
|
||||
const [updateSourceInfo, setUpdateSourceInfo] = useState<{
|
||||
primaryUpdateSource: 'github'
|
||||
githubRepository: {
|
||||
owner: string
|
||||
repo: string
|
||||
}
|
||||
policySources: Array<'github' | 'custom'>
|
||||
policyPrecedence: 'github'
|
||||
forceUpdatePolicyFallbackUrl: string
|
||||
} | null>(null)
|
||||
const [keyStatus, setKeyStatus] = useState('')
|
||||
const [message, setMessage] = useState<{ text: string; success: boolean } | null>(null)
|
||||
const [showDecryptKey, setShowDecryptKey] = useState(false)
|
||||
@@ -466,7 +489,7 @@ function SettingsPage() {
|
||||
const result = await window.electronAPI.app.checkForUpdates()
|
||||
if (result.hasUpdate) {
|
||||
setUpdateInfo(result)
|
||||
showMessage(`发现新版本 ${result.version}`, true)
|
||||
showMessage(result.forceUpdate ? `检测到强制更新 ${result.version}` : `发现新版本 ${result.version}`, true)
|
||||
} else {
|
||||
showMessage('当前已是最新版本', true)
|
||||
}
|
||||
@@ -2646,6 +2669,14 @@ function SettingsPage() {
|
||||
}
|
||||
}, [location.state])
|
||||
|
||||
useEffect(() => {
|
||||
window.electronAPI.app.getUpdateSourceInfo?.().then((info) => {
|
||||
setUpdateSourceInfo(info)
|
||||
}).catch((error) => {
|
||||
console.error('获取更新源信息失败:', error)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const renderAboutTab = () => (
|
||||
<div className="tab-content about-tab">
|
||||
<div className="about-card">
|
||||
@@ -2657,9 +2688,24 @@ function SettingsPage() {
|
||||
<p className="about-version">v{appVersion || '...'}</p>
|
||||
|
||||
<div className="about-update">
|
||||
{updateSourceInfo && (
|
||||
<div className="update-hint" style={{ marginBottom: '10px' }}>
|
||||
主更新源:GitHub Release ({updateSourceInfo.githubRepository.owner}/{updateSourceInfo.githubRepository.repo})<br />
|
||||
策略补充源:{updateSourceInfo.forceUpdatePolicyFallbackUrl}
|
||||
</div>
|
||||
)}
|
||||
{updateInfo?.hasUpdate ? (
|
||||
<>
|
||||
<p className="update-hint">新版本 v{updateInfo.version} 可用</p>
|
||||
<p className="update-hint">
|
||||
{updateInfo.forceUpdate ? '检测到强制更新' : `新版本 v${updateInfo.version} 可用`}
|
||||
</p>
|
||||
<p className="update-hint">
|
||||
更新来源:{updateInfo.updateSource === 'github' ? 'GitHub Release' : '未知'} / 策略来源:
|
||||
{updateInfo.policySource === 'github' ? 'GitHub' : updateInfo.policySource === 'custom' ? '自定义源' : '无'}
|
||||
</p>
|
||||
{updateInfo.forceUpdate && updateInfo.minimumSupportedVersion && (
|
||||
<p className="update-hint">最低安全版本:v{updateInfo.minimumSupportedVersion}</p>
|
||||
)}
|
||||
{isDownloading ? (
|
||||
<div className="download-progress">
|
||||
<div className="progress-bar">
|
||||
|
||||
Vendored
+52
-2
@@ -81,12 +81,62 @@ export interface ElectronAPI {
|
||||
cwd: string
|
||||
mode: 'dev' | 'packaged'
|
||||
} | null>
|
||||
checkForUpdates: () => Promise<{ hasUpdate: boolean; version?: string; releaseNotes?: string }>
|
||||
getUpdateState: () => Promise<{
|
||||
hasUpdate: boolean
|
||||
forceUpdate: boolean
|
||||
currentVersion: string
|
||||
version?: string
|
||||
releaseNotes?: string
|
||||
title?: string
|
||||
message?: string
|
||||
minimumSupportedVersion?: string
|
||||
reason?: 'minimum-version' | 'blocked-version'
|
||||
checkedAt: number
|
||||
updateSource: 'github' | 'custom' | 'none'
|
||||
policySource: 'github' | 'custom' | 'none'
|
||||
} | null>
|
||||
getUpdateSourceInfo: () => Promise<{
|
||||
primaryUpdateSource: 'github'
|
||||
githubRepository: {
|
||||
owner: string
|
||||
repo: string
|
||||
}
|
||||
policySources: Array<'github' | 'custom'>
|
||||
policyPrecedence: 'github'
|
||||
forceUpdatePolicyFallbackUrl: string
|
||||
}>
|
||||
checkForUpdates: () => Promise<{
|
||||
hasUpdate: boolean
|
||||
forceUpdate: boolean
|
||||
currentVersion: string
|
||||
version?: string
|
||||
releaseNotes?: string
|
||||
title?: string
|
||||
message?: string
|
||||
minimumSupportedVersion?: string
|
||||
reason?: 'minimum-version' | 'blocked-version'
|
||||
checkedAt: number
|
||||
updateSource: 'github' | 'custom' | 'none'
|
||||
policySource: 'github' | 'custom' | 'none'
|
||||
}>
|
||||
downloadAndInstall: () => Promise<void>
|
||||
getStartupDbConnected?: () => Promise<boolean>
|
||||
setAppIcon: (iconName: string) => Promise<void>
|
||||
onDownloadProgress: (callback: (progress: number) => void) => () => void
|
||||
onUpdateAvailable: (callback: (info: { version: string; releaseNotes: string }) => void) => () => void
|
||||
onUpdateAvailable: (callback: (info: {
|
||||
hasUpdate: boolean
|
||||
forceUpdate: boolean
|
||||
currentVersion: string
|
||||
version?: string
|
||||
releaseNotes?: string
|
||||
title?: string
|
||||
message?: string
|
||||
minimumSupportedVersion?: string
|
||||
reason?: 'minimum-version' | 'blocked-version'
|
||||
checkedAt: number
|
||||
updateSource: 'github' | 'custom' | 'none'
|
||||
policySource: 'github' | 'custom' | 'none'
|
||||
}) => void) => () => void
|
||||
}
|
||||
httpApi: {
|
||||
getStatus: () => Promise<{
|
||||
|
||||
Reference in New Issue
Block a user