refactor: remove RSS support from social configuration

- Eliminated the RSS field from social settings in configuration files and UI schemas.
- Updated related documentation and localization files to reflect the removal of RSS support.
- Adjusted components to ensure compatibility with the updated social configuration.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-11-30 01:27:48 +08:00
parent 62fd908d35
commit a9703f34dc
15 changed files with 171 additions and 60 deletions

View File

@@ -103,7 +103,7 @@ Notes:
- `name` / `title` / `description` / `url` / `accentColor` - `name` / `title` / `description` / `url` / `accentColor`
- `author`: `{ name, url, avatar }` - `author`: `{ name, url, avatar }`
- `social`: `{ github?, twitter?, rss? }` - `social`: `{ github?, twitter? }`
- `feed`: supports `folo.challenge.feedId` and `userId` - `feed`: supports `folo.challenge.feedId` and `userId`
- `map`: map providers, e.g. `["maplibre"]` - `map`: map providers, e.g. `["maplibre"]`
- `mapStyle`: `builtin` or provider-specific style - `mapStyle`: `builtin` or provider-specific style

View File

@@ -41,6 +41,7 @@ export const MasonryHeaderMasonryItem = ({ style, className }: { style?: React.C
siteConfig.social && siteConfig.social.twitter siteConfig.social && siteConfig.social.twitter
? resolveSocialUrl(siteConfig.social.twitter, { baseUrl: 'https://twitter.com/', stripAt: true }) ? resolveSocialUrl(siteConfig.social.twitter, { baseUrl: 'https://twitter.com/', stripAt: true })
: undefined : undefined
const hasRss = true
return ( return (
<div <div
@@ -76,7 +77,7 @@ export const MasonryHeaderMasonryItem = ({ style, className }: { style?: React.C
<h2 className="mt-1 mb-1 text-2xl font-semibold text-gray-900 dark:text-white">{siteConfig.name}</h2> <h2 className="mt-1 mb-1 text-2xl font-semibold text-gray-900 dark:text-white">{siteConfig.name}</h2>
{/* Social media links */} {/* Social media links */}
{siteConfig.social && ( {(githubUrl || twitterUrl || hasRss) && (
<div className="mt-1 mb-3 flex items-center justify-center gap-3"> <div className="mt-1 mb-3 flex items-center justify-center gap-3">
{githubUrl && ( {githubUrl && (
<a <a
@@ -100,7 +101,7 @@ export const MasonryHeaderMasonryItem = ({ style, className }: { style?: React.C
<i className="i-mingcute-twitter-fill text-sm" /> <i className="i-mingcute-twitter-fill text-sm" />
</a> </a>
)} )}
{siteConfig.social.rss && ( {hasRss && (
<a <a
href="/feed.xml" href="/feed.xml"
target="_blank" target="_blank"

View File

@@ -1,13 +1,24 @@
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@afilmory/ui' import {
import { useAtom, useSetAtom } from 'jotai' DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@afilmory/ui'
import { useQuery } from '@tanstack/react-query'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useState } from 'react' import { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import { Drawer } from 'vaul' import { Drawer } from 'vaul'
import { gallerySettingAtom, isCommandPaletteOpenAtom } from '~/atoms/app' import { gallerySettingAtom, isCommandPaletteOpenAtom } from '~/atoms/app'
import { sessionUserAtom } from '~/atoms/session'
import { injectConfig } from '~/config'
import { useMobile } from '~/hooks/useMobile' import { useMobile } from '~/hooks/useMobile'
import { authApi } from '~/lib/api/auth'
import { UserAvatar } from '../../social/comments/UserAvatar'
import { ViewPanel } from '../panels/ViewPanel' import { ViewPanel } from '../panels/ViewPanel'
import { ActionIconButton } from './utils' import { ActionIconButton } from './utils'
@@ -17,6 +28,7 @@ export const PageHeaderRight = () => {
const [gallerySetting] = useAtom(gallerySettingAtom) const [gallerySetting] = useAtom(gallerySettingAtom)
const setCommandPaletteOpen = useSetAtom(isCommandPaletteOpenAtom) const setCommandPaletteOpen = useSetAtom(isCommandPaletteOpenAtom)
const navigate = useNavigate() const navigate = useNavigate()
const sessionUser = useAtomValue(sessionUserAtom)
// 计算视图设置是否有自定义配置 // 计算视图设置是否有自定义配置
const hasViewCustomization = gallerySetting.columns !== 'auto' || gallerySetting.sortOrder !== 'desc' const hasViewCustomization = gallerySetting.columns !== 'auto' || gallerySetting.sortOrder !== 'desc'
@@ -66,6 +78,13 @@ export const PageHeaderRight = () => {
</DesktopViewButton> </DesktopViewButton>
)} )}
</div> </div>
{/* Auth Section - Only show when useCloud is true */}
{injectConfig.useCloud && (
<div className="border-border flex items-center gap-1 rounded-full border-[0.5px]">
{sessionUser ? <UserMenuButton user={sessionUser} /> : <LoginButton />}
</div>
)}
</div> </div>
) )
} }
@@ -143,3 +162,138 @@ const MobileViewButton = ({
</> </>
) )
} }
// 登录按钮
const LoginButton = () => {
const { t } = useTranslation()
const [isOpen, setIsOpen] = useState(false)
const { data: socialProviders } = useQuery({
queryKey: ['socialProviders'],
queryFn: authApi.getSocialProviders,
enabled: isOpen,
})
const handleSignIn = async (provider: string) => {
try {
const { url } = await authApi.signInSocial(provider)
window.location.href = url
} catch (error) {
console.error('Sign in failed:', error)
}
}
return (
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenuTrigger asChild>
<button
type="button"
className="relative flex size-7 items-center justify-center rounded-full text-white/60 transition-all duration-200 hover:bg-white/10 hover:text-white lg:size-8"
title={t('action.login')}
>
<i className="i-lucide-log-in text-sm lg:text-base" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[200px]">
<div className="px-2 py-1.5 text-xs text-white/50">{t('comments.chooseProvider')}</div>
<DropdownMenuSeparator />
{socialProviders?.providers.map((provider) => (
<DropdownMenuItem
key={provider.id}
onClick={() => handleSignIn(provider.id)}
icon={<LoginPlatformIcon provider={provider.id} />}
>
{t('comments.signInWith', { provider: provider.name })}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}
// 登录平台图标
const LoginPlatformIcon = ({ provider }: { provider: string }) => {
switch (provider) {
case 'github': {
return <i className="i-simple-icons-github text-base" />
}
case 'google': {
return (
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 262">
<path
fill="#4285F4"
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
/>
<path
fill="#34A853"
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
/>
<path
fill="#FBBC05"
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
/>
<path
fill="#EB4335"
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
/>
</svg>
)
}
default: {
return <i className="i-lucide-user text-base" />
}
}
}
// 用户菜单按钮
const UserMenuButton = ({
user,
}: {
user: { id: string; name?: string | null; image?: string | null; role?: string | null }
}) => {
const { t } = useTranslation()
const isAdmin = user.role === 'admin' || user.role === 'superadmin'
// 如果是 admin点击头像直接导航到 dashboard
if (isAdmin) {
return (
<button
type="button"
className="relative flex size-7 items-center justify-center rounded text-white/60 transition-all duration-200 hover:bg-white/10 hover:text-white lg:size-8"
title={t('action.dashboard')}
onClick={() => (window.location.href = '/platform')}
>
<UserAvatar image={user.image} name={user.name || user.id} fallback="?" size={28} className="lg:size-8" />
</button>
)
}
// 非 admin 用户显示菜单
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="relative flex size-7 items-center justify-center rounded text-white/60 transition-all duration-200 hover:bg-white/10 hover:text-white lg:size-8"
title={user.name || user.id}
>
<UserAvatar image={user.image} name={user.name || user.id} fallback="?" size={28} className="lg:size-8" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[200px]">
<div className="px-2 py-1.5">
<div className="text-sm font-medium text-white/90">{user.name || user.id}</div>
</div>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
// TODO: Implement sign out
window.location.reload()
}}
icon={<i className="i-lucide-log-out text-base" />}
>
{t('action.signOut')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -149,11 +149,6 @@ const enUiSchema = {
title: 'GitHub', title: 'GitHub',
helper: 'Supports full URLs or usernames.', helper: 'Supports full URLs or usernames.',
}, },
rss: {
title: 'Expose RSS feed',
description: 'Enable to publish an RSS endpoint on the public site.',
helper: 'Visitors can subscribe to the latest photos via RSS.',
},
}, },
}, },
}, },

View File

@@ -148,11 +148,6 @@ const zhCnUiSchema = {
title: 'GitHub', title: 'GitHub',
helper: '支持完整链接或用户名。', helper: '支持完整链接或用户名。',
}, },
rss: {
title: '生成 RSS 订阅源',
description: '启用后将在前台站点暴露 RSS 订阅入口。',
helper: '开启后访客可通过 RSS 订阅最新照片。',
},
}, },
}, },
}, },

