Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

64 lines
2.3 KiB
TypeScript

import { useMemo, useSyncExternalStore } from 'react';
import { useRedditStore } from '../state';
import type { RedditPost } from '../types';
import { getPostsSync, subscribePosts } from '../data/loader';
function isRedditPost(value: RedditPost | null | undefined): value is RedditPost {
return Boolean(value);
}
// Stable empty fallback so useSyncExternalStore's snapshot identity stays
// fixed before loadPosts() resolves — otherwise React would re-render on
// every commit (different `[]` reference each call).
const EMPTY_POSTS: RedditPost[] = Object.freeze([]) as readonly RedditPost[] as RedditPost[];
const getFixtureSnapshot = (): RedditPost[] => getPostsSync() ?? EMPTY_POSTS;
export function useFixturePosts(): RedditPost[] {
return useSyncExternalStore(subscribePosts, getFixtureSnapshot, getFixtureSnapshot);
}
export function useRedditPosts(): RedditPost[] {
const fixture = useFixturePosts();
const postsOverlay = useRedditStore((state) => state.posts);
const postIds = useRedditStore((state) => state.user.postIds);
return useMemo(() => {
const fixtureById = new Map(fixture.map((post) => [String(post.id), post]));
const resolvePost = (id: string): RedditPost | null => {
if (Object.prototype.hasOwnProperty.call(postsOverlay, id)) {
return postsOverlay[id];
}
return fixtureById.get(id) ?? null;
};
const indexedPosts = postIds.map((id) => resolvePost(id)).filter(isRedditPost);
const seen = new Set(indexedPosts.map((post) => post.id));
const fixturePosts = fixture
.map((post) => {
const id = String(post.id);
if (Object.prototype.hasOwnProperty.call(postsOverlay, id)) {
const overlay = postsOverlay[id];
if (overlay === null) return null;
return overlay;
}
return post;
})
.filter(isRedditPost)
.filter((post) => {
if (seen.has(post.id)) return false;
seen.add(post.id);
return true;
});
return [...indexedPosts, ...fixturePosts];
}, [postIds, postsOverlay, fixture]);
}
export function useRedditPostById(id: string | null | undefined): RedditPost | null {
const posts = useRedditPosts();
return useMemo(() => {
if (!id) return null;
return posts.find((post) => post.id === id) ?? null;
}, [id, posts]);
}