From b41139b9417ea9aeddc27def29b864fd8b03742d Mon Sep 17 00:00:00 2001 From: Fox_block Date: Fri, 16 Jan 2026 18:57:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增结算历史页面,支持查看历史结算记录和排行榜 - 实现结算功能,可将当前积分记录归档并清零学生分数 - 优化学生搜索功能,支持拼音匹配 - 更新应用图标和品牌标识 - 改进主题系统,支持自定义侧边栏文本颜色 - 重构积分管理表单,优化用户体验 - 修复若干已知问题并改进代码结构 --- .gitignore | 1 + electron-builder.yml | 17 +- package.json | 1 + pnpm-lock.yaml | 8 + resources/SecScore.svg | 37 +++++ resources/SecScore_logo.ico | Bin 0 -> 410598 bytes resources/icon.png | Bin 35949 -> 0 bytes scripts/afterPack.cjs | 10 ++ src/main/db/DbManager.ts | 18 ++ src/main/index.ts | 98 +++++++++-- src/main/repos/EventRepository.ts | 9 +- src/main/repos/SettlementRepository.ts | 102 ++++++++++++ src/preload/index.ts | 6 + src/preload/types.ts | 12 ++ src/renderer/index.html | 2 +- src/renderer/src/App.tsx | 57 ++----- src/renderer/src/assets/main.css | 40 +++++ src/renderer/src/components/ReasonManager.tsx | 10 ++ src/renderer/src/components/ScoreManager.tsx | 84 +++++++++- src/renderer/src/components/Settings.tsx | 85 +++++++--- .../src/components/SettlementHistory.tsx | 155 ++++++++++++++++++ .../src/components/StudentManager.tsx | 9 +- src/renderer/src/contexts/ThemeContext.tsx | 8 +- src/renderer/src/utils/color.ts | 151 +++++++++++++++++ themes/dark.json | 3 +- 25 files changed, 828 insertions(+), 95 deletions(-) create mode 100644 resources/SecScore.svg create mode 100644 resources/SecScore_logo.ico delete mode 100644 resources/icon.png create mode 100644 scripts/afterPack.cjs create mode 100644 src/main/repos/SettlementRepository.ts create mode 100644 src/renderer/src/components/SettlementHistory.tsx create mode 100644 src/renderer/src/utils/color.ts diff --git a/.gitignore b/.gitignore index caed81c..1ab4fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ out .DS_Store .eslintcache *.log* +logs build db.sqlite diff --git a/electron-builder.yml b/electron-builder.yml index a0697c7..a770c96 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -1,5 +1,5 @@ -appId: com.electron.app -productName: secscore +appId: com.secscore.app +productName: SecScore directories: buildResources: build files: @@ -11,13 +11,24 @@ files: - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' asarUnpack: - resources/** +afterPack: ./scripts/afterPack.cjs +extraFiles: + - from: themes + to: themes +extraResources: + - from: resources + to: assets win: - executableName: secscore + target: + - zip + executableName: SecScore + icon: resources/SecScore_logo.ico nsis: artifactName: ${name}-${version}-setup.${ext} shortcutName: ${productName} uninstallDisplayName: ${productName} createDesktopShortcut: always + allowToChangeInstallationDirectory: true mac: entitlementsInherit: build/entitlements.mac.plist extendInfo: diff --git a/package.json b/package.json index 01fdf0f..8a08fdb 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "better-sqlite3": "^12.6.0", "chokidar": "^5.0.0", "electron-updater": "^6.3.9", + "pinyin-pro": "^3.27.0", "tdesign-react": "^1.16.3", "uuid": "^13.0.0", "ws": "^8.19.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 190ff7e..79a9252 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: electron-updater: specifier: ^6.3.9 version: 6.7.3 + pinyin-pro: + specifier: ^3.27.0 + version: 3.27.0 tdesign-react: specifier: ^1.16.3 version: 1.16.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -2351,6 +2354,9 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pinyin-pro@3.27.0: + resolution: {integrity: sha512-Osdgjwe7Rm17N2paDMM47yW+jUIUH3+0RGo8QP39ZTLpTaJVDK0T58hOLaMQJbcMmAebVuK2ePunTEVEx1clNQ==} + plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -5514,6 +5520,8 @@ snapshots: picomatch@4.0.3: {} + pinyin-pro@3.27.0: {} + plist@3.1.0: dependencies: '@xmldom/xmldom': 0.8.11 diff --git a/resources/SecScore.svg b/resources/SecScore.svg new file mode 100644 index 0000000..39a5ac6 --- /dev/null +++ b/resources/SecScore.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/SecScore_logo.ico b/resources/SecScore_logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..3b5807162387bfc65d0a4ff6bc9e61bb84592e41 GIT binary patch literal 410598 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Êm$WT@m8~{ziY! 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 literal 0 HcmV?d00001 diff --git a/resources/icon.png b/resources/icon.png deleted file mode 100644 index cf9e8b2c87b5c18ac0b26913af6fd3ed00ec3bfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35949 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

