Skip to content

Commit 1617ca8

Browse files
committed
feat: feature provider
Signed-off-by: Innei <[email protected]>
1 parent 4274a17 commit 1617ca8

File tree

7 files changed

+119
-75
lines changed

7 files changed

+119
-75
lines changed

src/app.static.config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ export const appStaticConfig = {
77
},
88
},
99
}
10+
11+
export const CDN_HOST = 'cdn.innei.ren'
12+
export const TENCENT_CDN_DOMAIN = CDN_HOST

src/app/(app)/feed/route.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import xss from 'xss'
1010
import type { AggregateRoot } from '@mx-space/api-client'
1111
import type { MarkdownToJSX } from 'markdown-to-jsx'
1212

13+
import { CDN_HOST } from '~/app.static.config'
1314
import { InsertRule } from '~/components/ui/markdown/parsers/ins'
1415
import { MarkRule } from '~/components/ui/markdown/parsers/mark'
1516
import { MentionRule } from '~/components/ui/markdown/parsers/mention'
@@ -94,6 +95,15 @@ ${ReactDOM.renderToString(
9495
Gallery: () => (
9596
<div style={{ textAlign: 'center' }}>这个内容只能在原文中查看哦~</div>
9697
),
98+
99+
img: ({ src, alt }) => {
100+
if (src) {
101+
if (new URL(src).hostname === CDN_HOST) {
102+
return <span>此图片不支持在 RSS Render 中查看。</span>
103+
}
104+
}
105+
return <img src={src} alt={alt} />
106+
},
97107
},
98108
additionalParserRules: {
99109
spoilder: SpoilerRule,

src/app/(app)/layout.tsx

+44-45
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Analytics } from '@vercel/analytics/react'
22
import { ToastContainer } from 'react-toastify'
33
import type { AggregateRoot } from '@mx-space/api-client'
44
import type { Viewport } from 'next'
5+
import type { PropsWithChildren } from 'react'
56

67
import { ClerkProvider } from '@clerk/nextjs'
78

@@ -16,6 +17,7 @@ import { attachUAAndRealIp } from '~/lib/attach-ua'
1617
import { sansFont, serifFont } from '~/lib/fonts'
1718
import { getQueryClient } from '~/lib/query-client.server'
1819
import { AggregationProvider } from '~/providers/root/aggregation-data-provider'
20+
import { AppFeatureProvider } from '~/providers/root/app-feature-provider'
1921
import { queries } from '~/queries/definition'
2022

2123
import { WebAppProviders } from '../../providers/root'
@@ -120,11 +122,7 @@ export const generateMetadata = async () => {
120122
}
121123
}
122124

