feat: add favorites state and buttons
This commit is contained in:
@@ -24,6 +24,7 @@ import type { Post } from "../types/post";
|
||||
import { downloadAttachment } from "./messageStream/utils/downloadFile";
|
||||
import { mediaSaveKindFromType, useSaveToAlbumGuide } from "./SaveToAlbumGuide";
|
||||
import { useToast } from "./Toast";
|
||||
import { FavoriteButton } from "../favorites/FavoriteButton";
|
||||
|
||||
const MEDALS = ["🥇", "🥈", "🥉"];
|
||||
const MAX_ITEMS = 5;
|
||||
@@ -174,6 +175,7 @@ function PopularRankRow({
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex shrink-0 items-center gap-1">
|
||||
<FavoriteButton resourceId={r.id} />
|
||||
{r.isDownloadable ? (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "./messageStream/utils/downloadFile";
|
||||
import { mediaSaveKindFromType, useSaveToAlbumGuide } from "./SaveToAlbumGuide";
|
||||
import { useToast } from "./Toast";
|
||||
import { FavoriteButton } from "../favorites/FavoriteButton";
|
||||
|
||||
function isPlaceholderAsset(path: string | undefined | null) {
|
||||
return !path || path.includes("placeholder-cover");
|
||||
@@ -133,6 +134,11 @@ export function RecommendedCard({
|
||||
{r.badgeLabel}
|
||||
</span>
|
||||
) : null}
|
||||
<FavoriteButton
|
||||
resourceId={r.id}
|
||||
size="sm"
|
||||
className="absolute right-3 top-3 z-20 shadow-lg shadow-black/30"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AlbumBubble } from "./bubbles/AlbumBubble";
|
||||
import { VideoBubble } from "./bubbles/VideoBubble";
|
||||
import { LinkPreviewCard } from "./LinkPreviewCard";
|
||||
import { formatDateTime } from "./utils/formatTime";
|
||||
import { FavoriteButton } from "../../favorites/FavoriteButton";
|
||||
|
||||
type BubbleComponent = ComponentType<{ post: Post }>;
|
||||
|
||||
@@ -53,6 +54,11 @@ export function MessageBubble({
|
||||
isVisual ? "p-0" : "px-4 py-3"
|
||||
}`}
|
||||
>
|
||||
<FavoriteButton
|
||||
resourceId={post.id}
|
||||
size="sm"
|
||||
className="absolute right-3 top-3 z-20 shadow-lg shadow-black/30"
|
||||
/>
|
||||
<Bubble post={post} />
|
||||
{post.linkPreview ? (
|
||||
<div className={isVisual ? "px-4 pt-3" : "mt-3"}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { FilterChips } from "./FilterChips";
|
||||
import { MessageBubble } from "./MessageBubble";
|
||||
import { useGroupedByDay } from "./hooks/useGroupedByDay";
|
||||
import { usePostStream } from "./hooks/usePostStream";
|
||||
import { useFavorites } from "../../favorites/FavoritesProvider";
|
||||
|
||||
export type MessageStreamProps = {
|
||||
scope: PostScope;
|
||||
@@ -30,9 +31,14 @@ export function MessageStream({ scope }: MessageStreamProps) {
|
||||
|
||||
const { items, isLoading, error, hasMore, loadMore, reset } =
|
||||
usePostStream(params);
|
||||
const { ensureFavoriteIds } = useFavorites();
|
||||
const groups = useGroupedByDay(items, lang);
|
||||
const retryLabel = lang === "zh-CN" ? "重试" : "Retry";
|
||||
|
||||
useEffect(() => {
|
||||
void ensureFavoriteIds(items.map((item) => item.id)).catch(() => undefined);
|
||||
}, [ensureFavoriteIds, items]);
|
||||
|
||||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||
const filterBarRef = useRef<HTMLDivElement>(null);
|
||||
const hasMoreRef = useRef(hasMore);
|
||||
|
||||
Reference in New Issue
Block a user