(|V7O%OOlsc*vU+P^72X!U7K_eX*CG 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 E`T9pxN5i1O>p>VAy6%lIREYyb! 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 Kl11VsAO0U5dJ04U diff --git a/scripts/afterPack.cjs b/scripts/afterPack.cjs new file mode 100644 index 0000000..049d355 --- /dev/null +++ b/scripts/afterPack.cjs @@ -0,0 +1,10 @@ +const fs = require('fs') +const path = require('path') + +const afterPack = async (context) => { + const appOutDir = context.appOutDir + fs.mkdirSync(path.join(appOutDir, 'data'), { recursive: true }) +} + +module.exports = afterPack +module.exports.default = afterPack diff --git a/src/main/db/DbManager.ts b/src/main/db/DbManager.ts index 15bda42..684f063 100644 --- a/src/main/db/DbManager.ts +++ b/src/main/db/DbManager.ts @@ -51,6 +51,24 @@ export class DbManager { ) `) + this.db.exec(` + CREATE TABLE IF NOT EXISTS settlements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + start_time DATETIME NOT NULL, + end_time DATETIME NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `) + + const scoreEventColumns = this.db.prepare(`PRAGMA table_info(score_events)`).all() as { + name: string + }[] + const hasSettlementId = scoreEventColumns.some((c) => c.name === 'settlement_id') + if (!hasSettlementId) { + this.db.exec(`ALTER TABLE score_events ADD COLUMN settlement_id INTEGER`) + this.db.exec(`CREATE INDEX IF NOT EXISTS idx_score_events_settlement_id ON score_events(settlement_id)`) + } + // 建立系统设置表 this.db.exec(` CREATE TABLE IF NOT EXISTS settings ( diff --git a/src/main/index.ts b/src/main/index.ts index 2cb8d38..bbbb36f 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,15 +1,16 @@ import { app, shell, BrowserWindow, ipcMain } from 'electron' -import { join } from 'path' +import { join, dirname } from 'path' import fs from 'fs' import crypto from 'crypto' import { electronApp, optimizer, is } from '@electron-toolkit/utils' -import icon from '../../resources/icon.png?asset' +import icon from '../../resources/SecScore_logo.ico?asset' import { ThemeService } from './services/ThemeService' import { LoggerService, LogLevel } from './services/LoggerService' import { DbManager } from './db/DbManager' import { StudentRepository } from './repos/StudentRepository' import { ReasonRepository } from './repos/ReasonRepository' import { EventRepository } from './repos/EventRepository' +import { SettlementRepository } from './repos/SettlementRepository' import { WsClient } from './services/WsClient' import { SyncEngine } from './services/SyncEngine' @@ -18,9 +19,10 @@ function createWindow(): void { const mainWindow = new BrowserWindow({ width: 900, height: 670, + title: 'SecScore', show: false, autoHideMenuBar: true, - ...(process.platform === 'linux' ? { icon } : {}), + icon, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false @@ -59,22 +61,31 @@ app.whenReady().then(() => { optimizer.watchWindowShortcuts(window) }) - // Base data directory (production 使用单独目录) - const dataRoot = is.dev ? process.cwd() : join(app.getPath('userData'), 'secscore-data') - if (!fs.existsSync(dataRoot)) { - fs.mkdirSync(dataRoot, { recursive: true }) + const appRoot = is.dev ? process.cwd() : dirname(process.execPath) + + const ensureWritableDir = (preferred: string, fallback: string) => { + try { + if (!fs.existsSync(preferred)) fs.mkdirSync(preferred, { recursive: true }) + return preferred + } catch { + if (!fs.existsSync(fallback)) fs.mkdirSync(fallback, { recursive: true }) + return fallback + } } + const dataRoot = is.dev + ? process.cwd() + : ensureWritableDir(join(appRoot, 'data'), join(app.getPath('userData'), 'secscore-data')) + // Initialize Logger const logDir = is.dev ? join(process.cwd(), 'logs') : join(dataRoot, 'logs') const logger = new LoggerService(logDir) logger.info('Application starting...') - // Initialize Themes (prod: 使用单独目录并从内置模板拷贝) - const themeDir = is.dev ? join(process.cwd(), 'themes') : join(dataRoot, 'themes') - if (!fs.existsSync(themeDir)) { - fs.mkdirSync(themeDir, { recursive: true }) - } + const themeDir = is.dev + ? join(process.cwd(), 'themes') + : ensureWritableDir(join(appRoot, 'themes'), join(dataRoot, 'themes')) + if (!is.dev) { try { const existing = fs.readdirSync(themeDir).filter((f) => f.toLowerCase().endsWith('.json')) @@ -143,6 +154,7 @@ app.whenReady().then(() => { const studentRepo = new StudentRepository(dbManager.getDb()) const reasonRepo = new ReasonRepository(dbManager.getDb()) const eventRepo = new EventRepository(dbManager.getDb()) + const settlementRepo = new SettlementRepository(dbManager.getDb()) const SETTINGS_SECURITY_ADMIN = 'security_admin_password' const SETTINGS_SECURITY_POINTS = 'security_points_password' @@ -356,6 +368,7 @@ app.whenReady().then(() => { .prepare( `SELECT * FROM score_events WHERE student_name = ? + AND settlement_id IS NULL AND julianday(event_time) >= julianday(?) ORDER BY event_time DESC LIMIT ?` @@ -366,6 +379,7 @@ app.whenReady().then(() => { .prepare( `SELECT * FROM score_events WHERE student_name = ? + AND settlement_id IS NULL ORDER BY event_time DESC LIMIT ?` ) @@ -409,6 +423,7 @@ app.whenReady().then(() => { FROM students s LEFT JOIN score_events e ON e.student_name = s.name + AND e.settlement_id IS NULL AND julianday(e.event_time) >= julianday(?) GROUP BY s.id, s.name, s.score ORDER BY s.score DESC, range_change DESC, s.name ASC` @@ -421,6 +436,34 @@ app.whenReady().then(() => { } }) + ipcMain.handle('db:settlement:query', async () => { + try { + return { success: true, data: settlementRepo.findAll() } + } catch (err: any) { + return { success: false, message: err.message } + } + }) + + ipcMain.handle('db:settlement:create', async (event) => { + try { + if (!requirePermission(event, 'admin')) return { success: false, message: 'Permission denied' } + const data = settlementRepo.settleNow() + return { success: true, data } + } catch (err: any) { + return { success: false, message: err.message } + } + }) + + ipcMain.handle('db:settlement:leaderboard', async (_, params) => { + try { + const settlementId = Number(params?.settlement_id) + if (!Number.isFinite(settlementId)) return { success: false, message: 'Invalid settlement_id' } + return { success: true, data: settlementRepo.getLeaderboard(settlementId) } + } catch (err: any) { + return { success: false, message: err.message } + } + }) + // Settings IPC ipcMain.handle('db:getSettings', () => { const rows = dbManager.getDb().prepare('SELECT key, value FROM settings').all() as { @@ -624,15 +667,21 @@ app.whenReady().then(() => { .all() const events = db .prepare( - 'SELECT id, uuid, student_name, reason_content, delta, val_prev, val_curr, event_time, sync_state, remote_id FROM score_events' + 'SELECT id, uuid, student_name, reason_content, delta, val_prev, val_curr, event_time, settlement_id, sync_state, remote_id FROM score_events' ) .all() + const settlements = db + .prepare('SELECT id, start_time, end_time, created_at FROM settlements ORDER BY id ASC') + .all() const settingsRows = db.prepare('SELECT key, value FROM settings').all() as { key: string value: string }[] const settings = settingsRows.filter((r) => !String(r.key).startsWith('security_')) - return { success: true, data: JSON.stringify({ students, reasons, events, settings }, null, 2) } + return { + success: true, + data: JSON.stringify({ students, reasons, events, settlements, settings }, null, 2) + } }) ipcMain.handle('data:importJson', (event, jsonText: string) => { @@ -647,12 +696,14 @@ app.whenReady().then(() => { const students = Array.isArray(parsed?.students) ? parsed.students : [] const reasons = Array.isArray(parsed?.reasons) ? parsed.reasons : [] const events = Array.isArray(parsed?.events) ? parsed.events : [] + const settlements = Array.isArray(parsed?.settlements) ? parsed.settlements : [] const settings = Array.isArray(parsed?.settings) ? parsed.settings : [] const db = dbManager.getDb() try { db.transaction(() => { db.prepare('DELETE FROM score_events').run() + db.prepare('DELETE FROM settlements').run() db.prepare('DELETE FROM students').run() db.prepare('DELETE FROM reasons').run() db.prepare("DELETE FROM settings WHERE key NOT LIKE 'security_%'").run() @@ -688,8 +739,19 @@ app.whenReady().then(() => { } const insertEvent = db.prepare( - 'INSERT INTO score_events (uuid, student_name, reason_content, delta, val_prev, val_curr, event_time, sync_state, remote_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)' + 'INSERT INTO score_events (uuid, student_name, reason_content, delta, val_prev, val_curr, event_time, settlement_id, sync_state, remote_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' ) + const insertSettlement = db.prepare( + 'INSERT INTO settlements (id, start_time, end_time, created_at) VALUES (?, ?, ?, ?)' + ) + for (const s of settlements) { + const id = Number(s?.id) + const startTime = String(s?.start_time ?? '').trim() + const endTime = String(s?.end_time ?? '').trim() + const createdAt = String(s?.created_at ?? new Date().toISOString()) + if (!Number.isFinite(id) || !startTime || !endTime) continue + insertSettlement.run(id, startTime, endTime, createdAt) + } for (const e of events) { const uuid = String(e?.uuid ?? '').trim() const studentName = String(e?.student_name ?? '').trim() @@ -699,6 +761,11 @@ app.whenReady().then(() => { const valPrev = Number(e?.val_prev ?? 0) const valCurr = Number(e?.val_curr ?? 0) const eventTime = String(e?.event_time ?? new Date().toISOString()) + const settlementIdRaw = e?.settlement_id + const settlementId = + settlementIdRaw === null || settlementIdRaw === undefined + ? null + : Number(settlementIdRaw) const syncState = Number(e?.sync_state ?? 0) ? 1 : 0 const remoteId = e?.remote_id != null ? String(e.remote_id) : null insertEvent.run( @@ -709,6 +776,7 @@ app.whenReady().then(() => { Number.isFinite(valPrev) ? valPrev : 0, Number.isFinite(valCurr) ? valCurr : 0, eventTime, + Number.isFinite(settlementId as any) ? settlementId : null, syncState, remoteId ) diff --git a/src/main/repos/EventRepository.ts b/src/main/repos/EventRepository.ts index f959111..c269a3a 100644 --- a/src/main/repos/EventRepository.ts +++ b/src/main/repos/EventRepository.ts @@ -12,6 +12,7 @@ export interface ScoreEvent { val_curr: number event_time: string sync_state: number + settlement_id?: number | null remote_id?: string } @@ -23,6 +24,7 @@ export class EventRepository { .prepare( ` SELECT * FROM score_events + WHERE settlement_id IS NULL ORDER BY event_time DESC LIMIT ? ` @@ -91,9 +93,12 @@ export class EventRepository { this.db.transaction(() => { // 1. Get event info const event = this.db - .prepare('SELECT student_name, delta FROM score_events WHERE uuid = ?') - .get(uuid) as { student_name: string; delta: number } + .prepare('SELECT student_name, delta, settlement_id FROM score_events WHERE uuid = ?') + .get(uuid) as { student_name: string; delta: number; settlement_id: number | null } if (!event) return + if (event.settlement_id !== null && event.settlement_id !== undefined) { + throw new Error('该记录已结算,无法撤销') + } // 2. Revert student score this.db diff --git a/src/main/repos/SettlementRepository.ts b/src/main/repos/SettlementRepository.ts new file mode 100644 index 0000000..6963903 --- /dev/null +++ b/src/main/repos/SettlementRepository.ts @@ -0,0 +1,102 @@ +import { Database } from 'better-sqlite3' + +export interface SettlementSummary { + id: number + start_time: string + end_time: string + event_count: number +} + +export interface SettlementLeaderboardRow { + name: string + score: number +} + +export class SettlementRepository { + constructor(private db: Database) {} + + findAll(): SettlementSummary[] { + const rows = this.db + .prepare( + ` + SELECT + s.id as id, + s.start_time as start_time, + s.end_time as end_time, + ( + SELECT COUNT(1) + FROM score_events e + WHERE e.settlement_id = s.id + ) as event_count + FROM settlements s + ORDER BY julianday(s.end_time) DESC + ` + ) + .all() as SettlementSummary[] + return rows + } + + settleNow() { + return this.db.transaction(() => { + const unassigned = this.db + .prepare(`SELECT COUNT(1) as count FROM score_events WHERE settlement_id IS NULL`) + .get() as { count: number } + + const eventCount = Number(unassigned?.count ?? 0) + if (eventCount <= 0) { + throw new Error('暂无可结算记录') + } + + const endTime = new Date().toISOString() + const lastSettlement = this.db + .prepare(`SELECT end_time FROM settlements ORDER BY julianday(end_time) DESC LIMIT 1`) + .get() as { end_time: string } | undefined + + const minEvent = this.db + .prepare(`SELECT MIN(event_time) as min_time FROM score_events WHERE settlement_id IS NULL`) + .get() as { min_time: string } | undefined + + const startTime = lastSettlement?.end_time || minEvent?.min_time || endTime + + const info = this.db + .prepare(`INSERT INTO settlements (start_time, end_time) VALUES (?, ?)`) + .run(startTime, endTime) + const settlementId = info.lastInsertRowid as number + + this.db + .prepare(`UPDATE score_events SET settlement_id = ? WHERE settlement_id IS NULL`) + .run(settlementId) + + this.db.prepare(`UPDATE students SET score = 0, updated_at = CURRENT_TIMESTAMP`).run() + + return { settlementId, startTime, endTime, eventCount } + })() + } + + getLeaderboard(settlementId: number) { + const settlement = this.db + .prepare(`SELECT id, start_time, end_time FROM settlements WHERE id = ?`) + .get(settlementId) as { id: number; start_time: string; end_time: string } | undefined + + if (!settlement) { + throw new Error('结算记录不存在') + } + + const rows = this.db + .prepare( + ` + SELECT + e.student_name as name, + COALESCE(SUM(e.delta), 0) as score + FROM score_events e + WHERE e.settlement_id = ? + GROUP BY e.student_name + ORDER BY score DESC, name ASC + ` + ) + .all(settlementId) as SettlementLeaderboardRow[] + + return { settlement, rows } + } +} + diff --git a/src/preload/index.ts b/src/preload/index.ts index 4890059..fbfe185 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -32,6 +32,12 @@ const api = { queryEventsByStudent: (params: any) => ipcRenderer.invoke('db:event:queryByStudent', params), queryLeaderboard: (params: any) => ipcRenderer.invoke('db:leaderboard:query', params), + // Settlement + querySettlements: () => ipcRenderer.invoke('db:settlement:query'), + createSettlement: () => ipcRenderer.invoke('db:settlement:create'), + querySettlementLeaderboard: (params: any) => + ipcRenderer.invoke('db:settlement:leaderboard', params), + // Settings & Sync getSettings: () => ipcRenderer.invoke('db:getSettings'), updateSetting: (key: string, value: string) => ipcRenderer.invoke('db:updateSetting', key, value), diff --git a/src/preload/types.ts b/src/preload/types.ts index 8bf8a1a..412a558 100644 --- a/src/preload/types.ts +++ b/src/preload/types.ts @@ -50,6 +50,18 @@ export interface ElectronApi { range: 'today' | 'week' | 'month' }) => Promise> + // Settlement + querySettlements: () => + Promise> + createSettlement: () => + Promise> + querySettlementLeaderboard: (params: { settlement_id: number }) => Promise< + IpcResponse<{ + settlement: { id: number; start_time: string; end_time: string } + rows: { name: string; score: number }[] + }> + > + // Settings & Sync getSettings: () => Promise>> updateSetting: (key: string, value: string) => Promise> diff --git a/src/renderer/index.html b/src/renderer/index.html index e198e05..d47e868 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -2,7 +2,7 @@ - Electron + SecScore ('none') - const [appearanceBlur, setAppearanceBlur] = useState(0) useEffect(() => { const checkWizard = async () => { @@ -44,30 +42,9 @@ function MainContent(): React.JSX.Element { setHasAnyPassword(anyPwd) if (anyPwd && authRes.data.permission === 'view') setAuthVisible(true) } - const settingsRes = await (window as any).api.getSettings() - if (settingsRes?.success && settingsRes.data) { - const bg = settingsRes.data.appearance_background - const blur = Number(settingsRes.data.appearance_background_blur || 0) - setAppearanceBackground(bg === 'wavy-lines' ? 'wavy-lines' : 'none') - setAppearanceBlur(Number.isFinite(blur) ? blur : 0) - } } loadAuthAndSettings() - - const onSettingsUpdated = (e: any) => { - const key = e?.detail?.key - const value = e?.detail?.value - if (key === 'appearance_background') { - setAppearanceBackground(value === 'wavy-lines' ? 'wavy-lines' : 'none') - } - if (key === 'appearance_background_blur') { - const blur = Number(value || 0) - setAppearanceBlur(Number.isFinite(blur) ? blur : 0) - } - } - window.addEventListener('ss:settings-updated', onSettingsUpdated as any) - return () => window.removeEventListener('ss:settings-updated', onSettingsUpdated as any) }, []) const login = async () => { @@ -102,6 +79,8 @@ function MainContent(): React.JSX.Element { return case 'leaderboard': return + case 'settlements': + return case 'reasons': return case 'settings': @@ -122,30 +101,23 @@ function MainContent(): React.JSX.Element { return ( - {appearanceBackground !== 'none' && ( -

0 ? `blur(${appearanceBlur}px)` : undefined, - transform: appearanceBlur > 0 ? 'scale(1.05)' : undefined - }} - /> - )}