From 4a1ac0f669c9c1effcace0986cdf36f41cf0959a Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 30 Jun 2023 17:57:16 +0800 Subject: [PATCH] chore: change thing and form init Signed-off-by: Innei --- README.md | 6 +- next.config.mjs | 4 +- package.json | 2 +- packages/markdown/remark/index.ts | 2 +- src/app/error.tsx | 14 +- src/app/friends/layout.tsx | 11 + src/app/friends/page.tsx | 341 ++++++++++++++++++ src/app/notes/[id]/page.module.css | 5 + src/app/notes/error.tsx | 1 + src/components/common/NotSupport.tsx | 6 +- src/components/layout/container/Normal.tsx | 2 +- src/components/layout/footer/Footer.tsx | 2 +- src/components/layout/footer/config.ts | 2 +- src/components/ui/avatar/Avatar.tsx | 47 ++- src/components/ui/button/StyledButton.tsx | 4 +- src/components/ui/form/Form.tsx | 67 ++++ src/components/ui/form/FormContext.tsx | 15 + src/components/ui/form/FormInput.tsx | 45 +++ src/components/ui/form/index.ts | 4 + src/components/ui/form/types.ts | 11 + .../CommentBox/CommentBoxLegacyForm.tsx | 6 +- src/lib/_.ts | 11 + src/middleware.ts | 17 +- src/providers/root/modal-stack-provider.tsx | 24 +- 24 files changed, 607 insertions(+), 42 deletions(-) create mode 100644 src/app/friends/layout.tsx create mode 100644 src/app/friends/page.tsx create mode 100644 src/components/ui/form/Form.tsx create mode 100644 src/components/ui/form/FormContext.tsx create mode 100644 src/components/ui/form/FormInput.tsx create mode 100644 src/components/ui/form/index.ts create mode 100644 src/components/ui/form/types.ts diff --git a/README.md b/README.md index 634ca7d1dc..83c8747030 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Springtide +# Shiro -A clean and modern front-end for [Mix Space](https://github.com/mx-space) +A minimalist personal website embodying the purity of paper and freshness of snow. + +A theme for [Mix Space](https://github.com/mx-space) ## Status diff --git a/next.config.mjs b/next.config.mjs index 2a16a5b215..c4d8f61cc5 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -46,7 +46,7 @@ let nextConfig = { sentryWebpackPlugin({ org: 'inneis-site', - project: 'springtide', + project: 'Shiro', authToken: process.env.SENTRY_AUTH_TOKEN, }), ) @@ -68,7 +68,7 @@ if (env.SENTRY === 'true' && isProd) { silent: true, org: 'inneis-site', - project: 'springtide', + project: 'Shiro', }, { // For all available options, see: diff --git a/package.json b/package.json index 4ef8f0b487..7cb74b87c3 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "Springtide", + "name": "Shiro", "license": "GPL-3.0", "private": false, "version": "0.0.0", diff --git a/packages/markdown/remark/index.ts b/packages/markdown/remark/index.ts index 42bbf44785..fca4784e34 100644 --- a/packages/markdown/remark/index.ts +++ b/packages/markdown/remark/index.ts @@ -35,7 +35,7 @@ export const parseMarkdown = (markdownText: string): ParserResult => { .use(rehypeAutolinkHeadings, { properties: { className: [ - 'springtide-anchor opacity-0 hover:opacity-100 transition-opacity duration-200 ease-in-out text-sm relative -bottom-2', + 'Shiro-anchor opacity-0 hover:opacity-100 transition-opacity duration-200 ease-in-out text-sm relative -bottom-2', ], ariaHidden: true, tabIndex: -1, diff --git a/src/app/error.tsx b/src/app/error.tsx index b892a511fb..f8187cefce 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -5,8 +5,11 @@ import { useEffect } from 'react' import { captureException } from '@sentry/nextjs' import { NotFound404 } from '~/components/common/404' +import { NormalContainer } from '~/components/layout/container/Normal' +import { StyledButton } from '~/components/ui/button' import { isRequestError, pickStatusCode } from '~/lib/is-error' +// eslint-disable-next-line react/display-name export default ({ error, reset }: any) => { useEffect(() => { captureException(error) @@ -21,8 +24,13 @@ export default ({ error, reset }: any) => { } return ( -
-

Something went wrong!

-
+ +
+

Something went wrong!

