upgrade the project
This commit is contained in:
@@ -1,33 +1,34 @@
|
||||
import { SocialIcons } from "@/components/utils/SocialIcons";
|
||||
import { Config } from "@/data/config";
|
||||
import { fontFangZhengXiaoBiaoSongCN, fontSourceSerifScreenCN } from "@/styles/font";
|
||||
|
||||
export const HomeCover = () => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="mb-20 mt-5 flex w-full justify-center rounded-xl"
|
||||
style={{
|
||||
aspectRatio: "4/1",
|
||||
background: `url(${Config.PageCovers.websiteCoverURL})`,
|
||||
backgroundSize: "cover",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className="relative top-1/2 my-auto h-24 w-24 rounded-full shadow-2xl md:h-32 md:w-32"
|
||||
alt={Config.Nickname}
|
||||
src={Config.AvatarURL}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<div
|
||||
className="mb-20 mt-5 flex w-full justify-center rounded-xl"
|
||||
style={{
|
||||
aspectRatio: "4/1",
|
||||
background: `url(${Config.PageCovers.websiteCoverURL})`,
|
||||
backgroundSize: "cover",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={Config.Nickname}
|
||||
className="relative top-1/2 my-auto h-24 w-24 rounded-full shadow-2xl md:h-32 md:w-32"
|
||||
src={Config.AvatarURL}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${fontFangZhengXiaoBiaoSongCN.className} my-8 text-center text-4xl font-bold`}>
|
||||
{Config.Nickname}
|
||||
</div>
|
||||
<SocialIcons />
|
||||
<div className={`font-fang-zheng-xiao-biao-song my-8 text-center text-4xl font-bold`}>{Config.Nickname}</div>
|
||||
{Config.Sentence && (
|
||||
<div className="my-8 flex justify-center">
|
||||
<p className={`${fontSourceSerifScreenCN.className} text-lg`}>{Config.Sentence}</p>
|
||||
<div className="my-5 flex justify-center">
|
||||
<p className={`font-source-serif-screen text-lg`}>{Config.Sentence}</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="my-8">
|
||||
<SocialIcons />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
13
components/mdx/Blockquote.tsx
Normal file
13
components/mdx/Blockquote.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
const Blockquote = (props: JSX.IntrinsicElements["blockquote"]) => {
|
||||
return (
|
||||
<blockquote
|
||||
className={
|
||||
"not-prose scroll-mt-20 my-5 px-5 py-4 bg-gray-100 dark:bg-gray-800 dark:border-gray-700 border-gray-300 border-l-4"
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</blockquote>
|
||||
);
|
||||
};
|
||||
|
||||
export default Blockquote;
|
||||
@@ -1,9 +1,9 @@
|
||||
import { fontFangZhengXiaoBiaoSongCN } from "@/styles/font";
|
||||
|
||||
export const H2 = (props: JSX.IntrinsicElements["h2"]) => {
|
||||
const H2 = (props: JSX.IntrinsicElements["h2"]) => {
|
||||
return (
|
||||
<h2 className={`${fontFangZhengXiaoBiaoSongCN.className} mt-4 mb-2 scroll-mt-20`} id={props.id}>
|
||||
<h2 className={`font-fang-zheng-xiao-biao-song scroll-mt-20`} id={props.id}>
|
||||
{props.children}
|
||||
</h2>
|
||||
);
|
||||
};
|
||||
|
||||
export default H2;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { FaCheck } from "react-icons/fa";
|
||||
import { IoCopyOutline } from "react-icons/io5";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
const PreWrapper = ({ children }: { children: JSX.Element }) => {
|
||||
const textInput = useRef(null);
|
||||
@@ -16,51 +19,23 @@ const PreWrapper = ({ children }: { children: JSX.Element }) => {
|
||||
setCopied(true);
|
||||
//@ts-ignore
|
||||
textInput.current && navigator.clipboard.writeText(textInput.current.textContent);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={textInput} onMouseEnter={onEnter} onMouseLeave={onExit} className="relative flat-scrollbar-normal">
|
||||
<div className="relative flat-scrollbar-normal" onMouseLeave={onExit} onMouseMove={onEnter} ref={textInput}>
|
||||
{hovered && (
|
||||
<button
|
||||
<Button
|
||||
aria-label="Copy code"
|
||||
className={`absolute right-2 top-2 h-8 w-8 rounded border-2 bg-gray-700 p-1 dark:bg-gray-800 ${
|
||||
copied ? "border-green-400 focus:border-green-400 focus:outline-none" : "border-gray-300"
|
||||
}`}
|
||||
className={`absolute right-2 top-2 h-8 w-8 rounded p-1 ${copied ? "hover:text-green-500 text-green-500" : ""}`}
|
||||
onClick={onCopy}
|
||||
variant={"outline"}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
className={copied ? "text-green-400" : "text-gray-300"}
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
{copied ? <FaCheck /> : <IoCopyOutline />}
|
||||
</Button>
|
||||
)}
|
||||
<pre className="flat-scrollbar-normal">{children}</pre>
|
||||
<pre className="p-2 dark:bg-[#0d1117] bg-[#F6F8FA] rounded-md flat-scrollbar-normal not-prose text-sm dark:selection:bg-gray-700 selection:bg-gray-300 selection:text-inherit">
|
||||
{children}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { H2 } from "./H2";
|
||||
import Blockquote from "./Blockquote";
|
||||
import H2 from "./H2";
|
||||
import ImageWrapper from "./ImageWrapper";
|
||||
import PreWrapper from "./PreWrapper";
|
||||
import TableWrapper from "./TableWrapper";
|
||||
@@ -8,4 +9,5 @@ export const MDXComponentsSet = {
|
||||
table: TableWrapper,
|
||||
img: ImageWrapper,
|
||||
h2: H2,
|
||||
blockquote: Blockquote,
|
||||
};
|
||||
|
||||
10
components/readerpage/BottomCard.tsx
Normal file
10
components/readerpage/BottomCard.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Config } from "@/data/config";
|
||||
|
||||
export const BottomCard = () => {
|
||||
return (
|
||||
<div className="p-8 w-full flex flex-col justify-center">
|
||||
<img alt={Config.AuthorName} className="h-24 w-24 rounded-full mx-auto" src={Config.AvatarURL} />
|
||||
<p className="mx-auto mt-5 font-source-serif-screen">{Config.Sentence}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,12 +11,12 @@ export const DrawerTOC = (props: { data: TTOCItem[] }) => {
|
||||
const setIsTOCOpen = useDrawerTOCState((state) => state.changeDrawerTOCOpen);
|
||||
const activeId = useActiveHeading(props.data.map((item) => `#${item.anchorId}`));
|
||||
return (
|
||||
<Sheet open={isTOCOpen} onOpenChange={setIsTOCOpen}>
|
||||
<Sheet onOpenChange={setIsTOCOpen} open={isTOCOpen}>
|
||||
<SheetTrigger
|
||||
title="Open the table of contents"
|
||||
className="bottom-16 right-5 fixed bg-white dark:bg-black border-gray-700 border dark:border-gray-500 shadow-xl"
|
||||
title="Open the table of contents"
|
||||
>
|
||||
<div title="Open the table of contents" onClick={() => setIsTOCOpen(!isTOCOpen)} className="p-1 font-bold">
|
||||
<div className="p-1 font-bold" onClick={() => setIsTOCOpen(!isTOCOpen)} title="Open the table of contents">
|
||||
<MdMenuBook className="text-3xl" />
|
||||
</div>
|
||||
</SheetTrigger>
|
||||
@@ -31,11 +31,11 @@ export const DrawerTOC = (props: { data: TTOCItem[] }) => {
|
||||
"border-t border-b py-1 px-2 border-dashed hover:bg-gray-100 hover:dark:bg-gray-900",
|
||||
activeId === `#${item.anchorId}` ? "bg-gray-100 dark:bg-gray-900 text-sky-700 dark:text-sky-500" : "",
|
||||
)}
|
||||
href={`#${item.anchorId}`}
|
||||
key={`drawer-toc-${item.anchorId}`}
|
||||
onClick={() => {
|
||||
setIsTOCOpen(false);
|
||||
}}
|
||||
key={`drawer-toc-${item.anchorId}`}
|
||||
href={`#${item.anchorId}`}
|
||||
>
|
||||
<li className={"p-2"} style={{ paddingLeft: `${item.level - 2}em` }}>{`${item.title}`}</li>
|
||||
</Link>
|
||||
|
||||
@@ -8,19 +8,19 @@ export const PostComments = (props: { postId: string }) => {
|
||||
Config.Giscus && (
|
||||
<div className="mt-10 mb-5">
|
||||
<Giscus
|
||||
id={props.postId}
|
||||
repo={Config.Giscus.repo as `${string}/${string}`}
|
||||
repoId={Config.Giscus.repoId}
|
||||
category={Config.Giscus.category}
|
||||
categoryId={Config.Giscus.categoryId}
|
||||
mapping="pathname"
|
||||
term={props.postId}
|
||||
reactionsEnabled="1"
|
||||
emitMetadata="0"
|
||||
theme={theme === "light" ? "light_tritanopia" : "dark_tritanopia"}
|
||||
id={props.postId}
|
||||
inputPosition="top"
|
||||
loading="eager"
|
||||
lang="en"
|
||||
loading="eager"
|
||||
mapping="pathname"
|
||||
reactionsEnabled="1"
|
||||
repo={Config.Giscus.repo as `${string}/${string}`}
|
||||
repoId={Config.Giscus.repoId}
|
||||
term={props.postId}
|
||||
theme={theme === "light" ? "light_tritanopia" : "dark_tritanopia"}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const PostCover = (props: { coverURL: string }) => {
|
||||
return (
|
||||
<div
|
||||
className="mb-8 mt-0 flex w-full justify-center rounded-xl"
|
||||
className="mb-8 mt-0 flex w-full justify-center rounded-md"
|
||||
style={{
|
||||
aspectRatio: "5/2",
|
||||
background: `url(${props.coverURL})`,
|
||||
|
||||
@@ -11,27 +11,26 @@ export const ShareButtons = (props: {
|
||||
title: string;
|
||||
quote?: string | null;
|
||||
}) => {
|
||||
const postURL = `https://${Config.SiteDomain}/blog/${props.postId}`;
|
||||
const postURL = encodeURI(`https://${Config.SiteDomain}/blog/${props.postId}`);
|
||||
const copyShareText = `${props.title} ${props.subtitle ? `- ${props.subtitle}` : ""} - ${
|
||||
Config.Nickname
|
||||
}'s Blog ${postURL}`;
|
||||
const { toast } = useToast();
|
||||
return (
|
||||
<div className="my-3 flex space-x-4 text-2xl">
|
||||
<div className="py-3 flex justify-center space-x-4 text-2xl">
|
||||
{props.allowShare != false ? (
|
||||
<>
|
||||
<div className="my-auto text-sm font-bold">{"SHARE :"}</div>
|
||||
<FacebookShareButton className="mx-2" url={postURL} quote={props.quote ?? props.title}>
|
||||
<FaFacebook title="Share to Facebook" className="hover:text-blue-500" />
|
||||
<FacebookShareButton className="mx-2" quote={props.quote ?? props.title} url={postURL}>
|
||||
<FaFacebook className="hover:text-blue-500" title="Share to Facebook" />
|
||||
</FacebookShareButton>
|
||||
<TwitterShareButton className="mx-2" url={postURL} title={props.title}>
|
||||
<FaTwitter title="Share to Twitter" className="hover:text-sky-500" />
|
||||
<TwitterShareButton className="mx-2" title={props.title} url={postURL}>
|
||||
<FaTwitter className="hover:text-sky-500" title="Share to Twitter" />
|
||||
</TwitterShareButton>
|
||||
<LinkedinShareButton className="mx-2" url={postURL} title={props.title}>
|
||||
<FaLinkedin title="Share to Linkedin" className="hover:text-blue-500" />
|
||||
<LinkedinShareButton className="mx-2" title={props.title} url={postURL}>
|
||||
<FaLinkedin className="hover:text-blue-500" title="Share to Linkedin" />
|
||||
</LinkedinShareButton>
|
||||
<RedditShareButton className="mx-2" url={postURL} title={props.title}>
|
||||
<FaReddit title="Share to Reddit" className="hover:text-orange-500" />
|
||||
<RedditShareButton className="mx-2" title={props.title} url={postURL}>
|
||||
<FaReddit className="hover:text-orange-500" title="Share to Reddit" />
|
||||
</RedditShareButton>
|
||||
<CopyToClipboard
|
||||
onCopy={() => {
|
||||
@@ -40,8 +39,8 @@ export const ShareButtons = (props: {
|
||||
text={copyShareText}
|
||||
>
|
||||
<FaLink
|
||||
title="Share with the post url and description"
|
||||
className="hover:text-gray-500 mx-2 cursor-pointer"
|
||||
title="Share with the post url and description"
|
||||
/>
|
||||
</CopyToClipboard>
|
||||
</>
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { useActiveHeading } from "@/hooks/useActiveHeading";
|
||||
import { TTOCItem } from "@/types/toc.type";
|
||||
import Link from "next/link";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
export const TOC = (props: { data: TTOCItem[] }) => {
|
||||
const activeId = useActiveHeading(props.data.map((item) => `#${item.anchorId}`));
|
||||
|
||||
return (
|
||||
<Card className="sticky top-[5em] mx-5">
|
||||
<CardHeader className="p-3">
|
||||
<CardTitle className="text-lg text-center">{"TABLE OF CONTENTS"}</CardTitle>
|
||||
</CardHeader>
|
||||
<Separator />
|
||||
<CardContent className="px-2 py-2 h-[60vh] overflow-y-auto flat-scrollbar-normal">
|
||||
<ul>
|
||||
<div className="mx-5">
|
||||
<div className="text-lg text-center p-2 font-bold border-t-2 border-b-2 border-gray-500">
|
||||
{"TABLE OF CONTENTS"}
|
||||
</div>
|
||||
<div className="px-2 py-2 h-[60vh] overflow-y-auto flat-scrollbar-normal">
|
||||
<div>
|
||||
{props.data?.map((item) => (
|
||||
<Link className={""} href={`#${item.anchorId}`} key={`toc-${item.anchorId}`}>
|
||||
<li
|
||||
<Link href={`#${item.anchorId}`} key={`toc-${item.anchorId}`}>
|
||||
<div
|
||||
className={twMerge(
|
||||
`py-2 text-sm rounded-lg hover:bg-gray-100 hover:dark:bg-gray-900`,
|
||||
activeId === `#${item.anchorId}` ? "bg-gray-100 dark:bg-gray-900 text-sky-700 dark:text-sky-500" : "",
|
||||
`py-2 text-sm rounded-lg hover:text-sky-700 dark:hover:text-sky-400`,
|
||||
activeId === `#${item.anchorId}` ? "text-sky-700 dark:text-sky-400" : "",
|
||||
)}
|
||||
style={{ paddingLeft: `${item.level - 1}em` }}
|
||||
>{`${item.title}`}</li>
|
||||
>{`${item.title}`}</div>
|
||||
</Link>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
167
components/sponsor-page/SponsorBoard.tsx
Normal file
167
components/sponsor-page/SponsorBoard.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Config } from "@/data/config";
|
||||
import { isEmptyString } from "@/lib/utils";
|
||||
import { DialogTrigger } from "@radix-ui/react-dialog";
|
||||
import { nanoid } from "nanoid";
|
||||
import Link from "next/link";
|
||||
import { QRCodeSVG } from "qrcode.react";
|
||||
import { useState } from "react";
|
||||
import CopyToClipboard from "react-copy-to-clipboard";
|
||||
import { FaCheck, FaCopy } from "react-icons/fa";
|
||||
import { FaGithub, FaPatreon, FaPaypal } from "react-icons/fa6";
|
||||
import { SiAlipay, SiWechat } from "react-icons/si";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export const SponsorBoard = () => {
|
||||
const [isCopiedList, setIsCopiedList] = useState(Config.Sponsor?.Crypto?.map(() => false) ?? []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
onOpenChange={() => {
|
||||
setIsCopiedList(Config.Sponsor?.Crypto?.map(() => false) ?? []);
|
||||
}}
|
||||
>
|
||||
<div className="mx-2 my-10 flex flex-col justify-around space-y-5">
|
||||
{Config.Sponsor?.Crypto && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<div className="mx-3 my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Crypto"}</h3>
|
||||
<div className="text-xs">{"Supports BTC, USDT and ETH."}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-auto">
|
||||
<DialogTrigger>
|
||||
<Button className="my-auto font-bold">{"DONATE"}</Button>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{Config.Sponsor?.Github && Config.SocialLinks.github && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<FaGithub className="mx-3 my-auto text-4xl text-gray-900 dark:text-gray-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Github Sponsor"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-auto">
|
||||
<Button asChild className="my-auto font-bold">
|
||||
<Link href={`https://github.com/sponsors/${Config.SocialLinks.github}`} target="_blank">
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isEmptyString(Config.Sponsor?.WechatPayQRCodeContent) && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<SiWechat className="mx-3 my-auto text-4xl text-green-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Wechat Pay"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-auto bg-white p-1">
|
||||
<QRCodeSVG height={100} value={Config.Sponsor?.WechatPayQRCodeContent!} width={100} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isEmptyString(Config.Sponsor?.AlipayLink) && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<SiAlipay className="mx-3 my-auto text-4xl text-blue-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Alipay"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-auto">
|
||||
<Button asChild className="my-auto font-bold">
|
||||
<Link href={Config.Sponsor?.AlipayLink!} target="_blank">
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isEmptyString(Config.Sponsor?.PaypalId) && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<FaPaypal className="mx-3 my-auto text-4xl text-blue-600" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Paypal"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-auto">
|
||||
<Button asChild className="my-auto font-bold">
|
||||
<Link href={`https://paypal.me/${Config.Sponsor?.PaypalId}`} target="_blank">
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isEmptyString(Config.Sponsor?.PatreonId) && (
|
||||
<div className="py-3 flex justify-between border-b">
|
||||
<div className="my-auto flex">
|
||||
<FaPatreon className="mx-3 my-auto text-4xl text-gray-500" />
|
||||
<div className="my-auto">
|
||||
<h3 className="mx-auto text-sm font-bold">{"Patreon"}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<Button asChild className="my-auto font-bold">
|
||||
<Link href={`https://patreon.com/${Config.Sponsor?.PatreonId}`} target="_blank">
|
||||
{"DONATE"}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex">{"CRYPTO"}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div>
|
||||
<div className="w-full text-sm my-2">
|
||||
<div>
|
||||
<b>NOTE: </b> Please confirm the corresponding block network and address before transferring money to
|
||||
avoid loss.
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
{Config.Sponsor?.Crypto?.map((cryptoItem, cryptoItemIndex) => (
|
||||
<div className="w-full py-3 border-b" key={nanoid()}>
|
||||
<div className="my-2 flex space-x-2">
|
||||
<div className="font-bold my-auto text-sm">{`${cryptoItem.Name} - ${cryptoItem.Blockchain}`}</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Input autoFocus={false} defaultValue={cryptoItem.Address} readOnly />
|
||||
<CopyToClipboard
|
||||
onCopy={() => {
|
||||
const newIsCopiedList = Config.Sponsor?.Crypto?.map(() => false) ?? [];
|
||||
newIsCopiedList[cryptoItemIndex] = true;
|
||||
setIsCopiedList(newIsCopiedList);
|
||||
}}
|
||||
text={cryptoItem.Address}
|
||||
>
|
||||
<Button
|
||||
className={`ml-3 my-auto ${isCopiedList[cryptoItemIndex] && "bg-green-500 hover:bg-green-500"}`}
|
||||
size="sm"
|
||||
type="submit"
|
||||
>
|
||||
<span className="sr-only">{"Copy"}</span>
|
||||
{isCopiedList[cryptoItemIndex] ? <FaCheck className="h-4 w-4" /> : <FaCopy className="h-4 w-4" />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
28
components/sponsor-page/SponsorDescription.tsx
Normal file
28
components/sponsor-page/SponsorDescription.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Config } from "@/data/config";
|
||||
|
||||
export const SponsorDescription = () => {
|
||||
return (
|
||||
<div className="h-full">
|
||||
{!Config.Sponsor ? (
|
||||
<p className={`font-source-serif-screen break-words text-lg`}>
|
||||
{"Thank you, for data and personal private security, every sponsor method was paused."}
|
||||
</p>
|
||||
) : (
|
||||
<p className={`font-source-serif-screen break-words text-lg`}>
|
||||
{
|
||||
"If you like my works, I would deeply appreciate your support as a patron. Your contribution not only fuels my creative journey but also allows me to delve deeper into my passion. Your support plays a vital role in making this vision a reality. Thank you for considering becoming a patron and being an integral part of this work endeavor."
|
||||
}
|
||||
<br />
|
||||
{
|
||||
"If you have donated me please note your name and contact information, or contact me with the transfer record, and I will add you to the sponsor list."
|
||||
}
|
||||
<br />
|
||||
{"Here are the ways you can become a patron. Thank you for your support!"}
|
||||
<br />
|
||||
<br />
|
||||
{`Yours, ${Config.AuthorName}`}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
54
components/ui/accordion.tsx
Normal file
54
components/ui/accordion.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item className={cn("border-b", className)} ref={ref} {...props} />
|
||||
));
|
||||
AccordionItem.displayName = "AccordionItem";
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
||||
@@ -3,39 +3,39 @@ import * as React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
|
||||
<div className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} ref={ref} {...props} />
|
||||
));
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
||||
<div className={cn("flex flex-col space-y-1.5 p-6", className)} ref={ref} {...props} />
|
||||
),
|
||||
);
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
<h3 className={cn("text-2xl font-semibold leading-none tracking-tight", className)} ref={ref} {...props} />
|
||||
),
|
||||
);
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
<p className={cn("text-sm text-muted-foreground", className)} ref={ref} {...props} />
|
||||
),
|
||||
);
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />,
|
||||
({ className, ...props }, ref) => <div className={cn("p-6 pt-0", className)} ref={ref} {...props} />,
|
||||
);
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
||||
<div className={cn("flex items-center p-6 pt-0", className)} ref={ref} {...props} />
|
||||
),
|
||||
);
|
||||
CardFooter.displayName = "CardFooter";
|
||||
|
||||
@@ -13,11 +13,11 @@ const Command = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -44,11 +44,11 @@ const CommandInput = React.forwardRef<
|
||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -61,8 +61,8 @@ const CommandList = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.List
|
||||
ref={ref}
|
||||
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -72,7 +72,7 @@ CommandList.displayName = CommandPrimitive.List.displayName;
|
||||
const CommandEmpty = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Empty>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
|
||||
>((props, ref) => <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />);
|
||||
>((props, ref) => <CommandPrimitive.Empty className="py-6 text-center text-sm" ref={ref} {...props} />);
|
||||
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
||||
|
||||
@@ -81,11 +81,11 @@ const CommandGroup = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Group
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -96,7 +96,7 @@ const CommandSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Separator ref={ref} className={cn("-mx-1 h-px bg-border", className)} {...props} />
|
||||
<CommandPrimitive.Separator className={cn("-mx-1 h-px bg-border", className)} ref={ref} {...props} />
|
||||
));
|
||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
||||
|
||||
@@ -105,11 +105,11 @@ const CommandItem = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -19,11 +19,11 @@ const DialogOverlay = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -36,11 +36,11 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -68,8 +68,8 @@ const DialogTitle = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -79,7 +79,7 @@ const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
<DialogPrimitive.Description className={cn("text-sm text-muted-foreground", className)} ref={ref} {...props} />
|
||||
));
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
type={type}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -10,10 +10,10 @@ const Separator = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -61,7 +61,7 @@ const SheetContent = React.forwardRef<React.ElementRef<typeof SheetPrimitive.Con
|
||||
({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
||||
<SheetPrimitive.Content className={cn(sheetVariants({ side }), className)} ref={ref} {...props}>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||
<X className="h-4 w-4" />
|
||||
@@ -87,7 +87,7 @@ const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title ref={ref} className={cn("text-lg font-semibold text-foreground", className)} {...props} />
|
||||
<SheetPrimitive.Title className={cn("text-lg font-semibold text-foreground", className)} ref={ref} {...props} />
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
@@ -95,7 +95,7 @@ const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
<SheetPrimitive.Description className={cn("text-sm text-muted-foreground", className)} ref={ref} {...props} />
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ const ToastViewport = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -41,7 +41,7 @@ const Toast = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
|
||||
>(({ className, variant, ...props }, ref) => {
|
||||
return <ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />;
|
||||
return <ToastPrimitives.Root className={cn(toastVariants({ variant }), className)} ref={ref} {...props} />;
|
||||
});
|
||||
Toast.displayName = ToastPrimitives.Root.displayName;
|
||||
|
||||
@@ -50,11 +50,11 @@ const ToastAction = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Action
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -65,11 +65,11 @@ const ToastClose = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Close
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
toast-close=""
|
||||
{...props}
|
||||
>
|
||||
@@ -82,7 +82,7 @@ const ToastTitle = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title ref={ref} className={cn("text-sm font-semibold", className)} {...props} />
|
||||
<ToastPrimitives.Title className={cn("text-sm font-semibold", className)} ref={ref} {...props} />
|
||||
));
|
||||
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
||||
|
||||
@@ -90,7 +90,7 @@ const ToastDescription = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Description ref={ref} className={cn("text-sm opacity-90", className)} {...props} />
|
||||
<ToastPrimitives.Description className={cn("text-sm opacity-90", className)} ref={ref} {...props} />
|
||||
));
|
||||
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ export const Footer = () => {
|
||||
text={RSSFeedURL}
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
className={`ml-3 my-auto ${isCopied && "bg-green-500 hover:bg-green-500"}`}
|
||||
size="sm"
|
||||
type="submit"
|
||||
>
|
||||
<span className="sr-only">{"Copy"}</span>
|
||||
{isCopied ? <FaCheck className="h-4 w-4" /> : <FaCopy className="h-4 w-4" />}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||
import { Config } from "@/data/config";
|
||||
import { fontFangZhengXiaoBiaoSongCN } from "@/styles/font";
|
||||
import { nanoid } from "nanoid";
|
||||
import { useTheme } from "next-themes";
|
||||
import Link from "next/link";
|
||||
@@ -32,40 +31,43 @@ export const NavBar = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={isSideNavOpen} onOpenChange={(open) => setIsSideNavOpen(open)}>
|
||||
<div className="sticky top-0 z-50 border-gray-200 dark:border-gray-700 border-b flex flex-wrap justify-between py-3 backdrop-blur bg-white/50 dark:bg-gray-950/50 px-5 md:px-10 lg:px-20 xl:px-32 2xl:px-52">
|
||||
<Link href="/" className="cursor-pointer my-auto text-2xl font-bold">
|
||||
<h1 className={`${fontFangZhengXiaoBiaoSongCN.className} my-auto`}>{Config.SiteTitle}</h1>
|
||||
<Sheet onOpenChange={(open) => setIsSideNavOpen(open)} open={isSideNavOpen}>
|
||||
<div className="sticky top-0 z-50 border-black-200 dark:border-gray-700 border-b bg-white dark:bg-gray-950 flex flex-wrap justify-between py-3 px-5 md:px-10 lg:px-20 xl:px-32 2xl:px-52">
|
||||
<Link className="cursor-pointer my-auto text-2xl font-bold" href="/">
|
||||
<h1 className={`font-fang-zheng-xiao-biao-song my-auto`} title="Click to jump to home page.">
|
||||
{Config.SiteTitle}
|
||||
</h1>
|
||||
</Link>
|
||||
<div className="my-auto hidden sm:flex">
|
||||
{MenuItems.map((menuItem) => (
|
||||
<Link
|
||||
className="font-bold hover:text-sky-700 dark:hover:text-sky-500 mx-2 my-auto px-2"
|
||||
href={menuItem.href}
|
||||
key={nanoid()}
|
||||
className="border-b-sky-600 font-bold hover:text-sky-600 dark:hover:border-b-sky-500 dark:hover:text-sky-500 mx-2 my-auto px-2"
|
||||
onClick={() => setIsSideNavOpen(false)}
|
||||
>
|
||||
{menuItem.title}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href={"/search"}
|
||||
className="cursor-pointer mx-2 rounded-full p-1 text-3xl text-black hover:bg-gray-200 dark:text-gray-50 dark:hover:bg-gray-800"
|
||||
href={"/search"}
|
||||
title="Search posts by keywords"
|
||||
>
|
||||
<MdSearch />
|
||||
</Link>
|
||||
<div
|
||||
title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
|
||||
className="cursor-pointer mx-1 rounded-full p-1 text-3xl text-black hover:bg-gray-200 dark:text-gray-50 dark:hover:bg-gray-800"
|
||||
onClick={handleSwitchTheme}
|
||||
title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
|
||||
>
|
||||
{theme === "light" ? <MdOutlineDarkMode /> : <MdOutlineLightMode />}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-3xl sm:hidden my-auto">
|
||||
<SheetTrigger
|
||||
title="Spread the navigation menu"
|
||||
className="text-black rounded-full p-1 hover:bg-gray-200 dark:text-gray-50 dark:hover:bg-gray-800"
|
||||
title="Spread the navigation menu"
|
||||
>
|
||||
<MdMenu
|
||||
onClick={() => {
|
||||
@@ -78,19 +80,19 @@ export const NavBar = () => {
|
||||
<SheetContent className="bg:white border-none py-16 shadow-md dark:bg-black flex flex-col text-end">
|
||||
{MenuItems.map((menuItem) => (
|
||||
<Link
|
||||
className="border-b border-dashed p-3 text-xl hover:text-sky-500"
|
||||
href={menuItem.href}
|
||||
key={nanoid()}
|
||||
className="border-b border-dashed p-3 text-xl hover:text-sky-500"
|
||||
onClick={() => setIsSideNavOpen(false)}
|
||||
>
|
||||
{menuItem.title}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
title="Search the posts"
|
||||
className="border-b border-dashed p-3 text-xl hover:text-sky-500"
|
||||
href="/search"
|
||||
onClick={() => setIsSideNavOpen(false)}
|
||||
title="Search the posts"
|
||||
>
|
||||
{"SEARCH"}
|
||||
</Link>
|
||||
@@ -99,8 +101,8 @@ export const NavBar = () => {
|
||||
onClick={handleSwitchTheme}
|
||||
>
|
||||
<div
|
||||
title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
|
||||
className="cursor-pointer mx-1 my-auto rounded-full text-2xl"
|
||||
title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
|
||||
>
|
||||
{theme === "light" ? <MdOutlineDarkMode /> : <MdOutlineLightMode />}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { normalizeDate } from "@/lib/date";
|
||||
import { fontSourceSerifScreenCN } from "@/styles/font";
|
||||
import { TPostListItem } from "@/types/post-list";
|
||||
import { nanoid } from "nanoid";
|
||||
import Link from "next/link";
|
||||
@@ -13,35 +12,43 @@ export const PostList = (props: { data: TPostListItem[] }) => {
|
||||
<div
|
||||
className={`flex flex-col justify-center ${
|
||||
index !== props.data.length - 1 && "border-b"
|
||||
} border-gray-200 hover:bg-gray-50 dark:hover:bg-gray-950 dark:border-gray-800 p-3`}
|
||||
} border-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 dark:border-gray-800 px-3 py-1`}
|
||||
>
|
||||
<div className={`${fontSourceSerifScreenCN.className} text-center flex-col py-2`}>
|
||||
<h3 className="mx-auto text-lg font-extrabold capitalize">{postItem.frontMatter.title}</h3>
|
||||
{postItem.frontMatter.subtitle && (
|
||||
<div className="mx-auto text-base font-semibold capitalize text-gray-700 dark:text-gray-300">
|
||||
{postItem.frontMatter.subtitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-center text-sm italic">{normalizeDate(postItem.frontMatter.time)}</div>
|
||||
{postItem.frontMatter.tags && (
|
||||
<div className="my-2 flex justify-center">
|
||||
{postItem.frontMatter.tags.map((tagName) => (
|
||||
<Badge
|
||||
variant={"secondary"}
|
||||
className="mx-1 text-gray-600 dark:text-gray-300"
|
||||
key={`tags-${nanoid()}`}
|
||||
>
|
||||
{tagName}
|
||||
</Badge>
|
||||
))}
|
||||
<div className={"font-source-serif-screen flex-col py-3"}>
|
||||
<div className="flex justify-center">
|
||||
<h3 className="mx-auto text-lg font-extrabold capitalize">{postItem.frontMatter.title}</h3>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-center">
|
||||
{postItem.frontMatter.subtitle && (
|
||||
<div className="mx-auto text-sm font-bold capitalize text-gray-700 dark:text-gray-300">
|
||||
{postItem.frontMatter.subtitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{postItem.frontMatter.summary && (
|
||||
<div className={`${fontSourceSerifScreenCN.className} flex my-1 justify-center`}>
|
||||
<div className={"font-source-serif-screen flex justify-center"}>
|
||||
<p>{postItem.frontMatter.summary}</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-wrap justify-between my-2">
|
||||
<div className="text-center flex flex-col justify-center italic text-sm my-auto mr-2 h-6">
|
||||
<div className="my-auto">{normalizeDate(postItem.frontMatter.time)}</div>
|
||||
</div>
|
||||
{postItem.frontMatter.tags && (
|
||||
<div className="flex flex-wrap my-auto">
|
||||
{postItem.frontMatter.tags.map((tagName) => (
|
||||
<Badge
|
||||
className="mr-1 my-1 text-gray-600 dark:text-gray-300"
|
||||
key={`tags-${nanoid()}`}
|
||||
variant={"secondary"}
|
||||
>
|
||||
{tagName}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -6,9 +6,8 @@ export const SEO = (props: { title: string; description?: string | null; coverUR
|
||||
return (
|
||||
<>
|
||||
<title>{props.title}</title>
|
||||
<link rel="alternate" type="application/rss+xml" href={RSSFeedURL} />
|
||||
<link href={RSSFeedURL} rel="alternate" type="application/rss+xml" />
|
||||
<NextSeo
|
||||
title={props.title}
|
||||
description={props.description ?? Config.Sentence}
|
||||
openGraph={{
|
||||
title: props.title,
|
||||
@@ -31,6 +30,7 @@ export const SEO = (props: { title: string; description?: string | null; coverUR
|
||||
},
|
||||
],
|
||||
}}
|
||||
title={props.title}
|
||||
twitter={{
|
||||
handle: `@${Config.SocialLinks.twitter}`,
|
||||
site: WebsiteURL,
|
||||
|
||||
@@ -7,36 +7,36 @@ export const SocialIcons = () => {
|
||||
return (
|
||||
<div className="my-5 flex justify-center space-x-4 text-2xl font-bold">
|
||||
{Config.SocialLinks.twitter && (
|
||||
<Link target="_blank" href={`https://x.com/${Config.SocialLinks.twitter}`} title="Twitter">
|
||||
<Link href={`https://x.com/${Config.SocialLinks.twitter}`} target="_blank" title="Twitter">
|
||||
<FiTwitter className="hover:text-sky-500" />
|
||||
</Link>
|
||||
)}
|
||||
{Config.SocialLinks.mastodon && (
|
||||
<Link target="_blank" href={Config.SocialLinks.mastodon} title="Mastodon">
|
||||
<Link href={Config.SocialLinks.mastodon} target="_blank" title="Mastodon">
|
||||
<TbBrandMastodon className="hover:text-purple-500" />
|
||||
</Link>
|
||||
)}
|
||||
{Config.SocialLinks.instagram && (
|
||||
<Link target="_blank" href={`https://instagram.com/${Config.SocialLinks.instagram}`} title="Instagram">
|
||||
<Link href={`https://instagram.com/${Config.SocialLinks.instagram}`} target="_blank" title="Instagram">
|
||||
<FiInstagram className="hover:text-orange-500" />
|
||||
</Link>
|
||||
)}
|
||||
{Config.SocialLinks.facebook && (
|
||||
<Link target="_blank" href={`https://instagram.com/${Config.SocialLinks.facebook}`} title="Instagram">
|
||||
<Link href={`https://instagram.com/${Config.SocialLinks.facebook}`} target="_blank" title="Instagram">
|
||||
<TbBrandFacebook className="hover:text-blue-500" />
|
||||
</Link>
|
||||
)}
|
||||
{Config.SocialLinks.linkedin && (
|
||||
<Link target="_blank" href={`https://linkedin.com/in/${Config.SocialLinks.linkedin}`} title="Instagram">
|
||||
<Link href={`https://linkedin.com/in/${Config.SocialLinks.linkedin}`} target="_blank" title="Instagram">
|
||||
<TbBrandLinkedin className="hover:text-blue-500" />
|
||||
</Link>
|
||||
)}
|
||||
{Config.SocialLinks.github && (
|
||||
<Link target="_blank" href={`https://github.com/${Config.SocialLinks.github}`} title="Github">
|
||||
<Link href={`https://github.com/${Config.SocialLinks.github}`} target="_blank" title="Github">
|
||||
<FiGithub className="hover:text-gray-500" />
|
||||
</Link>
|
||||
)}
|
||||
<Link target="_blank" href={`mailto:${Config.SocialLinks.email}`} title="EMail Address">
|
||||
<Link href={`mailto:${Config.SocialLinks.email}`} target="_blank" title="EMail Address">
|
||||
<FiMail className="hover:text-gray-500" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user