mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-22 05:56:58 +08:00
fix: 优化数据库启动与设置页响应性能
(cherry picked from commit f6614548b0e742bc703b2a5aaa5060720985ed27)
This commit is contained in:
@@ -210,6 +210,7 @@ const mergeMessagePages = (older: Message[], current: Message[]): Message[] => {
|
||||
function App(): React.ReactElement {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
||||
const [isDatabaseConnected, setIsDatabaseConnected] = useState(false)
|
||||
const [isDatabaseConnecting, setIsDatabaseConnecting] = useState(false)
|
||||
const [dbKey, setDbKey] = useState(getDevelopmentDatabaseKey)
|
||||
const [contacts, setContacts] = useState<Contact[]>([])
|
||||
const [selectedContact, setSelectedContact] = useState<Contact | null>(null)
|
||||
@@ -582,6 +583,7 @@ function App(): React.ReactElement {
|
||||
if (!autoLoginEnabled) return
|
||||
try {
|
||||
const startupCacheReady = await loadStartupCache()
|
||||
setIsDatabaseConnecting(true)
|
||||
const initPromise = window.api.initDb(key)
|
||||
if (startupCacheReady) {
|
||||
setIsAuthenticated(true)
|
||||
@@ -606,6 +608,9 @@ function App(): React.ReactElement {
|
||||
console.warn('[Startup] background database init failed:', error)
|
||||
setDbKeyStatusKind('error')
|
||||
})
|
||||
.finally(() => {
|
||||
if (active) setIsDatabaseConnecting(false)
|
||||
})
|
||||
return
|
||||
}
|
||||
const result = await initPromise
|
||||
@@ -638,6 +643,8 @@ function App(): React.ReactElement {
|
||||
setDbKeyStatus(`自动连接失败: ${message}`)
|
||||
setDbKeyStatusKind('error')
|
||||
setBootState('login')
|
||||
} finally {
|
||||
if (active) setIsDatabaseConnecting(false)
|
||||
}
|
||||
}
|
||||
void attemptAutoConnect()
|
||||
@@ -663,6 +670,7 @@ function App(): React.ReactElement {
|
||||
const keyToUse = keyInput || dbKey
|
||||
if (!keyToUse) return
|
||||
setBootState('connecting')
|
||||
setIsDatabaseConnecting(true)
|
||||
// 持久化用户手动指定的微信聊天文件路径,供 db:init 读取 settings.dbRoot
|
||||
const trimmedRoot = dbRootInput.trim()
|
||||
if (trimmedRoot) {
|
||||
@@ -746,6 +754,8 @@ function App(): React.ReactElement {
|
||||
setBootState('login')
|
||||
setStartupProgress(null)
|
||||
alert('Error connecting to database')
|
||||
} finally {
|
||||
setIsDatabaseConnecting(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,6 +928,7 @@ function App(): React.ReactElement {
|
||||
const handleReturnToLogin = (): void => {
|
||||
setIsAuthenticated(false)
|
||||
setIsDatabaseConnected(false)
|
||||
setIsDatabaseConnecting(false)
|
||||
setBootState('login')
|
||||
setDatabaseConnectionMode(dbKey ? 'manual' : 'automatic')
|
||||
setActivePage('archive')
|
||||
@@ -1425,6 +1436,7 @@ function App(): React.ReactElement {
|
||||
width={sidebarWidth}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={isDatabaseConnected}
|
||||
dbConnecting={isDatabaseConnecting}
|
||||
onOpenSettings={openSettings}
|
||||
/>
|
||||
<div className="resizer" onMouseDown={startResizing} />
|
||||
@@ -1454,6 +1466,7 @@ function App(): React.ReactElement {
|
||||
selectedReportId={selectedReportId}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={isDatabaseConnected}
|
||||
dbConnecting={isDatabaseConnecting}
|
||||
onSelectReport={openReport}
|
||||
onCreateReport={openReportConfigure}
|
||||
onDeleteReport={handleDeleteReport}
|
||||
@@ -1476,6 +1489,7 @@ function App(): React.ReactElement {
|
||||
selectedContact={reportSourceContact}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={isDatabaseConnected}
|
||||
dbConnecting={isDatabaseConnecting}
|
||||
onSelectContact={handleSelectReportSource}
|
||||
onOpenSettings={openSettings}
|
||||
/>
|
||||
@@ -1546,6 +1560,7 @@ function App(): React.ReactElement {
|
||||
onCategoryChange={setSettingsCategory}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={isDatabaseConnected}
|
||||
dbConnecting={isDatabaseConnecting}
|
||||
dbKey={dbKey}
|
||||
onDbKeyChange={setDbKey}
|
||||
onDatabaseConnectionChange={setIsDatabaseConnected}
|
||||
@@ -1692,6 +1707,7 @@ function App(): React.ReactElement {
|
||||
activePage={activePage}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={isDatabaseConnected}
|
||||
dbConnecting={isDatabaseConnecting}
|
||||
onPageChange={handlePageChange}
|
||||
onOpenSettings={openSettings}
|
||||
onOpenGuide={openFirstUseGuide}
|
||||
|
||||
@@ -10,6 +10,7 @@ interface SelfInfo {
|
||||
interface AccountSummaryProps {
|
||||
selfInfo: SelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
compact?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
@@ -17,13 +18,20 @@ interface AccountSummaryProps {
|
||||
export function AccountSummary({
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
compact = false,
|
||||
onClick
|
||||
}: AccountSummaryProps): React.ReactElement {
|
||||
const showAccount = Boolean(selfInfo && (dbReady || dbConnecting))
|
||||
const displayName =
|
||||
dbReady && selfInfo ? selfInfo.nickname || selfInfo.wxid || '当前账号' : '未连接'
|
||||
const subtitle = dbReady && selfInfo ? selfInfo.wxid : '打开设置'
|
||||
const statusText = dbReady ? '数据库已连接' : '数据库未连接'
|
||||
showAccount && selfInfo ? selfInfo.nickname || selfInfo.wxid || '当前账号' : '未连接'
|
||||
const subtitle = showAccount && selfInfo ? selfInfo.wxid : '打开设置'
|
||||
const statusText = dbReady
|
||||
? '数据库已连接'
|
||||
: dbConnecting
|
||||
? '正在连接数据库'
|
||||
: '数据库未连接'
|
||||
const statusClass = dbReady ? 'ready' : dbConnecting ? 'connecting' : ''
|
||||
const initial = (displayName || '?').charAt(0)
|
||||
const title = `${displayName}\n${subtitle}`
|
||||
const avatar = (
|
||||
@@ -33,7 +41,7 @@ export function AccountSummary({
|
||||
) : (
|
||||
initial
|
||||
)}
|
||||
<span className={`account-summary-status ${dbReady ? 'ready' : ''}`} aria-hidden />
|
||||
<span className={`account-summary-status ${statusClass}`} aria-hidden />
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -52,7 +60,7 @@ export function AccountSummary({
|
||||
<span className="account-summary-name">{displayName}</span>
|
||||
<span className="account-summary-meta">{subtitle}</span>
|
||||
<span className="account-summary-state">
|
||||
<span className={`account-summary-state-dot ${dbReady ? 'ready' : ''}`} aria-hidden />
|
||||
<span className={`account-summary-state-dot ${statusClass}`} aria-hidden />
|
||||
{statusText}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface ConversationSidebarProps {
|
||||
width: number
|
||||
selfInfo: SelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
onOpenSettings: () => void
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ export function ConversationSidebar({
|
||||
width,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
onOpenSettings
|
||||
}: ConversationSidebarProps): React.ReactElement {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
@@ -124,7 +126,12 @@ export function ConversationSidebar({
|
||||
</div>
|
||||
</div>
|
||||
<div className="conversation-sidebar-account">
|
||||
<AccountSummary selfInfo={selfInfo} dbReady={dbReady} onClick={onOpenSettings} />
|
||||
<AccountSummary
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
onClick={onOpenSettings}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ interface AppShellProps {
|
||||
activePage: AppPage
|
||||
selfInfo: SelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
onPageChange: (page: AppPage) => void
|
||||
onOpenSettings: () => void
|
||||
onOpenGuide: () => void
|
||||
@@ -35,6 +36,7 @@ export function AppShell({
|
||||
activePage,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
onPageChange,
|
||||
onOpenSettings,
|
||||
onOpenGuide,
|
||||
@@ -63,7 +65,13 @@ export function AppShell({
|
||||
<span className="app-guide-launcher-label">新手引导</span>
|
||||
</button>
|
||||
<div className="app-rail-account">
|
||||
<AccountSummary selfInfo={selfInfo} dbReady={dbReady} compact onClick={onOpenSettings} />
|
||||
<AccountSummary
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
compact
|
||||
onClick={onOpenSettings}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
<main className="app-shell-main" aria-label={activeItem?.label || '工作区'}>
|
||||
|
||||
@@ -14,6 +14,7 @@ interface ReportHistorySidebarProps {
|
||||
selectedReportId: string | null
|
||||
selfInfo: SelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
onSelectReport: (reportId: string) => void
|
||||
onCreateReport: () => void
|
||||
onDeleteReport: (reportId: string) => Promise<{ success: boolean; error?: string }>
|
||||
@@ -80,6 +81,7 @@ export function ReportHistorySidebar({
|
||||
selectedReportId,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
onSelectReport,
|
||||
onCreateReport,
|
||||
onDeleteReport,
|
||||
@@ -202,7 +204,12 @@ export function ReportHistorySidebar({
|
||||
)}
|
||||
</div>
|
||||
<div className="report-history-account">
|
||||
<AccountSummary selfInfo={selfInfo} dbReady={dbReady} onClick={onOpenSettings} />
|
||||
<AccountSummary
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
onClick={onOpenSettings}
|
||||
/>
|
||||
</div>
|
||||
{pendingDelete && (
|
||||
<div className="report-delete-confirm" role="dialog" aria-modal="true">
|
||||
|
||||
@@ -14,6 +14,7 @@ interface ReportSourceSidebarProps {
|
||||
selectedContact: Contact | null
|
||||
selfInfo: SelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
onSelectContact: (contact: Contact) => void
|
||||
onOpenSettings: () => void
|
||||
}
|
||||
@@ -23,6 +24,7 @@ export function ReportSourceSidebar({
|
||||
selectedContact,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
onSelectContact,
|
||||
onOpenSettings
|
||||
}: ReportSourceSidebarProps): React.ReactElement {
|
||||
@@ -95,6 +97,7 @@ export function ReportSourceSidebar({
|
||||
<AccountSummary
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
onClick={onOpenSettings}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ export function SettingsWorkspace({
|
||||
onCategoryChange,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
dbKey,
|
||||
onDbKeyChange,
|
||||
onDatabaseConnectionChange,
|
||||
@@ -35,6 +36,7 @@ export function SettingsWorkspace({
|
||||
onCategoryChange: (id: SettingsCategoryId) => void
|
||||
selfInfo: SettingsSelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
dbKey: string
|
||||
onDbKeyChange: (key: string) => void
|
||||
onDatabaseConnectionChange: (connected: boolean) => void
|
||||
@@ -47,6 +49,54 @@ export function SettingsWorkspace({
|
||||
onOpenSettings: () => void
|
||||
onAppearanceChange: (settings: { theme: 'system' | 'light' | 'dark'; compactMode: boolean }) => void
|
||||
}): React.ReactElement {
|
||||
const renderSelectedPage = (): React.ReactElement => {
|
||||
switch (selectedCategory) {
|
||||
case 'account-database':
|
||||
return (
|
||||
<AccountDatabasePage
|
||||
dbKey={dbKey}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
selfInfo={selfInfo}
|
||||
onNotice={onNotice}
|
||||
/>
|
||||
)
|
||||
case 'database-key':
|
||||
return (
|
||||
<DatabaseKeyPage
|
||||
dbKey={dbKey}
|
||||
dbReady={dbReady}
|
||||
selfInfo={selfInfo}
|
||||
onDbKeyChange={onDbKeyChange}
|
||||
onDatabaseConnectionChange={onDatabaseConnectionChange}
|
||||
onSelfInfoChange={onSelfInfoChange}
|
||||
onContactsChange={onContactsChange}
|
||||
onFilteredContactsChange={onFilteredContactsChange}
|
||||
onReturnToLogin={onReturnToLogin}
|
||||
onNotice={onNotice}
|
||||
/>
|
||||
)
|
||||
case 'image-key':
|
||||
return <ImageDecryptionPage selfInfo={selfInfo} onNotice={onNotice} />
|
||||
case 'ai-model':
|
||||
return <AIModelPage onRuntimeChange={onAIRuntimeChange} onNotice={onNotice} />
|
||||
case 'recall-protection':
|
||||
return <RecallProtectionPage onNotice={onNotice} />
|
||||
case 'advanced':
|
||||
return <AdvancedPage onNotice={onNotice} />
|
||||
case 'cache-cleanup':
|
||||
return <CacheCleanupPage onNotice={onNotice} />
|
||||
case 'appearance':
|
||||
return (
|
||||
<AppearancePage onNotice={onNotice} onAppearanceChange={onAppearanceChange} />
|
||||
)
|
||||
case 'about':
|
||||
return <AboutPage onNotice={onNotice} />
|
||||
default:
|
||||
return <SettingsEmptyState label={SETTINGS_CATEGORY_LABELS[selectedCategory]} />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="settings-workspace">
|
||||
<SettingsSidebar
|
||||
@@ -54,70 +104,10 @@ export function SettingsWorkspace({
|
||||
onSelect={onCategoryChange}
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
<div
|
||||
className={`settings-page-panel ${selectedCategory === 'account-database' ? 'active' : ''}`}
|
||||
>
|
||||
<AccountDatabasePage
|
||||
dbKey={dbKey}
|
||||
dbReady={dbReady}
|
||||
selfInfo={selfInfo}
|
||||
onNotice={onNotice}
|
||||
/>
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'database-key' ? 'active' : ''}`}>
|
||||
<DatabaseKeyPage
|
||||
dbKey={dbKey}
|
||||
dbReady={dbReady}
|
||||
selfInfo={selfInfo}
|
||||
onDbKeyChange={onDbKeyChange}
|
||||
onDatabaseConnectionChange={onDatabaseConnectionChange}
|
||||
onSelfInfoChange={onSelfInfoChange}
|
||||
onContactsChange={onContactsChange}
|
||||
onFilteredContactsChange={onFilteredContactsChange}
|
||||
onReturnToLogin={onReturnToLogin}
|
||||
onNotice={onNotice}
|
||||
/>
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'image-key' ? 'active' : ''}`}>
|
||||
<ImageDecryptionPage selfInfo={selfInfo} onNotice={onNotice} />
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'ai-model' ? 'active' : ''}`}>
|
||||
<AIModelPage onRuntimeChange={onAIRuntimeChange} onNotice={onNotice} />
|
||||
</div>
|
||||
<div
|
||||
className={`settings-page-panel ${selectedCategory === 'recall-protection' ? 'active' : ''}`}
|
||||
>
|
||||
<RecallProtectionPage onNotice={onNotice} />
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'advanced' ? 'active' : ''}`}>
|
||||
<AdvancedPage onNotice={onNotice} />
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'cache-cleanup' ? 'active' : ''}`}>
|
||||
<CacheCleanupPage onNotice={onNotice} />
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'appearance' ? 'active' : ''}`}>
|
||||
<AppearancePage onNotice={onNotice} onAppearanceChange={onAppearanceChange} />
|
||||
</div>
|
||||
<div className={`settings-page-panel ${selectedCategory === 'about' ? 'active' : ''}`}>
|
||||
<AboutPage onNotice={onNotice} />
|
||||
</div>
|
||||
{![
|
||||
'account-database',
|
||||
'database-key',
|
||||
'image-key',
|
||||
'ai-model',
|
||||
'recall-protection',
|
||||
'advanced',
|
||||
'cache-cleanup',
|
||||
'appearance',
|
||||
'about'
|
||||
].includes(selectedCategory) && (
|
||||
<div className="settings-page-panel active">
|
||||
<SettingsEmptyState label={SETTINGS_CATEGORY_LABELS[selectedCategory]} />
|
||||
</div>
|
||||
)}
|
||||
<div className="settings-page-panel active">{renderSelectedPage()}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import type { ConnectionCheckState } from './types'
|
||||
export function useAccountDatabaseController({
|
||||
dbKey,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
selfInfo,
|
||||
onNotice
|
||||
}: {
|
||||
dbKey: string
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
selfInfo: SettingsSelfInfo | null
|
||||
onNotice: (message: string) => void
|
||||
}) {
|
||||
@@ -28,7 +30,7 @@ export function useAccountDatabaseController({
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
void window.api
|
||||
.getImageDecryptionStatus()
|
||||
.getImageKeyConfig()
|
||||
.then((result) => {
|
||||
if (active) setHasImageKey(result.configured)
|
||||
})
|
||||
@@ -58,8 +60,11 @@ export function useAccountDatabaseController({
|
||||
[checkState, dbKey, dbReady, hasImageKey, selfInfo]
|
||||
)
|
||||
const connectionStatus = useMemo(
|
||||
() => getConnectionOverviewStatus({ dbReady, checkState, diagnostics }),
|
||||
[checkState, dbReady, diagnostics]
|
||||
() =>
|
||||
dbConnecting
|
||||
? ('checking' as const)
|
||||
: getConnectionOverviewStatus({ dbReady, checkState, diagnostics }),
|
||||
[checkState, dbConnecting, dbReady, diagnostics]
|
||||
)
|
||||
const lastCheckedLabel = formatConnectionCheckedAt(checkState, clock)
|
||||
|
||||
@@ -121,7 +126,7 @@ export function useAccountDatabaseController({
|
||||
diagnostics,
|
||||
connectionStatus,
|
||||
checkState,
|
||||
isChecking: checkState.status === 'checking',
|
||||
isChecking: dbConnecting || checkState.status === 'checking',
|
||||
lastCheckedLabel,
|
||||
testConnection,
|
||||
openAccountDirectory,
|
||||
|
||||
@@ -8,12 +8,14 @@ export function SettingsSidebar({
|
||||
onSelect,
|
||||
selfInfo,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
onOpenSettings
|
||||
}: {
|
||||
selectedId: SettingsCategoryId
|
||||
onSelect: (id: SettingsCategoryId) => void
|
||||
selfInfo: SettingsSelfInfo | null
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
onOpenSettings: () => void
|
||||
}): React.ReactElement {
|
||||
const [keyword, setKeyword] = useState('')
|
||||
@@ -57,7 +59,12 @@ export function SettingsSidebar({
|
||||
))}
|
||||
</div>
|
||||
<div className="settings-sidebar-account">
|
||||
<AccountSummary selfInfo={selfInfo} dbReady={dbReady} onClick={onOpenSettings} />
|
||||
<AccountSummary
|
||||
selfInfo={selfInfo}
|
||||
dbReady={dbReady}
|
||||
dbConnecting={dbConnecting}
|
||||
onClick={onOpenSettings}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
|
||||
@@ -17,15 +17,23 @@ const STATUS_LABELS: Record<ConnectionOverviewStatus, string> = {
|
||||
export function AccountDatabasePage({
|
||||
dbKey,
|
||||
dbReady,
|
||||
dbConnecting = false,
|
||||
selfInfo,
|
||||
onNotice
|
||||
}: {
|
||||
dbKey: string
|
||||
dbReady: boolean
|
||||
dbConnecting?: boolean
|
||||
selfInfo: SettingsSelfInfo | null
|
||||
onNotice: (message: string) => void
|
||||
}): React.ReactElement {
|
||||
const controller = useAccountDatabaseController({ dbKey, dbReady, selfInfo, onNotice })
|
||||
const controller = useAccountDatabaseController({
|
||||
dbKey,
|
||||
dbReady,
|
||||
dbConnecting,
|
||||
selfInfo,
|
||||
onNotice
|
||||
})
|
||||
const [autoLogin, setAutoLogin] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+8
@@ -253,6 +253,10 @@ body {
|
||||
&.ready {
|
||||
background: var(--wxex-success);
|
||||
}
|
||||
|
||||
&.connecting {
|
||||
background: var(--wxex-brand);
|
||||
}
|
||||
}
|
||||
|
||||
.account-summary:not(.compact) .account-summary-avatar .account-summary-status {
|
||||
@@ -306,6 +310,10 @@ body {
|
||||
&.ready {
|
||||
background: var(--wxex-success);
|
||||
}
|
||||
|
||||
&.connecting {
|
||||
background: var(--wxex-brand);
|
||||
}
|
||||
}
|
||||
|
||||
.account-summary-settings {
|
||||
|
||||
Reference in New Issue
Block a user