feats: add feedback comment
This commit is contained in:
9
app/(marketing)/feedback/loading.tsx
Normal file
9
app/(marketing)/feedback/loading.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
export default function DashboardLoading() {
|
||||
return (
|
||||
<>
|
||||
<Skeleton className="h-full w-full rounded-lg" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
11
app/(marketing)/feedback/page.tsx
Normal file
11
app/(marketing)/feedback/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { constructMetadata } from "@/lib/utils";
|
||||
import Comment from "@/components/shared/comment";
|
||||
|
||||
export const metadata = constructMetadata({
|
||||
title: "Feedback",
|
||||
description: "Help us do better",
|
||||
});
|
||||
|
||||
export default async function Page() {
|
||||
return <Comment />;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { DashboardHeader } from "@/components/dashboard/header";
|
||||
|
||||
export default function DashboardLoading() {
|
||||
return (
|
||||
|
||||
115
components/shared/comment.tsx
Normal file
115
components/shared/comment.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLocale } from "next-intl";
|
||||
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
|
||||
export default function TwikooComment() {
|
||||
const locale = useLocale();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const cdnScript = document.createElement("script");
|
||||
cdnScript.src =
|
||||
"https://cdn.staticfile.org/twikoo/1.6.32/twikoo.all.min.js";
|
||||
cdnScript.async = true;
|
||||
|
||||
const loadSecondScript = () => {
|
||||
const initScript = document.createElement("script");
|
||||
initScript.innerHTML = `
|
||||
twikoo.init({
|
||||
envId: "https://comment.oiov.dev",
|
||||
el: '#twikoo-comment',
|
||||
pageSize: 10,
|
||||
includeReply: true,
|
||||
lang: "${locale === "zh" ? "zh-CN" : "en"}",
|
||||
path: location.pathname,
|
||||
onCommentLoaded: function() {
|
||||
// 评论加载完成后隐藏 loading
|
||||
window.dispatchEvent(new CustomEvent('twikoo-loaded'));
|
||||
}
|
||||
});
|
||||
`;
|
||||
initScript.id = "twikoo-init-id";
|
||||
document.body.appendChild(initScript);
|
||||
};
|
||||
|
||||
// 监听 twikoo 加载完成事件
|
||||
const handleTwikooLoaded = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
window.addEventListener("twikoo-loaded", handleTwikooLoaded);
|
||||
|
||||
cdnScript.addEventListener("load", loadSecondScript);
|
||||
document.body.appendChild(cdnScript);
|
||||
|
||||
// 设置超时,防止一直加载
|
||||
const timeout = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 5000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
window.removeEventListener("twikoo-loaded", handleTwikooLoaded);
|
||||
|
||||
if (loadSecondScript) {
|
||||
cdnScript.removeEventListener("load", loadSecondScript);
|
||||
}
|
||||
if (cdnScript && document.body.contains(cdnScript)) {
|
||||
document.body.removeChild(cdnScript);
|
||||
}
|
||||
const secondScript = document.querySelector("#twikoo-init-id");
|
||||
if (secondScript && document.body.contains(secondScript)) {
|
||||
document.body.removeChild(secondScript);
|
||||
}
|
||||
};
|
||||
}, [locale]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto my-8 max-w-2xl px-6">
|
||||
<div className="grids my-20 text-balance py-8 text-center font-satoshi text-[40px] font-black leading-[1.15] tracking-tight sm:text-5xl md:text-6xl md:leading-[1.15]">
|
||||
Feedback{" "}
|
||||
<span className="bg-gradient-to-r from-violet-600 via-blue-600 to-cyan-500 bg-clip-text text-[30px] text-transparent sm:text-3xl md:text-4xl">
|
||||
help us do better
|
||||
</span>
|
||||
</div>
|
||||
{isLoading && (
|
||||
<div className="space-y-4">
|
||||
{/* 评论输入框占位 */}
|
||||
<Skeleton className="h-32 w-full rounded-lg" />
|
||||
|
||||
{/* 评论列表占位 */}
|
||||
{[...Array(3)].map((_, index) => (
|
||||
<div key={index} className="space-y-3 border-b pb-4">
|
||||
{/* 用户头像和名称 */}
|
||||
<div className="flex items-center space-x-3">
|
||||
<Skeleton className="h-8 w-8 rounded-full" />
|
||||
<Skeleton className="h-4 w-24" />
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
{/* 评论内容 */}
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="h-4 w-3/4" />
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex space-x-4">
|
||||
<Skeleton className="h-3 w-8" />
|
||||
<Skeleton className="h-3 w-8" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
id="twikoo-comment"
|
||||
className={
|
||||
isLoading
|
||||
? "opacity-0"
|
||||
: "opacity-100 transition-opacity duration-300"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export const sidebarLinks: SidebarNavItem[] = [
|
||||
{ href: "/dashboard/settings", icon: "userSettings", title: "Settings" },
|
||||
{ href: "/docs", icon: "bookOpen", title: "Documentation" },
|
||||
{
|
||||
href: siteConfig.links.feedback,
|
||||
href: "/feedback",
|
||||
icon: "messageQuoted",
|
||||
title: "Feedback",
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export const marketingConfig: MarketingConfig = {
|
||||
},
|
||||
{
|
||||
title: "Feedback",
|
||||
href: siteConfig.links.feedback,
|
||||
href: "/feedback",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user