nicolium: fix sorting of items in filtered timelines
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -33,9 +33,10 @@ import {
|
||||
} from '@/queries/timelines/use-timelines';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
import { selectChild } from '@/utils/scroll-utils';
|
||||
import { hasActiveFilters, sortFilteredTimeline } from '@/utils/timeline-filter';
|
||||
|
||||
import type { FilterContextType } from '@/queries/settings/use-filters';
|
||||
import type { Settings } from '@/schemas/frontend-settings';
|
||||
import type { TimelineFilters } from '@/schemas/frontend-settings';
|
||||
import type { TimelineEntry } from '@/stores/timelines';
|
||||
import type { VirtuosoHandle } from 'react-virtuoso';
|
||||
|
||||
@ -361,7 +362,7 @@ type IBaseTimeline = Pick<
|
||||
'emptyMessageIcon' | 'emptyMessageText' | 'onTopItemChanged'
|
||||
> & {
|
||||
featuredStatusIds?: Array<string>;
|
||||
filters?: Settings['timelines'][string];
|
||||
filters?: TimelineFilters;
|
||||
};
|
||||
|
||||
interface ITimeline extends IBaseTimeline {
|
||||
@ -478,45 +479,32 @@ const Timeline: React.FC<ITimeline> = ({
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
.map((entry) => {
|
||||
if (entry.type === 'status') {
|
||||
return {
|
||||
...entry,
|
||||
filtered:
|
||||
(filters?.showDirect === false && entry.isDirect) ||
|
||||
(filters?.showReblogs === false && entry.isReblog) ||
|
||||
(filters?.showReplies === false && entry.isReply) ||
|
||||
(filters?.showQuotes === false && entry.isQuote) ||
|
||||
(filters?.showNonMedia === false && !entry.hasMedia),
|
||||
};
|
||||
}
|
||||
return entry;
|
||||
})
|
||||
.forEach((entry, entryIndex, entries) => {
|
||||
if (entry.type === 'status' && entry.filtered) {
|
||||
return;
|
||||
}
|
||||
const processedEntries = hasActiveFilters(filters)
|
||||
? sortFilteredTimeline(entries, filters)
|
||||
: entries;
|
||||
|
||||
if (entry.type === 'status') {
|
||||
const previousEntry = entries[entryIndex - 1];
|
||||
const nextEntry = entries[entryIndex + 1];
|
||||
processedEntries.forEach((entry, entryIndex, arr) => {
|
||||
if (entry.type === 'status') {
|
||||
const previousEntry = arr[entryIndex - 1];
|
||||
const nextEntry = arr[entryIndex + 1];
|
||||
|
||||
rendered.push(
|
||||
renderEntry(entry, rendered.length, {
|
||||
isConnectedTop:
|
||||
!!entry.isConnectedTop &&
|
||||
previousEntry?.type === 'status' &&
|
||||
!previousEntry.filtered,
|
||||
isConnectedBottom:
|
||||
!!entry.isConnectedBottom && nextEntry?.type === 'status' && !nextEntry.filtered,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
rendered.push(
|
||||
renderEntry(entry, rendered.length, {
|
||||
isConnectedTop:
|
||||
!!entry.isConnectedTop &&
|
||||
previousEntry?.type === 'status' &&
|
||||
!!previousEntry.isConnectedBottom,
|
||||
isConnectedBottom:
|
||||
!!entry.isConnectedBottom &&
|
||||
nextEntry?.type === 'status' &&
|
||||
!!nextEntry.isConnectedTop,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
rendered.push(renderEntry(entry, rendered.length));
|
||||
});
|
||||
rendered.push(renderEntry(entry, rendered.length));
|
||||
});
|
||||
|
||||
return rendered;
|
||||
}, [entries, contextType, timelineId, featuredStatusIds, filters]);
|
||||
|
||||
@ -125,5 +125,6 @@ const settingsSchema = v.object({
|
||||
});
|
||||
|
||||
type Settings = v.InferOutput<typeof settingsSchema>;
|
||||
type TimelineFilters = Settings['timelines'][string];
|
||||
|
||||
export { settingsSchema, type Settings };
|
||||
export { settingsSchema, type Settings, type TimelineFilters };
|
||||
|
||||
82
packages/nicolium/src/utils/timeline-filter.ts
Normal file
82
packages/nicolium/src/utils/timeline-filter.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { compareId } from '@/utils/comparators';
|
||||
|
||||
import type { TimelineFilters } from '@/schemas/frontend-settings';
|
||||
import type { TimelineEntry } from '@/stores/timelines';
|
||||
|
||||
type StatusEntry = Extract<TimelineEntry, { type: 'status' }>;
|
||||
|
||||
const isEntryFiltered = (entry: StatusEntry, filters: TimelineFilters): boolean =>
|
||||
(filters?.showDirect === false && entry.isDirect) ||
|
||||
(filters?.showReblogs === false && entry.isReblog) ||
|
||||
(filters?.showReplies === false && entry.isReply) ||
|
||||
(filters?.showQuotes === false && entry.isQuote) ||
|
||||
(filters?.showNonMedia === false && !entry.hasMedia);
|
||||
|
||||
const hasActiveFilters = (filters: TimelineFilters | undefined): filters is TimelineFilters =>
|
||||
!!filters &&
|
||||
(filters.showDirect === false ||
|
||||
filters.showReblogs === false ||
|
||||
filters.showReplies === false ||
|
||||
filters.showQuotes === false ||
|
||||
filters.showNonMedia === false);
|
||||
|
||||
const sortFilteredTimeline = (
|
||||
entries: Array<TimelineEntry>,
|
||||
filters: TimelineFilters,
|
||||
): Array<TimelineEntry> => {
|
||||
const result: Array<TimelineEntry> = [];
|
||||
let collectedGroups: Array<{ sortKey: string; entries: Array<StatusEntry> }> = [];
|
||||
let currentGroup: Array<StatusEntry> = [];
|
||||
|
||||
const endGroup = () => {
|
||||
if (currentGroup.length === 0) return;
|
||||
|
||||
let sortKey = currentGroup[0].originalId;
|
||||
for (let i = 1; i < currentGroup.length; i++) {
|
||||
if (compareId(currentGroup[i].originalId, sortKey) > 0) {
|
||||
sortKey = currentGroup[i].originalId;
|
||||
}
|
||||
}
|
||||
|
||||
collectedGroups.push({ sortKey, entries: currentGroup });
|
||||
currentGroup = [];
|
||||
};
|
||||
|
||||
const endSection = () => {
|
||||
endGroup();
|
||||
|
||||
if (collectedGroups.length > 1) {
|
||||
collectedGroups.sort((a, b) => compareId(b.sortKey, a.sortKey));
|
||||
}
|
||||
|
||||
for (const group of collectedGroups) {
|
||||
for (const entry of group.entries) {
|
||||
result.push(entry);
|
||||
}
|
||||
}
|
||||
collectedGroups = [];
|
||||
};
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.type === 'status' && isEntryFiltered(entry, filters)) continue;
|
||||
|
||||
if (entry.type !== 'status') {
|
||||
endSection();
|
||||
result.push(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.isConnectedTop && currentGroup.length > 0) {
|
||||
currentGroup.push(entry);
|
||||
} else {
|
||||
endGroup();
|
||||
currentGroup = [entry];
|
||||
}
|
||||
}
|
||||
|
||||
endSection();
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export { sortFilteredTimeline, hasActiveFilters };
|
||||
Reference in New Issue
Block a user