Skip to content

Commit b4f9f98

Browse files
committed
feat: post copyright
Signed-off-by: Innei <[email protected]>
1 parent 12a0600 commit b4f9f98

File tree

7 files changed

+133
-58
lines changed

7 files changed

+133
-58
lines changed

src/app/posts/(post-detail)/[category]/[slug]/page.tsx

+49-42
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { Balancer } from 'react-wrap-balancer'
55
import type { Image } from '@mx-space/api-client'
66
import type { PropsWithChildren } from 'react'
77

8+
import { ClientOnly } from '~/components/common/ClientOnly'
89
import { ReadIndicator } from '~/components/common/ReadIndicator'
910
import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks'
1011
import { Markdown } from '~/components/ui/markdown'
1112
import { PostActionAside } from '~/components/widgets/post/PostActionAside'
13+
import { PostCopyright } from '~/components/widgets/post/PostCopyright'
1214
import { PostMetaBar } from '~/components/widgets/post/PostMetaBar'
1315
import { SubscribeBell } from '~/components/widgets/subscribe/SubscribeBell'
1416
import { TocAside, TocAutoScroll } from '~/components/widgets/toc'
@@ -21,6 +23,53 @@ import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvide
2123

2224
import Loading from './loading'
2325

26+
const PostPage = () => {
27+
const id = useCurrentPostDataSelector((p) => p?.id)
28+
const title = useCurrentPostDataSelector((p) => p?.title)
29+
if (!id) {
30+
return <Loading />
31+
}
32+
33+
return (
34+
<div>
35+
<HeaderMetaInfoSetting />
36+
<article className="prose">
37+
<header className="mb-8">
38+
<h1 className="text-center">
39+
<Balancer>{title}</Balancer>
40+
</h1>
41+
42+
<PostMetaBarInternal className="mb-8 justify-center" />
43+
44+
<XLogSummaryForPost />
45+
</header>
46+
<WrappedElementProvider>
47+
<PostMarkdownImageRecordProvider>
48+
<PostMarkdown />
49+
</PostMarkdownImageRecordProvider>
50+
51+
<LayoutRightSidePortal>
52+
<TocAside
53+
className="sticky top-[120px] ml-4 mt-[120px]"
54+
treeClassName="max-h-[calc(100vh-6rem-4.5rem-300px)] h-[calc(100vh-6rem-4.5rem-300px)] min-h-[120px] relative"
55+
accessory={ReadIndicator}
56+
>
57+
<PostActionAside className="translate-y-full" />
58+
</TocAside>
59+
<TocAutoScroll />
60+
</LayoutRightSidePortal>
61+
</WrappedElementProvider>
62+
</article>
63+
64+
<ClientOnly>
65+
<PostCopyright />
66+
<SubscribeBell defaultType="post_c" />
67+
<XLogInfoForPost />
68+
</ClientOnly>
69+
</div>
70+
)
71+
}
72+
2473
const PostMarkdown = () => {
2574
const text = useCurrentPostDataSelector((data) => data?.text)
2675
if (!text) return null
@@ -74,47 +123,5 @@ const PostMetaBarInternal: Component = ({ className }) => {
74123
if (!meta) return null
75124
return <PostMetaBar meta={meta} className={className} />
76125
}
77-
const PostPage = () => {
78-
const id = useCurrentPostDataSelector((p) => p?.id)
79-
const title = useCurrentPostDataSelector((p) => p?.title)
80-
if (!id) {
81-
return <Loading />
82-
}
83126

84-
return (
85-
<div>
86-
<HeaderMetaInfoSetting />
87-
<article className="prose">
88-
<header className="mb-8">
89-
<h1 className="text-center">
90-
<Balancer>{title}</Balancer>
91-
</h1>
92-
93-
<PostMetaBarInternal className="mb-8 justify-center" />
94-
95-
<XLogSummaryForPost />
96-
</header>
97-
<WrappedElementProvider>
98-
<PostMarkdownImageRecordProvider>
99-
<PostMarkdown />
100-
</PostMarkdownImageRecordProvider>
101-
102-
<LayoutRightSidePortal>
103-
<TocAside
104-
className="sticky top-[120px] ml-4 mt-[120px]"
105-
treeClassName="max-h-[calc(100vh-6rem-4.5rem-300px)] h-[calc(100vh-6rem-4.5rem-300px)] min-h-[120px] relative"
106-
accessory={ReadIndicator}
107-
>
108-
<PostActionAside className="translate-y-full" />
109-
</TocAside>
110-
<TocAutoScroll />
111-
</LayoutRightSidePortal>
112-
</WrappedElementProvider>
113-
</article>
114-
115-
<SubscribeBell defaultType="post_c" />
116-
<XLogInfoForPost />
117-
</div>
118-
)
119-
}
120127
export default PostPage

src/atoms/url.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import { apiClient } from '~/utils/request'
55

66
export interface UrlConfig {
77
adminUrl: string
8-
backendUrl: string
98

10-
frontendUrl: string
9+
webUrl: string
1110
}
1211

1312
const appUrlAtom = atom<UrlConfig | null>(null)

src/components/ui/markdown/parsers/mention.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const prefixToUrlMap = {
2323
// {GH@Innei} {TW@Innei} {TG@Innei}
2424
export const MentionRule: MarkdownToJSX.Rule = {
2525
match: simpleInlineRegex(
26-
/^\{((?<prefix>(GH)|(TW)|(TG))@(?<name>\w+\b))\}\s?(?!\[.*?\])/,
26+
/^(\[(?<displayName>.*?)\])?\{((?<prefix>(GH)|(TW)|(TG))@(?<name>\w+\b))\}\s?(?!\[.*?\])/,
2727
),
2828
order: Priority.MIN,
2929
parse(capture) {
@@ -33,7 +33,7 @@ export const MentionRule: MarkdownToJSX.Rule = {
3333
return {}
3434
}
3535
return {
36-
content: { prefix: groups.prefix, name: groups.name },
36+
content: { ...groups },
3737
type: 'mention',
3838
}
3939
},
@@ -43,7 +43,7 @@ export const MentionRule: MarkdownToJSX.Rule = {
4343
return null as any
4444
}
4545

46-
const { prefix, name } = content
46+
const { prefix, name, displayName } = content
4747
if (!name) {
4848
return null as any
4949
}
@@ -63,8 +63,9 @@ export const MentionRule: MarkdownToJSX.Rule = {
6363
target="_blank"
6464
rel="noreferrer nofollow"
6565
href={`${urlPrefix}${name}`}
66+
className="no-underline"
6667
>
67-
{name}
68+
{displayName || name}
6869
</a>
6970
</span>
7071
)
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
.anchor {
2-
@apply relative inline cursor-alias overflow-hidden text-accent;
3-
}
1+
.link {
2+
color: currentColor;
3+
background-image: linear-gradient(theme(colors.accent), theme(colors.accent));
4+
background-size: 0% 1.5px;
5+
background-repeat: no-repeat;
6+
/* NOTE: this won't work with background images */
7+
text-shadow: 0.05em 0 theme(colors.base-100), -0.05em 0 theme(colors.base-100);
8+
transition: all 500ms ease;
49

5-
.anchor::after {
6-
@apply absolute -bottom-0.5 left-1/2 w-0 -translate-x-1/2 border-b-[1px] border-current text-center transition-[width] duration-500 ease-in-out;
10+
@apply border-0 no-underline;
711

8-
content: '';
9-
}
12+
background-position: left 1.2em;
13+
14+
&:hover {
15+
background-size: 100% 1.5px;
1016

11-
.anchor:hover::after {
12-
width: 100%;
17+
transition: all 250ms ease;
18+
}
1319
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const MLink: FC<{
7575
() => (
7676
<>
7777
<a
78-
className={clsx(styles['anchor'], 'is-link')}
78+
className={clsx(styles['link'])}
7979
href={props.href}
8080
target="_blank"
8181
onClick={handleRedirect}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import dayjs from 'dayjs'
2+
import type { FC } from 'react'
3+
4+
import { Divider } from '~/components/ui/divider'
5+
import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider'
6+
import { useAggregationSelector } from '~/providers/root/aggregation-data-provider'
7+
8+
export const PostCopyright: FC = () => {
9+
const name = useAggregationSelector((data) => data.user.name)
10+
11+
const webUrl = useAggregationSelector((data) => data.url.webUrl)
12+
const data = useCurrentPostDataSelector(
13+
(data) => {
14+
if (!webUrl) return null
15+
return {
16+
title: data?.title,
17+
link: new URL(location.pathname, webUrl).toString(),
18+
date: data?.modified,
19+
}
20+
},
21+
[webUrl],
22+
)
23+
if (!data) return null
24+
const { title, link, date } = data
25+
return (
26+
<section
27+
className="text-sm leading-loose text-gray-600 dark:text-neutral-400"
28+
id="copyright"
29+
>
30+
<p>文章标题:{title}</p>
31+
<p>文章作者:{name}</p>
32+
<p>
33+
文章链接:<span>{link}</span>{' '}
34+
<a
35+
onClick={() => {
36+
navigator.clipboard.writeText(link)
37+
}}
38+
data-hide-print
39+
className="select-none"
40+
>
41+
[复制]
42+
</a>
43+
</p>
44+
<p>
45+
最后修改时间:{' '}
46+
{date ? dayjs(date).format('YYYY 年 MM 月 DD 日 H:mm') : '暂没有修改过'}
47+
</p>
48+
<Divider />
49+
<div>
50+
<p>
51+
商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,未经站长允许不得对文章文字内容进行修改演绎。
52+
<br />
53+
本文采用
54+
<a href="https://creativecommons.org/licenses/by-nc-nd/4.0/">
55+
创作共用保留署名 - 非商业 - 禁止演绎 4.0 国际许可证
56+
</a>
57+
</p>
58+
</div>
59+
</section>
60+
)
61+
}

src/providers/root/aggregation-data-provider.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const AggregationProvider: FC<
3333
if (callOnceRef.current) return
3434
if (!aggregationData?.user) return
3535
callOnceRef.current = true
36+
3637
login().then((logged) => {
3738
if (logged) {
3839
// FIXME

0 commit comments

Comments
 (0)