Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion app/admin/settings/preferences/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '~
import { Input } from '~/components/ui/input'
import { Label } from '~/components/ui/label'
import { CopyIcon } from '~/components/icons/copy'
import { normalizeDefaultTheme } from '~/lib/utils/theme'

export default function Preferences() {
const [title, setTitle] = useState('')
Expand All @@ -30,6 +31,7 @@ export default function Preferences() {
const [maxUploadFiles, setMaxUploadFiles] = useState('5')
const [customIndexOriginEnable, setCustomIndexOriginEnable] = useState(false)
const [adminImagesPerPage, setAdminImagesPerPage] = useState('8')
const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>('light')
const t = useTranslations()

const { data, isValidating, isLoading } = useSWR<{ config_key: string, config_value: string }[]>('/api/v1/settings/custom-info', fetcher)
Expand Down Expand Up @@ -77,7 +79,8 @@ export default function Preferences() {
umamiAnalytics,
maxUploadFiles: maxFiles,
customIndexOriginEnable,
adminImagesPerPage: imagesPerPage
adminImagesPerPage: imagesPerPage,
defaultTheme,
}),
}).then(res => res.json())
toast.success('修改成功!')
Expand All @@ -104,6 +107,7 @@ export default function Preferences() {
setMaxUploadFiles(data?.find((item) => item.config_key === 'max_upload_files')?.config_value || '5')
setCustomIndexOriginEnable(data?.find((item) => item.config_key === 'custom_index_origin_enable')?.config_value.toString() === 'true' || false)
setAdminImagesPerPage(data?.find((item) => item.config_key === 'admin_images_per_page')?.config_value || '8')
setDefaultTheme(normalizeDefaultTheme(data?.find((item) => item.config_key === 'default_theme')?.config_value))
}, [data])