View File

@@ -134,25 +134,6 @@ export const DEFAULT_SETTING_DEFINITIONS = {
isSensitive: false, isSensitive: false,
schema: z.string().trim(), schema: z.string().trim(),
}, },
'site.social.rss': {
isSensitive: false,
schema: z
.string()
.trim()
.transform((value) => value.toLowerCase())
.superRefine((value, ctx) => {
if (value.length === 0) {
return
}
if (value !== 'true' && value !== 'false') {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'RSS toggle must be either "true" or "false"',
})
}
}),
},
'site.feed.folo.challenge.feedId': { 'site.feed.folo.challenge.feedId': {
isSensitive: false, isSensitive: false,
schema: z.string().trim(), schema: z.string().trim(),

View File

@@ -1,7 +1,7 @@
import { authUsers } from '@afilmory/db' import { authUsers } from '@afilmory/db'
import { DbAccessor } from 'core/database/database.provider' import { DbAccessor } from 'core/database/database.provider'
import { BizException, ErrorCode } from 'core/errors' import { BizException, ErrorCode } from 'core/errors'
import { normalizeStringToUndefined, parseBoolean } from 'core/helpers/normalize.helper' import { normalizeStringToUndefined } from 'core/helpers/normalize.helper'
import { requireTenantContext } from 'core/modules/platform/tenant/tenant.context' import { requireTenantContext } from 'core/modules/platform/tenant/tenant.context'
import { asc, eq, sql } from 'drizzle-orm' import { asc, eq, sql } from 'drizzle-orm'
import { injectable } from 'tsyringe' import { injectable } from 'tsyringe'
@@ -309,7 +309,6 @@ interface SiteConfigAuthor {
interface SiteConfigSocial { interface SiteConfigSocial {
twitter?: string twitter?: string
github?: string github?: string
rss?: boolean
} }
interface SiteConfigFeed { interface SiteConfigFeed {
@@ -408,11 +407,6 @@ function buildSocialConfig(values: SiteSettingValueMap): SiteConfig['social'] |
social.github = value social.github = value
}) })
const rss = parseBoolean(values['site.social.rss'])
if (typeof rss === 'boolean') {
social.rss = rss
}
return Object.keys(social).length > 0 ? social : undefined return Object.keys(social).length > 0 ? social : undefined
} }

