mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
refactor(feed): gate feed cache to infinite scroll mode
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import useFeedCacheStore from '../use-feed-cache-store';
|
||||
|
||||
describe('useFeedCacheStore', () => {
|
||||
beforeEach(() => {
|
||||
useFeedCacheStore.getState().clearFeeds();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
useFeedCacheStore.getState().clearFeeds();
|
||||
});
|
||||
|
||||
it('accessFeed adds entries', () => {
|
||||
const { accessFeed } = useFeedCacheStore.getState();
|
||||
|
||||
accessFeed('plebbit/board', 'board');
|
||||
const afterFirst = useFeedCacheStore.getState().cachedFeeds;
|
||||
expect(afterFirst.length).toBe(1);
|
||||
expect(afterFirst[0].key).toBe('plebbit/board');
|
||||
expect(afterFirst[0].type).toBe('board');
|
||||
|
||||
accessFeed('plebbit/catalog', 'catalog');
|
||||
const afterSecond = useFeedCacheStore.getState().cachedFeeds;
|
||||
expect(afterSecond.length).toBe(2);
|
||||
expect(afterSecond.some((f) => f.key === 'plebbit/catalog' && f.type === 'catalog')).toBe(true);
|
||||
});
|
||||
|
||||
it('evicts least recently accessed when cache exceeds maxCacheSize (2)', () => {
|
||||
const { accessFeed, isFeedCached } = useFeedCacheStore.getState();
|
||||
vi.useFakeTimers();
|
||||
|
||||
accessFeed('a', 'board');
|
||||
vi.advanceTimersByTime(1);
|
||||
accessFeed('b', 'board');
|
||||
vi.advanceTimersByTime(1);
|
||||
accessFeed('c', 'board');
|
||||
|
||||
expect(isFeedCached('a')).toBe(false);
|
||||
expect(isFeedCached('b')).toBe(true);
|
||||
expect(isFeedCached('c')).toBe(true);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('clearFeeds empties cache', () => {
|
||||
const { accessFeed, clearFeeds } = useFeedCacheStore.getState();
|
||||
|
||||
accessFeed('plebbit/board', 'board');
|
||||
accessFeed('other/board', 'catalog');
|
||||
expect(useFeedCacheStore.getState().cachedFeeds.length).toBe(2);
|
||||
|
||||
clearFeeds();
|
||||
const after = useFeedCacheStore.getState().cachedFeeds;
|
||||
expect(after.length).toBe(0);
|
||||
expect(after).toEqual([]);
|
||||
});
|
||||
|
||||
it('clearFeeds is idempotent when already empty', () => {
|
||||
const { clearFeeds } = useFeedCacheStore.getState();
|
||||
|
||||
expect(useFeedCacheStore.getState().cachedFeeds.length).toBe(0);
|
||||
|
||||
clearFeeds();
|
||||
clearFeeds();
|
||||
clearFeeds();
|
||||
|
||||
const after = useFeedCacheStore.getState().cachedFeeds;
|
||||
expect(after.length).toBe(0);
|
||||
expect(after).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ interface FeedCacheState {
|
||||
maxCacheSize: number;
|
||||
accessFeed: (key: string, type: 'board' | 'catalog') => void;
|
||||
removeFeed: (key: string) => void;
|
||||
clearFeeds: () => void;
|
||||
isFeedCached: (key: string) => boolean;
|
||||
}
|
||||
|
||||
@@ -45,6 +46,12 @@ const useFeedCacheStore = create<FeedCacheState>((set, get) => ({
|
||||
set({ cachedFeeds: cachedFeeds.filter((feed) => feed.key !== key) });
|
||||
},
|
||||
|
||||
clearFeeds: () => {
|
||||
const { cachedFeeds } = get();
|
||||
if (cachedFeeds.length === 0) return;
|
||||
set({ cachedFeeds: [] });
|
||||
},
|
||||
|
||||
isFeedCached: (key: string) => {
|
||||
const { cachedFeeds } = get();
|
||||
return cachedFeeds.some((feed) => feed.key === key);
|
||||
|
||||
Reference in New Issue
Block a user