nicolium: what about testing stuff before committing?

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-03-05 17:21:21 +01:00
parent fe80efd7ff
commit 7984a47a29
5 changed files with 35 additions and 10 deletions

View File

@ -220,14 +220,17 @@ const TimelineStatus: React.FC<ITimelineStatus> = (props): React.JSX.Element =>
);
};
type IBaseTimeline = Pick<IScrollableList, 'emptyMessageIcon' | 'emptyMessageText'>;
type IBaseTimeline = Pick<
IScrollableList,
'emptyMessageIcon' | 'emptyMessageText' | 'onTopItemChanged'
>;
interface ITimeline extends IBaseTimeline {
query: ReturnType<typeof useHomeTimeline>;
contextType?: FilterContextType;
}
const Timeline: React.FC<ITimeline> = ({ query, contextType = 'public' }) => {
const Timeline: React.FC<ITimeline> = ({ query, contextType = 'public', ...props }) => {
const node = useRef<VirtuosoHandle | null>(null);
const {
@ -293,6 +296,7 @@ const Timeline: React.FC<ITimeline> = ({ query, contextType = 'public' }) => {
ref={node}
hasMore={hasNextPage}
onLoadMore={fetchNextPage}
{...props}
>
{(entries || []).map(renderEntry)}
</ScrollableList>

View File

@ -70,6 +70,8 @@ interface IScrollableList extends VirtuosoProps<any, any> {
onScrollToTop?: () => void;
/** Callback when the list is scrolled. */
onScroll?: () => void;
/** Callback when the topmost visible item index changes. */
onTopItemChanged?: (index: number) => void;
/** Placeholder component to render while loading. */
placeholderComponent?: React.ComponentType | React.NamedExoticComponent;
/** Number of placeholders to render while loading. */
@ -104,6 +106,7 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(
onScroll,
onScrollToTop,
onLoadMore,
onTopItemChanged,
className,
listClassName,
itemClassName,
@ -220,6 +223,7 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(
// HACK: using the first index can be buggy.
// Track the second item instead, unless the endIndex comes before it (eg one 1 item in view).
topIndex.current = Math.min(range.startIndex + 1, range.endIndex);
onTopItemChanged?.(topIndex.current);
handleScroll();
};

View File

@ -14,7 +14,12 @@ interface StreamConfig {
params?: StreamingParams;
}
const useTimeline = (timelineId: string, fetcher: TimelineFetcher, streamConfig?: StreamConfig) => {
const useTimeline = (
timelineId: string,
fetcher: TimelineFetcher,
streamConfig?: StreamConfig,
restoring?: boolean,
) => {
const timeline = useStoreTimeline(timelineId);
const timelineActions = useTimelinesActions();
@ -30,11 +35,11 @@ const useTimeline = (timelineId: string, fetcher: TimelineFetcher, streamConfig?
try {
const response = await fetcher();
importEntities({ statuses: response.items });
timelineActions.expandTimeline(timelineId, response.items, !!response.next, true);
timelineActions.expandTimeline(timelineId, response.items, !!response.next, true, restoring);
} catch (error) {
timelineActions.setError(timelineId, true);
}
}, [timelineId]);
}, [timelineId, restoring]);
const fetchNextPage = useCallback(async () => {
timelineActions.setLoading(timelineId, true);

View File

@ -17,14 +17,19 @@ import type {
WrenchedTimelineParams,
} from 'pl-api';
const useHomeTimeline = (params?: Omit<HomeTimelineParams, keyof PaginationParams>) => {
const useHomeTimeline = (
params?: Omit<HomeTimelineParams, keyof PaginationParams>,
maxId?: string,
) => {
const client = useClient();
const stream = 'home';
return useTimeline(
'home',
(paginationParams) => client.timelines.homeTimeline({ ...params, ...paginationParams }),
(paginationParams) =>
client.timelines.homeTimeline({ ...params, ...(paginationParams || { max_id: maxId }) }),
{ stream },
!!maxId,
);
};

View File

@ -23,8 +23,8 @@ type TimelineEntry =
}
| {
type: 'gap';
sinceId: string;
maxId: string;
sinceId?: string;
minId: string;
};
interface TimelineData {
@ -46,6 +46,7 @@ interface State {
statuses: Array<Status>,
hasMore?: boolean,
initialFetch?: boolean,
restoring?: boolean,
) => void;
receiveStreamingStatus: (timelineId: string, status: Status) => void;
deleteStatus: (statusId: string) => void;
@ -144,13 +145,19 @@ const useTimelinesStore = create<State>()(
mutative((set) => ({
timelines: {} as Record<string, TimelineData>,
actions: {
expandTimeline: (timelineId, statuses, hasMore, initialFetch = false) =>
expandTimeline: (timelineId, statuses, hasMore, initialFetch = false, restoring = false) =>
set((state) => {
const timeline = state.timelines[timelineId] ?? createEmptyTimeline();
const entries = processPage(statuses);
if (initialFetch) timeline.entries = entries;
else timeline.entries.push(...entries);
if (restoring) {
timeline.entries.unshift({
type: 'gap',
minId: statuses[0].id,
});
}
timeline.isPending = false;
timeline.isFetching = false;
if (typeof hasMore === 'boolean') {