View File

@@ -10,7 +10,6 @@ export const SITE_SETTING_KEYS = [
'site.accentColor', 'site.accentColor',
'site.social.twitter', 'site.social.twitter',
'site.social.github', 'site.social.github',
'site.social.rss',
'site.feed.folo.challenge.feedId', 'site.feed.folo.challenge.feedId',
'site.feed.folo.challenge.userId', 'site.feed.folo.challenge.userId',
'site.map.providers', 'site.map.providers',

View File

@@ -128,18 +128,6 @@ export function createSiteSettingUiSchema(t: UiSchemaTFunction): UiSchema<SiteSe
}, },
icon: 'github', icon: 'github',
}, },
{
type: 'field',
id: 'site-social-rss',
title: t('site.sections.social.groups.channels.fields.rss.title'),
description: t('site.sections.social.groups.channels.fields.rss.description'),
helperText: t('site.sections.social.groups.channels.fields.rss.helper'),
key: 'site.social.rss',
component: {
type: 'switch',
},
icon: 'rss',
},
], ],
}, },
], ],

File diff suppressed because one or more lines are too long

View File

@@ -11,8 +11,7 @@
}, },
"social": { "social": {
"github": "", "github": "",
"twitter": "", "twitter": ""
"rss": false
}, },
"feed": { "feed": {
"folo": { "folo": {
@@ -27,4 +26,4 @@
], ],
"mapStyle": "builtin", "mapStyle": "builtin",
"mapProjection": "mercator" "mapProjection": "mercator"
} }

View File

