diff --git a/app/app.css b/app/app.css index adb54cd..f3aa470 100644 --- a/app/app.css +++ b/app/app.css @@ -8,6 +8,7 @@ --animate-fade-out: fade-out 0.3s both; --animate-fade-in-left: fade-in-left 0.3s backwards; --animate-fade-off-right: fade-off-right 0.3s forwards; + --animate-scale: scale 0.3s 1s; @keyframes spinner-bar { 0% { @@ -57,6 +58,21 @@ transform: translateX(1.5rem); } } + + @keyframes scale { + 0% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.6; + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } + } } @media screen and (max-width: 639px) { diff --git a/app/components/layout/header.tsx b/app/components/layout/header.tsx index 0dab02f..a88554d 100644 --- a/app/components/layout/header.tsx +++ b/app/components/layout/header.tsx @@ -2,6 +2,8 @@ import { NavLink } from "react-router"; import { Feed, GitHub, QQ } from "../ui/icons"; import { clsn } from "~/utils"; +const { APP_APIURL } = import.meta.env; + const navItems = [ { name: "首页", to: "/" }, { name: "日记", to: "/note" }, @@ -34,13 +36,13 @@ function Header() { ))} diff --git a/app/components/shared/comment-form/index.tsx b/app/components/shared/comment-form/index.tsx new file mode 100644 index 0000000..48349ce --- /dev/null +++ b/app/components/shared/comment-form/index.tsx @@ -0,0 +1,178 @@ +import { useState, type ChangeEvent, type SubmitEvent } from "react"; +import { RiLoader5Fill } from "@remixicon/react"; + +import { add } from "~/components/ui/notice/utils"; +import { useIsLogin } from "~/composables/use-is-login"; +import { clsn } from "~/utils"; +import { getAuthorInfo, setAuthorInfo } from "~/utils/comment"; + +export interface CommentFormSubmitValues { + name: string; + email: string; + site: string; + content: string; + root?: string; + parent?: string; +} + +interface CommentFormProps { + className?: string; + parent?: string; + root?: string; + onSubmit: (values: CommentFormSubmitValues) => Promise; +} + +export default function CommentForm({ className, parent, root, onSubmit }: CommentFormProps) { + const isLogin = useIsLogin(); + const [loading, setLoading] = useState(false); + const [input, setInput] = useState(() => ({ + ...getAuthorInfo(), + content: "", + })); + + const onChange = (ev: ChangeEvent) => { + const { name, value } = ev.target; + setInput((prev) => ({ ...prev, [name]: value })); + }; + + const onFormSubmit = (ev: SubmitEvent) => { + ev.preventDefault(); + + if (!isLogin && (!input.name || !input.email || !input.content)) { + add({ + title: "评论失败", + type: "error", + content: "缺少必填项", + }); + + return; + } + + if (!input.content) { + add({ + title: "评论失败", + type: "error", + content: "缺少必填项", + }); + + return; + } + + setAuthorInfo({ + name: input.name, + email: input.email, + site: input.site, + }); + + setLoading(true); + + onSubmit({ + ...input, + root, + parent, + }).then(() => { + setInput((prev) => ({ ...prev, content: "" })); + setLoading(false); + }).catch(() => { + setLoading(false); + }); + }; + + const inputClass = "w-full px-3 py-2.5 rounded-xl bg-white border-4 border-gray-200 focus:border-pink-400 outline-none transition-colors placeholder:text-neutral-400"; + const labelClass = "block text-sm font-medium mb-2 text-neutral-500"; + + if (isLogin) { + return ( +
+
+

欢迎回来!

+
+
+