Skip to content

Commit f413e23

Browse files
committed
feat: note meta icon
Signed-off-by: Innei <[email protected]>
1 parent bc9f8ba commit f413e23

File tree

14 files changed

+280
-37
lines changed

14 files changed

+280
-37
lines changed

src/app/init.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import dayjs from "dayjs"
2+
import 'dayjs/locale/zh-cn'
3+
4+
export const init = () => {
5+
dayjs.locale('zh-cn')
6+
}

src/app/layout.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import { dehydrate } from '@tanstack/react-query'
55
import { ClerkProvider } from '@clerk/nextjs'
66

77
import { Root } from '~/components/layout/root/Root'
8-
import { ClerkZhCN } from '~/i18n/cherk-cn'
98
import { defineMetadata } from '~/lib/define-metadata'
109
import { sansFont, serifFont } from '~/lib/fonts'
1110
import { getQueryClient } from '~/utils/query-client.server'
1211

1312
import { Providers } from '../providers/root'
1413
import { Hydrate } from './hydrate'
14+
import { init } from './init'
15+
16+
init()
1517

1618
export const generateMetadata = defineMetadata(async (_, getData) => {
1719
const { seo, url, user } = await getData()
@@ -77,7 +79,8 @@ export default async function RootLayout(props: Props) {
7779
})
7880

7981
return (
80-
<ClerkProvider localization={ClerkZhCN}>
82+
// <ClerkProvider localization={ClerkZhCN}>
83+
<ClerkProvider>
8184
<html lang="zh-CN" className="noise" suppressHydrationWarning>
8285
<body
8386
className={`${sansFont.variable} ${serifFont.variable} m-0 h-full p-0 font-sans antialiased`}

src/app/notes/[id]/layout.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import RemoveMarkdown from 'remove-markdown'
21
import type { Metadata } from 'next'
32

43
import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView'
54
import { attachUA } from '~/lib/attach-ua'
5+
import { getSummaryFromMd } from '~/lib/markdown'
66
import { queries } from '~/queries/definition'
77
import { getQueryClient } from '~/utils/query-client.server'
88

@@ -20,8 +20,9 @@ export const generateMetadata = async ({
2020
const { data } = await getQueryClient().fetchQuery(
2121
queries.note.byNid(params.id),
2222
)
23-
const { title, images } = data
24-
const description = RemoveMarkdown(data.text).slice(0, 100)
23+
const { title, images, text } = data
24+
const description = getSummaryFromMd(text ?? '')
25+
2526
const ogImage = images?.length
2627
? {
2728
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

src/app/notes/[id]/page.tsx

+78-18
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@ import { useParams } from 'next/navigation'
88
import type { Image } from '@mx-space/api-client'
99
import type { MarkdownToJSX } from '~/components/ui/markdown'
1010

11+
import { ClientOnly } from '~/components/common/ClientOnly'
1112
import { PageDataHolder } from '~/components/common/PageHolder'
13+
import { MdiClockOutline } from '~/components/icons/clock'
1214
import { useSetHeaderMetaInfo } from '~/components/layout/header/internal/hooks'
15+
import { DividerVertical } from '~/components/ui/divider'
16+
import { FloatPopover } from '~/components/ui/float-popover'
1317
import { Loading } from '~/components/ui/loading'
1418
import { Markdown } from '~/components/ui/markdown'
1519
import { Toc, TocAutoScroll } from '~/components/widgets/toc'
1620
import { useBeforeMounted } from '~/hooks/common/use-before-mounted'
17-
import { useNoteByNidQuery } from '~/hooks/data/use-note'
21+
import { useNoteByNidQuery, useNoteData } from '~/hooks/data/use-note'
22+
import { mood2icon, weather2icon } from '~/lib/meta-icon'
1823
import { ArticleElementProvider } from '~/providers/article/article-element-provider'
1924
import { MarkdownImageRecordProvider } from '~/providers/article/markdown-image-record-provider'
2025
import { useSetCurrentNoteId } from '~/providers/note/current-note-id-provider'
2126
import { NoteLayoutRightSidePortal } from '~/providers/note/right-side-provider'
27+
import { parseDate } from '~/utils/datetime'
2228

2329
import styles from './page.module.css'
2430

@@ -28,6 +34,10 @@ const PageImpl = () => {
2834
const { id } = useParams() as { id: string }
2935
const { data } = useNoteByNidQuery(id)
3036

37+
// Why do this, I mean why do set NoteId to context, don't use `useParams().id` for children components.
38+
// Because any router params or query changes, will cause components that use `useParams()` hook, this hook is a context hook,
39+
// For example, `ComA` use `useParams()` just want to get value `id`,
40+
// but if router params or query changes `page` params, will cause `CompA` re - render.
3141
const setNoteId = useSetCurrentNoteId()
3242
useBeforeMounted(() => {
3343
setNoteId(id)
@@ -48,30 +58,26 @@ const PageImpl = () => {
4858
return <Loading useDefaultLoadingText />
4959
}
5060

51-
// const mardownResult = parseMarkdown(note.text ?? '')
52-
53-
// Why do this, I mean why do set NoteId to context, don't use `useParams().id` for children components.
54-
// Because any router params or query changes, will cause components that use `useParams()` hook, this hook is a context hook,
55-
// For example, `ComA` use `useParams()` just want to get value `id`,
56-
// but if router params or query changes `page` params, will cause `CompA` re - render.
57-
58-
const dateFormat = dayjs(data?.data.created)
59-
.locale('cn')
60-
.format('YYYY 年 M 月 D 日 dddd')
61+
const tips = `创建于 ${parseDate(note.created, 'YYYY 年 M 月 D 日 dddd')}${
62+
note.modified
63+
? `,修改于 ${parseDate(note.modified, 'YYYY 年 M 月 D 日 dddd')}`
64+
: ''
65+
}`
6166

6267
return (
6368
<article
6469
className={clsx('prose', styles['with-indent'], styles['with-serif'])}
6570
>
6671
<header>
67-
<h1 className="mt-8 text-left font-bold text-base-content/95">
68-
<Balancer>{note.title}</Balancer>
69-
</h1>
70-
72+
<NoteTitle />
7173
<span className="inline-flex items-center text-[13px] text-neutral-content/60">
72-
<time className="font-medium" suppressHydrationWarning>
73-
{dateFormat}
74-
</time>
74+
<FloatPopover TriggerComponent={NoteDateMeta}>{tips}</FloatPopover>
75+
76+
<DividerVertical className="!mx-2 scale-y-50" />
77+
78+
<ClientOnly>
79+
<NoteMetaBar />
80+
</ClientOnly>
7581
</span>
7682
</header>
7783

@@ -89,6 +95,59 @@ const PageImpl = () => {
8995
)
9096
}
9197

98+
const NoteTitle = () => {
99+
const note = useNoteData()
100+
if (!note) return null
101+
const title = note.title
102+
return (
103+
<h1 className="mt-8 text-left font-bold text-base-content/95">
104+
<Balancer>{title}</Balancer>
105+
</h1>
106+
)
107+
}
108+
109+
const NoteMetaBar = () => {
110+
const note = useNoteData()
111+
if (!note) return null
112+
113+
return (
114+
<>
115+
{note.weather && (
116+
<span className="inline-flex items-center space-x-1">
117+
{weather2icon(note.weather)}
118+
<span className="font-medium">{note.weather}</span>
119+
<DividerVertical className="!mx-2 scale-y-50" />
120+
</span>
121+
)}
122+
123+
{note.mood && (
124+
<span className="inline-flex items-center space-x-1">
125+
{mood2icon(note.mood)}
126+
<span className="font-medium">{note.mood}</span>
127+
{/* <DividerVertical className="!mx-2 scale-y-50" /> */}
128+
</span>
129+
)}
130+
</>
131+
)
132+
}
133+
const NoteDateMeta = () => {
134+
const note = useNoteData()
135+
if (!note) return null
136+
137+
const dateFormat = dayjs(note.created)
138+
.locale('zh-cn')
139+
.format('YYYY 年 M 月 D 日 dddd')
140+
141+
return (
142+
<span className="inline-flex items-center space-x-1">
143+
<MdiClockOutline />
144+
<time className="font-medium" suppressHydrationWarning>
145+
{dateFormat}
146+
</time>
147+
</span>
148+
)
149+
}
150+
92151
const Markdownrenderers: { [name: string]: Partial<MarkdownToJSX.Rule> } = {
93152
text: {
94153
react(node, _, state) {
@@ -100,6 +159,7 @@ const Markdownrenderers: { [name: string]: Partial<MarkdownToJSX.Rule> } = {
100159
},
101160
},
102161
}
162+
103163
export default PageDataHolder(PageImpl, () => {
104164
const { id } = useParams() as { id: string }
105165
return useNoteByNidQuery(id)

src/components/common/ClientOnly.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { useIsClient } from '~/hooks/common/use-is-client'
2+
3+
export const ClientOnly: Component = (props) => {
4+
const isClient = useIsClient()
5+
if (!isClient) return null
6+
return <>{props.children}</>
7+
}

src/components/layout/header/internal/BluredBackground.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
'use client'
22

3+
import clsx from 'clsx'
34

45
import { useHeaderBgOpacity } from './hooks'
5-
import clsx from 'clsx'
66

77
export const BluredBackground = () => {
88
const headerOpacity = useHeaderBgOpacity()
99
return (
1010
<div
1111
className={clsx(
12-
'absolute inset-0 transform-gpu [backdrop-filter:saturate(180%)_blur(20px)] [backface-visibility:hidden]',
12+
'absolute inset-0 transform-gpu [-webkit-backdrop-filter:saturate(180%)_blur(20px)] [backdrop-filter:saturate(180%)_blur(20px)] [backface-visibility:hidden]',
1313
'bg-themed-bg_opacity [border-bottom:1px_solid_rgb(187_187_187_/_20%)]',
1414
)}
1515
style={{

src/components/ui/divider/Divider.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import React from 'react'
2-
import { clsx } from 'clsx'
32
import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react'
43

4+
import { clsxm } from '~/utils/helper'
5+
56
export const Divider: FC<
67
DetailedHTMLProps<HTMLAttributes<HTMLHRElement>, HTMLHRElement>
78
> = (props) => {
89
const { className, ...rest } = props
910
return (
1011
<hr
11-
className={clsx(
12+
className={clsxm(
1213
'my-4 h-[0.5px] border-0 bg-always-black !bg-opacity-30 dark:bg-always-white',
1314
className,
1415
)}
@@ -23,8 +24,8 @@ export const DividerVertical: FC<
2324
const { className, ...rest } = props
2425
return (
2526
<span
26-
className={clsx(
27-
'mx-4 inline-block h-full w-[0.5px] bg-always-black !bg-opacity-30 text-transparent dark:bg-always-white',
27+
className={clsxm(
28+
'mx-4 inline-block h-full w-[0.5px] select-none bg-always-black !bg-opacity-30 text-transparent dark:bg-always-white',
2829
className,
2930
)}
3031
{...rest}

src/components/ui/float-popover/FloatPopover.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { flip, offset, shift, useFloating } from '@floating-ui/react-dom'
44
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5-
import clsx from 'clsx'
65
import type { UseFloatingOptions } from '@floating-ui/react-dom'
76
import type { FC, PropsWithChildren } from 'react'
87

@@ -192,7 +191,7 @@ export const FloatPopover: FC<
192191
<As
193192
// @ts-ignore
194193
role={trigger === 'both' || trigger === 'click' ? 'button' : 'note'}
195-
className={clsx('inline-block', wrapperClassNames)}
194+
className={clsxm('inline-block', wrapperClassNames)}
196195
ref={refs.setReference}
197196
{...listener}
198197
>

0 commit comments

Comments
 (0)