123-
type Props = {
124-
children: React.ReactNode
125-
}
126-
127-
export default async function RootLayout(props: Props) {
125+
export default async function RootLayout(props: PropsWithChildren) {
128126
attachUAAndRealIp()
129127
const { children } = props
130128

@@ -139,48 +137,49 @@ export default async function RootLayout(props: Props) {
139137
aggregationData = data
140138

141139
return (
142-
// <ClerkProvider localization={ClerkZhCN}>
143140
<ClerkProvider>
144-
<html lang="zh-CN" className="noise" suppressHydrationWarning>
145-
<head>
146-
<SayHi />
147-
<HydrationEndDetector />
148-
<AccentColorStyleInjector />
149-
<link
150-
rel="shortcut icon"
151-
href={themeConfig.config.site.faviconDark}
152-
type="image/x-icon"
153-
media="(prefers-color-scheme: dark)"
154-
/>
155-
<link
156-
rel="shortcut icon"
157-
href={themeConfig.config.site.favicon}
158-
type="image/x-icon"
159-
media="(prefers-color-scheme: light)"
160-
/>
161-
</head>
162-
<body
163-
className={`${sansFont.variable} ${serifFont.variable} m-0 h-full p-0 font-sans`}
164-
>
165-
<WebAppProviders>
166-
<AggregationProvider
167-
aggregationData={data}
168-
appConfig={themeConfig.config}
141+
<AppFeatureProvider tmdb={!!process.env.TMDB_API_KEY}>
142+
<html lang="zh-CN" className="noise" suppressHydrationWarning>
143+
<head>
144+
<SayHi />
145+
<HydrationEndDetector />
146+
<AccentColorStyleInjector />
147+
<link
148+
rel="shortcut icon"
149+
href={themeConfig.config.site.faviconDark}
150+
type="image/x-icon"
151+
media="(prefers-color-scheme: dark)"
169152
/>
170-
171-
<div data-theme>
172-
<Root>{children}</Root>
173-
</div>
174-
175-
<TocAutoScroll />
176-
<SearchPanelWithHotKey />
177-
<Analyze />
178-
</WebAppProviders>
179-
<ToastContainer stacked />
180-
<ScrollTop />
181-
</body>
182-
</html>
183-
<Analytics />
153+
<link
154+
rel="shortcut icon"
155+
href={themeConfig.config.site.favicon}
156+
type="image/x-icon"
157+
media="(prefers-color-scheme: light)"
158+
/>
159+
</head>
160+
<body
161+
className={`${sansFont.variable} ${serifFont.variable} m-0 h-full p-0 font-sans`}
162+
>
163+
<WebAppProviders>
164+
<AggregationProvider
165+
aggregationData={data}
166+
appConfig={themeConfig.config}
167+
/>
168+
169+
<div data-theme>
170+
<Root>{children}</Root>
171+
</div>
172+
173+
<TocAutoScroll />
174+
<SearchPanelWithHotKey />
175+
<Analyze />
176+
</WebAppProviders>
177+
<ToastContainer stacked />
178+
<ScrollTop />
179+
</body>
180+
</html>
181+
<Analytics />
182+
</AppFeatureProvider>
184183
</ClerkProvider>
185184
)
186185
}

src/components/ui/link-card/LinkCard.tsx

+25-19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { fetchGitHubApi } from '~/lib/github'
1919
import { clsxm } from '~/lib/helper'
2020
import { getDominantColor } from '~/lib/image'
2121
import { apiClient } from '~/lib/request'
22+
import { useFeatureEnabled } from '~/providers/root/app-feature-provider'
2223

2324
import { LinkCardSource } from './enums'
2425
import styles from './LinkCard.module.css'
@@ -73,6 +74,30 @@ const LinkCardImpl: FC<LinkCardProps> = (props) => {
7374
[fullUrl],
7475
)
7576

77+
const tmdbEnabled = useFeatureEnabled('tmdb')
78+
const validTypeAndFetchFunction = useCallback(
79+
(source: LinkCardSource, id: string) => {
80+
const fetchDataFunctions = {
81+
[LinkCardSource.MixSpace]: fetchMxSpaceData,
82+
[LinkCardSource.GHRepo]: fetchGitHubRepoData,
83+
[LinkCardSource.GHCommit]: fetchGitHubCommitData,
84+
[LinkCardSource.GHPr]: fetchGitHubPRData,
85+
[LinkCardSource.Self]: fetchMxSpaceData,
86+
} as Record<LinkCardSource, FetchObject>
87+
if (tmdbEnabled)
88+
fetchDataFunctions[LinkCardSource.TMDB] = fetchTheMovieDBData
89+
90+
const fetchFunction = fetchDataFunctions[source]
91+
if (!fetchFunction) {
92+
return { isValid: false, fetchFn: null }
93+
}
94+
95+
const isValid = fetchFunction.isValid(id)
96+
return { isValid, fetchFn: isValid ? fetchFunction.fetch : null }
97+
},
98+
[tmdbEnabled],
99+
)
100+
76101
const { isValid, fetchFn } = useMemo(
77102
() => validTypeAndFetchFunction(source, id),
78103
[source, id],
@@ -207,25 +232,6 @@ type FetchObject = {
207232
fetch: FetchFunction
208233
}
209234

210-
function validTypeAndFetchFunction(source: LinkCardSource, id: string) {
211-
const fetchDataFunctions = {
212-
[LinkCardSource.MixSpace]: fetchMxSpaceData,
213-
[LinkCardSource.GHRepo]: fetchGitHubRepoData,
214-
[LinkCardSource.GHCommit]: fetchGitHubCommitData,
215-
[LinkCardSource.GHPr]: fetchGitHubPRData,
216-
[LinkCardSource.Self]: fetchMxSpaceData,
217-
[LinkCardSource.TMDB]: fetchTheMovieDBData,
218-
} as Record<LinkCardSource, FetchObject>
219-
220-
const fetchFunction = fetchDataFunctions[source]
221-
if (!fetchFunction) {
222-
return { isValid: false, fetchFn: null }
223-
}
224-
225-
const isValid = fetchFunction.isValid(id)
226-
return { isValid, fetchFn: isValid ? fetchFunction.fetch : null }
227-
}
228-
229235
const fetchGitHubRepoData: FetchObject = {
230236
isValid: (id) => {
231237
// owner/repo

src/components/ui/markdown/renderers/LinkRenderer.tsx

+12-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
parseGithubPrUrl,
2121
parseGithubTypedUrl,
2222
} from '~/lib/link-parser'
23+
import { useFeatureEnabled } from '~/providers/root/app-feature-provider'
2324

2425
import { EmbedGithubFile } from '../../../modules/shared/EmbedGithubFile'
2526
import { LinkCard, LinkCardSource } from '../../link-card'
@@ -53,6 +54,8 @@ export const BlockLinkRenderer = ({
5354
[children, href],
5455
)
5556

57+
const tmdbEnabled = useFeatureEnabled('tmdb')
58+
5659
if (!url) {
5760
return fallbackElement
5861
}
@@ -113,18 +116,17 @@ export const BlockLinkRenderer = ({
113116
)
114117
}
115118
case isTMDBUrl(url): {
116-
return (
117-
<LinkCard
118-
fallbackUrl={url.toString()}
119-
source={LinkCardSource.TMDB}
120-
id={url.pathname.slice(1)}
121-
/>
122-
)
119+
if (tmdbEnabled)
120+
return (
121+
<LinkCard
122+
fallbackUrl={url.toString()}
123+
source={LinkCardSource.TMDB}
124+
id={url.pathname.slice(1)}
125+
/>
126+
)
123127
}
124-
125-
default:
126-
return fallbackElement
127128
}
129+
return fallbackElement
128130
}
129131

130132
const FixedRatioContainer = ({

src/lib/image.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { TENCENT_CDN_DOMAIN } from '~/app.static.config'
2+
13
export const calculateDimensions = ({
24
width,
35
height,
@@ -40,7 +42,6 @@ export function getDominantColor(imageObject: HTMLImageElement) {
4042
.slice(1)}`
4143
}
4244

43-
const TENCENT_CDN_DOMAIN = 'cdn.innei.ren'
4445
export const addImageUrlResizeQuery = (url: string, size: number) => {
4546
const parsedUrl = new URL(url)
4647

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client'
2+
3+
import { createContext, useContextSelector } from 'use-context-selector'
4+
import type { FC, PropsWithChildren } from 'react'
5+
6+
const appFeatures = {
7+
tmdb: false,
8+
}
9+
const AppFeatureContext = createContext(appFeatures)
10+
export const AppFeatureProvider: FC<PropsWithChildren & typeof appFeatures> = ({
11+
children,
12+
...features
13+
}) => {
14+
return (
15+
<AppFeatureContext.Provider value={features}>
16+
{children}
17+
</AppFeatureContext.Provider>
18+
)
19+
}
20+
21+
export const useFeatureEnabled = (feature: keyof typeof appFeatures) => {
22+
return useContextSelector(AppFeatureContext, (ctx) => ctx[feature])
23+
}

0 commit comments

Comments
 (0)