+ + Try again + +
+
) } diff --git a/src/app/friends/layout.tsx b/src/app/friends/layout.tsx new file mode 100644 index 0000000000..daaa54d78d --- /dev/null +++ b/src/app/friends/layout.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next' +import type { PropsWithChildren } from 'react' + +import { NormalContainer } from '~/components/layout/container/Normal' + +export const metadata: Metadata = { + title: '朋友们', +} +export default async function (props: PropsWithChildren) { + return {props.children} +} diff --git a/src/app/friends/page.tsx b/src/app/friends/page.tsx new file mode 100644 index 0000000000..c6fed23e14 --- /dev/null +++ b/src/app/friends/page.tsx @@ -0,0 +1,341 @@ +'use client' + +import { useQuery } from '@tanstack/react-query' +import { memo, useCallback, useRef, useState } from 'react' +import { AnimatePresence, m } from 'framer-motion' +import Markdown from 'markdown-to-jsx' +import type { LinkModel } from '@mx-space/api-client' +import type { FC } from 'react' + +import { LinkState, LinkType } from '@mx-space/api-client' + +import { NotSupport } from '~/components/common/NotSupport' +import { Avatar } from '~/components/ui/avatar' +import { StyledButton } from '~/components/ui/button' +import { Form, FormInput } from '~/components/ui/form' +import { Loading } from '~/components/ui/loading' +import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView' +import { shuffle } from '~/lib/_' +import { useAggregationSelector } from '~/providers/root/aggregation-data-provider' +import { useModalStack } from '~/providers/root/modal-stack-provider' +import { apiClient } from '~/utils/request' + +const renderTitle = (text: string) => { + return

{text}

+} + +export default function Page() { + const { data, isLoading } = useQuery({ + queryKey: ['friends'], + queryFn: async () => { + const { data } = await apiClient.link.getAll() + return data + }, + staleTime: Infinity, + cacheTime: Infinity, + select(data) { + const friends: LinkModel[] = [] + const collections: LinkModel[] = [] + const outdated: LinkModel[] = [] + const banned: LinkModel[] = [] + + for (const link of data) { + if (link.hide) { + continue + } + + switch (link.state) { + case LinkState.Banned: + banned.push(link) + continue + case LinkState.Outdate: + outdated.push(link) + continue + } + + switch (link.type) { + case LinkType.Friend: { + friends.push(link) + break + } + case LinkType.Collection: { + collections.push(link) + } + } + } + + return { friends: shuffle(friends), collections, outdated, banned } + }, + }) + + if (isLoading) return + if (!data) return null + const { banned, collections, friends, outdated } = data + return ( +
+
+

朋友们

+

海内存知己,天涯若比邻

+
+ +
+ {friends.length > 0 && ( + <> + {collections.length !== 0 && renderTitle('我的朋友')} + + + )} + {collections.length > 0 && ( + <> + {friends.length !== 0 && renderTitle('我的收藏')} + + + )} + + {outdated.length > 0 && ( + <> + {friends.length !== 0 && renderTitle('以下站点无法访问,已失联')} + + + )} + {banned.length > 0 && ( + <> + {friends.length !== 0 && renderTitle('以下站点不合规,已被禁止')} + + + )} +
+ + +
+ ) +} +type FriendSectionProps = { + data: LinkModel[] +} + +const FriendSection: FC = ({ data }) => { + return ( +
+ {data.map((link) => { + return ( + + + + ) + })} +
+ ) +} + +const LayoutBg = memo(() => { + return ( + + ) +}) +LayoutBg.displayName = 'LayoutBg' + +const Card: FC<{ link: LinkModel }> = ({ link }) => { + const [enter, setEnter] = useState(false) + + return ( + setEnter(true)} + onMouseLeave={() => setEnter(false)} + > + {enter && } + + + + {link.name} + + {link.description} + + + + ) +} + +const FavoriteSection: FC = ({ data }) => { + return ( +
    + {data.map((link) => { + return ( +
  • + + {link.name} + + {link.description || ''} +
  • + ) + })} +
+ ) +} + +const OutdateSection: FC = ({ data }) => { + return ( +
    + {data.map((link) => { + return ( +
  • + {link.name} + {link.description || ''} +
  • + ) + })} +
+ ) +} + +const BannedSection: FC = ({ data }) => { + return ( +
    + {data.map((link) => { + return ( +
  • + {link.name} +
  • + ) + })} +
+ ) +} + +const ApplyLinkInfo: FC = () => { + const { + seo, + user: { avatar, name }, + } = useAggregationSelector((a) => ({ + seo: a.seo!, + user: a.user!, + }))! + + const { data: canApply } = useQuery( + ['can-apply'], + () => apiClient.link.canApplyLink(), + { + initialData: true, + }, + ) + const { present } = useModalStack() + if (!canApply) { + return + } + return ( + <> +
+ + {[ + `**在申请友链之前请先将本站加入贵站的友链中**`, + `**填写邮箱后,待通过申请后会发送邮件**`, + `**我希望贵站不是商业化门户网站,亦或是植有影响观看体验广告的网站。**`, + `**失联站点将会定期移除,非法网站会立即禁止并拉黑。**`, + `
`, + `### 本站信息`, + ].join('\n\n') + + [ + '', + `**站点标题**: [${ + seo.title + }](${`${location.protocol}//${location.host}`})`, + `**站点描述**: ${seo.description}`, + `**主人头像**: [点击下载](${avatar})`, + `**主人名字**: ${name}`, + ].join('\n\n')} +
+
+ + { + present({ + title: '我想和你交朋友!', + + content: () => , + }) + }} + > + 和我做朋友吧! + + + ) +} + +const FormModal = () => { + const { dismissTop } = useModalStack() + const inputs = useRef([ + { name: 'author', placeholder: '昵称 *', required: true }, + { name: 'name', placeholder: '站点标题 *', required: true }, + { name: 'url', placeholder: '网站 * https://', required: true }, + { name: 'avatar', placeholder: '头像链接 * https://', required: true }, + { name: 'email', placeholder: '留下你的邮箱哦 *', required: true }, + { + name: 'description', + placeholder: '一句话描述一下自己吧 *', + maxLength: 50, + required: true, + }, + ]).current + const [state, setState] = useState({ + author: '', + name: '', + url: '', + avatar: '', + description: '', + email: '', + }) + + const setValue = useCallback((key: keyof typeof state, value: string) => { + setState((prevState) => ({ ...prevState, [key]: value })) + }, []) + + const handleChange = useCallback((e: React.ChangeEvent) => { + setValue(e.target.name as keyof typeof state, e.target.value) + }, []) + + const handleSubmit = useCallback((e: any) => { + e.preventDefault() + + apiClient.link.applyLink({ ...state }).then(() => { + dismissTop() + }) + }, []) + return ( +
+ {inputs.map((input) => ( + + ))} + + + 好耶! + + + ) +} diff --git a/src/app/notes/[id]/page.module.css b/src/app/notes/[id]/page.module.css index 5ae81d409d..365d6529db 100644 --- a/src/app/notes/[id]/page.module.css +++ b/src/app/notes/[id]/page.module.css @@ -81,5 +81,10 @@ font-size: 16px; } + + strong, + b { + @apply font-sans; + } } } diff --git a/src/app/notes/error.tsx b/src/app/notes/error.tsx index 8e64526781..4991b78d2f 100644 --- a/src/app/notes/error.tsx +++ b/src/app/notes/error.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/display-name */ 'use client' import { useEffect } from 'react' diff --git a/src/components/common/NotSupport.tsx b/src/components/common/NotSupport.tsx index b922d0ba46..b2b5a987f2 100644 --- a/src/components/common/NotSupport.tsx +++ b/src/components/common/NotSupport.tsx @@ -1,7 +1,9 @@ -export const NotSupport = () => { +export const NotSupport: Component<{ + text?: string +}> = ({ text }) => { return (
- 您当前所在地区暂不支持此功能 + {text || '您当前所在地区暂不支持此功能'}
) } diff --git a/src/components/layout/container/Normal.tsx b/src/components/layout/container/Normal.tsx index 9f2e21c397..a413ad3147 100644 --- a/src/components/layout/container/Normal.tsx +++ b/src/components/layout/container/Normal.tsx @@ -8,7 +8,7 @@ export const NormalContainer: Component = (props) => { return (
diff --git a/src/components/layout/footer/Footer.tsx b/src/components/layout/footer/Footer.tsx index 0cca316dd4..fb060bae11 100644 --- a/src/components/layout/footer/Footer.tsx +++ b/src/components/layout/footer/Footer.tsx @@ -4,7 +4,7 @@ import { FooterInfo } from './FooterInfo' export const Footer = () => { return ( -