return (
Expand Down Expand Up @@ -240,6 +244,20 @@ export default function Preferences() {
</SelectContent>
</Select>
</div>
<div className="w-full max-w-sm space-y-1">
<Label htmlFor="defaultThemeSelect">{t('Preferences.defaultTheme')}</Label>
<Select value={defaultTheme} onValueChange={(value) => setDefaultTheme(value as 'light' | 'dark' | 'system')}>
<SelectTrigger id="defaultThemeSelect" className="w-full cursor-pointer">
<SelectValue placeholder={t('Preferences.defaultTheme')} />
</SelectTrigger>
<SelectContent className="cursor-pointer">
<SelectItem className="cursor-pointer" value="light">{t('Theme.light')}</SelectItem>
<SelectItem className="cursor-pointer" value="dark">{t('Theme.dark')}</SelectItem>
<SelectItem className="cursor-pointer" value="system">{t('Theme.system')}</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">{t('Preferences.defaultThemeDescription')}</p>
</div>
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor="previewQuality">{t('Preferences.previewQuality')}</Label>
<Input
Expand Down
7 changes: 5 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NextIntlClientProvider } from 'next-intl'
import { getLocale, getMessages } from 'next-intl/server'
import { ConfigStoreProvider } from '~/app/providers/config-store-providers'
import Script from 'next/script'
import { normalizeDefaultTheme } from '~/lib/utils/theme'

const sourceSerif4 = Source_Serif_4({
subsets: ['latin'],
Expand Down Expand Up @@ -93,10 +94,12 @@ export default async function RootLayout({
const messages = await getMessages()

const data = await fetchConfigsByKeys([
'default_theme',
'umami_analytics',
'umami_host'
])

const defaultTheme = normalizeDefaultTheme(data?.find((item: ConfigItem) => item.config_key === 'default_theme')?.config_value)
const umamiHost = data?.find((item: ConfigItem) => item.config_key === 'umami_host')?.config_value || 'https://cloud.umami.is/script.js'
const umamiAnalytics = data?.find((item: ConfigItem) => item.config_key === 'umami_analytics')?.config_value

Expand All @@ -114,7 +117,7 @@ export default async function RootLayout({
<NextIntlClientProvider messages={messages}>
<ConfigStoreProvider>
<ButtonStoreProvider>
<ThemeProvider>
<ThemeProvider defaultTheme={defaultTheme}>
<ToasterProviders/>
<ProgressBarProviders>
{children}
Expand All @@ -135,4 +138,4 @@ export default async function RootLayout({
</body>
</html>
)
}
}
7 changes: 4 additions & 3 deletions app/providers/next-ui-providers.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'
import type { DefaultTheme } from '~/lib/utils/theme'

export function ThemeProvider({children}: { children: React.ReactNode }) {
export function ThemeProvider({children, defaultTheme}: { children: React.ReactNode, defaultTheme: DefaultTheme }) {
return (
<NextThemesProvider attribute="class" defaultTheme="system">
<NextThemesProvider attribute="class" defaultTheme={defaultTheme}>
{children}
</NextThemesProvider>
)
}
}
10 changes: 8 additions & 2 deletions hono/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Config } from '~/types'
import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'
import { updateOpenListConfig, updateCustomInfo, updateR2Config, updateS3Config } from '~/server/db/operate/configs'
import { normalizeDefaultTheme } from '~/lib/utils/theme'

const app = new Hono()

Expand All @@ -25,7 +26,8 @@ app.get('/custom-info', async (c) => {
'umami_analytics',
'max_upload_files',
'custom_index_origin_enable',
'admin_images_per_page'
'admin_images_per_page',
'default_theme'
])
return c.json({ code: 200, message: 'Success', data })
} catch (error) {
Expand Down Expand Up @@ -153,9 +155,13 @@ app.put('/custom-info', async (c) => {
maxUploadFiles: number
customIndexOriginEnable: boolean
adminImagesPerPage: number
defaultTheme: string
}
try {
await updateCustomInfo(query)
await updateCustomInfo({
...query,
defaultTheme: normalizeDefaultTheme(query.defaultTheme),
})
return c.json({
code: 200,
message: 'Success'
Expand Down
9 changes: 9 additions & 0 deletions lib/utils/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const DEFAULT_THEME_VALUES = ['light', 'dark', 'system'] as const

export type DefaultTheme = (typeof DEFAULT_THEME_VALUES)[number]

export function normalizeDefaultTheme(value: string | null | undefined): DefaultTheme {
return value === 'light' || value === 'dark' || value === 'system'
? value
: 'light'
}
7 changes: 6 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@
"maxWidth": "Maximum Width for Preview Images (positive integer)",
"inputMaxWidth": "Please enter the maximum width limit for preview images, a positive integer.",
"indexThemeSelect": "Select Home Page Theme",
"defaultTheme": "Default Theme",
"defaultThemeDescription": "Applies to visitors who have not chosen a theme yet.",
"customIndexDownloadEnable": "Show Download Button",
"umamiHost": "Umami Website Host",
"umamiAnalytics": "Umami Website ID",
Expand Down Expand Up @@ -407,7 +409,10 @@
"Theme": {
"indexDefaultStyle": "Default",
"indexSimpleStyle": "Single Column",
"indexPolaroidStyle": "Polaroid"
"indexPolaroidStyle": "Polaroid",
"light": "Light",
"dark": "Dark",
"system": "System"
},
"Filter": {
"title": "Filter",
Expand Down
7 changes: 6 additions & 1 deletion messages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@
"maxWidth": "プレビュー画像の最大幅(正の整数)",
"inputMaxWidth": "プレビュー画像の最大幅制限を入力してください、正の整数",
"indexThemeSelect": "トップページテーマを選択",
"defaultTheme": "デフォルトテーマ",
"defaultThemeDescription": "まだテーマを選択していない訪問者に適用されます。",
"customIndexDownloadEnable": "ダウンロードボタンを表示",
"umamiHost": "Umami Website Host",
"umamiAnalytics": "Umami Website ID",
Expand Down Expand Up @@ -407,7 +409,10 @@
"Theme": {
"indexDefaultStyle": "既定",
"indexSimpleStyle": "単列",
"indexPolaroidStyle": "ポラロイド"
"indexPolaroidStyle": "ポラロイド",
"light": "ライト",
"dark": "ダーク",
"system": "システム"
},
"Filter": {
"title": "フィルター",
Expand Down
7 changes: 6 additions & 1 deletion messages/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@
"maxWidth": "預覽圖最大寬度(正整數)",
"inputMaxWidth": "請輸入預覽圖最大寬度限制,正整數",
"indexThemeSelect": "選擇首頁主題",
"defaultTheme": "預設主題",
"defaultThemeDescription": "對尚未手動選擇主題的訪客生效。",
"customIndexDownloadEnable": "下載按鈕顯示",
"umamiHost": "Umami Website Host",
"umamiAnalytics": "Umami Website ID",
Expand Down Expand Up @@ -407,7 +409,10 @@
"Theme": {
"indexDefaultStyle": "預設",
"indexSimpleStyle": "單欄",
"indexPolaroidStyle": "寶麗來"
"indexPolaroidStyle": "寶麗來",
"light": "淺色",
"dark": "深色",
"system": "跟隨系統"
},
"Filter": {
"title": "篩選",
Expand Down
7 changes: 6 additions & 1 deletion messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@
"maxWidth": "预览图最大宽度(正整数)",
"inputMaxWidth": "请输入预览图最大宽度限制,正整数",
"indexThemeSelect": "选择首页主题",
"defaultTheme": "默认主题",
"defaultThemeDescription": "对尚未手动选择主题的访客生效。",
"customIndexDownloadEnable": "下载按钮显示",
"umamiHost": "Umami Website Host",
"umamiAnalytics": "Umami Website ID",
Expand Down Expand Up @@ -407,7 +409,10 @@
"Theme": {
"indexDefaultStyle": "默认",
"indexSimpleStyle": "单列",
"indexPolaroidStyle": "宝丽来"
"indexPolaroidStyle": "宝丽来",
"light": "浅色",
"dark": "深色",
"system": "跟随系统"
},
"Filter": {
"title": "筛选",
Expand Down
3 changes: 2 additions & 1 deletion prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const INITIAL_CONFIGS = [
{ config_key: 'max_upload_files', config_value: '5', detail: '最大上传文件数量' },
{ config_key: 'umami_analytics', config_value: '', detail: 'Umami Website ID.' },
{ config_key: 'umami_host', config_value: '', detail: 'Umami Cloud Analytics' },
{ config_key: 'default_theme', config_value: 'light', detail: 'Default theme for users without a saved preference.' },
{ config_key: 'admin_images_per_page', config_value: '8', detail: '管理界面每页显示的图片数量' },
{ config_key: 'daily_enabled', config_value: 'false', detail: '是否启用 Daily 首页模式' },
{ config_key: 'daily_refresh_interval', config_value: '24', detail: 'Daily 首页刷新间隔(小时):6/12/24/168' },
Expand Down Expand Up @@ -69,4 +70,4 @@ main()
})
.finally(async () => {
await prisma.$disconnect()
})
})
13 changes: 13 additions & 0 deletions server/db/operate/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use server'

import { db } from '~/server/lib/db'
import { normalizeDefaultTheme } from '~/lib/utils/theme'

/**
* 更新 S3 配置
Expand Down Expand Up @@ -88,6 +89,7 @@ export async function updateCustomInfo(payload: {
maxUploadFiles: number
customIndexOriginEnable: boolean
adminImagesPerPage: number
defaultTheme: string
}) {
const {
title,
Expand All @@ -105,7 +107,9 @@ export async function updateCustomInfo(payload: {
maxUploadFiles,
customIndexOriginEnable,
adminImagesPerPage,
defaultTheme,
} = payload
const normalizedDefaultTheme = normalizeDefaultTheme(defaultTheme)

const updates = [
db.configs.update({ where: { config_key: 'custom_title' }, data: { config_value: title, updatedAt: new Date() } }),
Expand All @@ -121,6 +125,15 @@ export async function updateCustomInfo(payload: {
db.configs.update({ where: { config_key: 'max_upload_files' }, data: { config_value: maxUploadFiles.toString(), updatedAt: new Date() } }),
db.configs.update({ where: { config_key: 'custom_index_origin_enable' }, data: { config_value: customIndexOriginEnable ? 'true' : 'false', updatedAt: new Date() } }),
db.configs.update({ where: { config_key: 'admin_images_per_page' }, data: { config_value: adminImagesPerPage.toString(), updatedAt: new Date() } }),
db.configs.upsert({
where: { config_key: 'default_theme' },
update: { config_value: normalizedDefaultTheme, updatedAt: new Date() },
create: {
config_key: 'default_theme',
config_value: normalizedDefaultTheme,
detail: 'Default theme for users without a saved preference.',
},
}),
]

if (previewImageMaxWidth > 0) {
Expand Down
Loading