@@ -4,7 +4,7 @@
- `name`, `title`, `description`, `url`, `accentColor` - `name`, `title`, `description`, `url`, `accentColor`
- `author`: `{ name, url, avatar }` - `author`: `{ name, url, avatar }`
- `social`: `{ github?, twitter?, rss? }` - `social`: `{ github?, twitter? }`
- `feed`: optional `folo.challenge.feedId` and `userId` - `feed`: optional `folo.challenge.feedId` and `userId`
- `map`: e.g. `["maplibre"]` - `map`: e.g. `["maplibre"]`
- `mapStyle`: `builtin` or provider style - `mapStyle`: `builtin` or provider style

View File

@@ -6,12 +6,14 @@
"action.camera.not-found": "No cameras match your search", "action.camera.not-found": "No cameras match your search",
"action.camera.search": "Search Cameras", "action.camera.search": "Search Cameras",
"action.columns.setting": "Column Settings", "action.columns.setting": "Column Settings",
"action.dashboard": "Dashboard",
"action.filter.title": "Filter Photos", "action.filter.title": "Filter Photos",
"action.lens.clear": "Clear", "action.lens.clear": "Clear",
"action.lens.empty": "No lenses available", "action.lens.empty": "No lenses available",
"action.lens.filter": "Lens Filter", "action.lens.filter": "Lens Filter",
"action.lens.not-found": "No lenses match your search", "action.lens.not-found": "No lenses match your search",
"action.lens.search": "Search Lenses", "action.lens.search": "Search Lenses",
"action.login": "Sign In",
"action.map.explore": "Map Explore", "action.map.explore": "Map Explore",
"action.rating.filter": "Rating Filter", "action.rating.filter": "Rating Filter",
"action.rating.filter-above": "Show {{rating}} stars and above", "action.rating.filter-above": "Show {{rating}} stars and above",
@@ -27,6 +29,7 @@
"action.search.results": "Found {{count}} photos", "action.search.results": "Found {{count}} photos",
"action.search.title": "Search Photos", "action.search.title": "Search Photos",
"action.search.unified.title": "Search & Filter", "action.search.unified.title": "Search & Filter",
"action.signOut": "Sign Out",
"action.sort.mode": "Sort Mode", "action.sort.mode": "Sort Mode",
"action.sort.newest.first": "Newest First", "action.sort.newest.first": "Newest First",
"action.sort.oldest.first": "Oldest First", "action.sort.oldest.first": "Oldest First",

View File

@@ -6,12 +6,14 @@
"action.camera.not-found": "没有相机匹配您的搜索", "action.camera.not-found": "没有相机匹配您的搜索",
"action.camera.search": "搜索相机", "action.camera.search": "搜索相机",
"action.columns.setting": "列设置", "action.columns.setting": "列设置",
"action.dashboard": "仪表盘",
"action.filter.title": "筛选照片", "action.filter.title": "筛选照片",
"action.lens.clear": "清除", "action.lens.clear": "清除",
"action.lens.empty": "暂无镜头", "action.lens.empty": "暂无镜头",
"action.lens.filter": "镜头筛选", "action.lens.filter": "镜头筛选",
"action.lens.not-found": "没有镜头匹配您的搜索", "action.lens.not-found": "没有镜头匹配您的搜索",
"action.lens.search": "搜索镜头", "action.lens.search": "搜索镜头",
"action.login": "登录",
"action.map.explore": "地图探索", "action.map.explore": "地图探索",
"action.rating.filter": "评分筛选", "action.rating.filter": "评分筛选",
"action.rating.filter-above": "显示 {{rating}} 星及以上", "action.rating.filter-above": "显示 {{rating}} 星及以上",
@@ -27,6 +29,7 @@
"action.search.results": "找到 {{count}} 张照片", "action.search.results": "找到 {{count}} 张照片",
"action.search.title": "搜索照片", "action.search.title": "搜索照片",
"action.search.unified.title": "搜索和筛选", "action.search.unified.title": "搜索和筛选",
"action.signOut": "登出",
"action.sort.mode": "排序模式", "action.sort.mode": "排序模式",
"action.sort.newest.first": "最新优先", "action.sort.newest.first": "最新优先",
"action.sort.oldest.first": "最早优先", "action.sort.oldest.first": "最早优先",

View File

@@ -39,7 +39,6 @@ interface Author {
interface Social { interface Social {
twitter?: string twitter?: string
github?: string github?: string
rss?: boolean
} }
const defaultConfig: SiteConfig = { const defaultConfig: SiteConfig = {