feat: 帖子收藏与用户关注

- 新增收藏功能:favorite 服务与 handler、FavoriteButton 组件
- 新增关注功能:follow 服务与 handler、FollowButton 组件
- 用户主页适配关注/粉丝与收藏展示
- flash 消息工具、广告与侧边栏组件微调
This commit is contained in:
2026-09-26 03:02:40 +08:00
parent 31f5360c26
commit d7c6b3a961
40 changed files with 1528 additions and 268 deletions

View File

@@ -1,12 +1,15 @@
"use client";
import { useEffect, useRef, useState, type CSSProperties } from "react";
import { AD_TIP_DELAY_MS, adHoverTip } from "@/lib/ads";
type AdTextProps = {
title: string;
textColor?: string;
bgColor?: string;
href?: string;
/** 广告到期时间(ISO 字符串):有值时气泡首行展示,无值时仅显示免责声明 */
endsAt?: string | null;
className?: string;
};
@@ -14,7 +17,14 @@ type AdTextProps = {
* 文字广告:仅当文字宽度超出容器时才启用跑马灯滚动,
* 能完整显示时居中静止。结构与样式复用侧栏 .j13-ad-text。
*/
export function AdText({ title, textColor, bgColor, href, className = "" }: AdTextProps) {
export function AdText({
title,
textColor,
bgColor,
href,
endsAt,
className = "",
}: AdTextProps) {
const trackRef = useRef<HTMLSpanElement>(null);
const [overflow, setOverflow] = useState(false);
@@ -35,6 +45,9 @@ export function AdText({ title, textColor, bgColor, href, className = "" }: AdTe
const cls = `j13-ad-text${overflow ? " is-overflow" : ""}${className ? ` ${className}` : ""}`;
const style: CSSProperties = { color: textColor, background: bgColor };
// 气泡首行:到期时间;第二行:免责声明
const tip = adHoverTip(endsAt);
const inner = (
<span
className={`j13-ad-text-track${overflow ? " is-overflow" : ""}`}
@@ -57,14 +70,20 @@ export function AdText({ title, textColor, bgColor, href, className = "" }: AdTe
rel="noopener noreferrer sponsored"
className={cls}
style={style}
data-tip={title}
data-tip={tip}
data-tip-delay={AD_TIP_DELAY_MS}
>
{inner}
</a>
);
}
return (
<span className={cls} style={style} data-tip={title}>
<span
className={cls}
style={style}
data-tip={tip}
data-tip-delay={AD_TIP_DELAY_MS}
>
{inner}
</span>
);