···77import { QueryPlaysOpts, QueryPlaysOptsJson } from '../../../backend/common/database/drizzle/repositories/PlayRepository.js';
88import { VscDebugRestart } from 'react-icons/vsc';
99import { sortByNewestDate } from '../../../core/PlayUtils.js';
1010+import { getAllIndexes } from '../../../core/DataUtils.js';
10111112dayjs.extend(doy);
1213···106107}
107108108109export const generateFlatItems = (data: PlayApiCommon[]) => {
110110+ // ensure there are no duplicates
111111+ // this may happen if a play is "bumped" from one "page" to another, based on offset,
112112+ // when new plays are inserted out of order (playedAt)
113113+ const allIds: string[] = [];
114114+ const dupes: string[] = [];
115115+ for(const d of data) {
116116+ if(allIds.includes(d.uid)) {
117117+ console.warn(`Duplicate ID detected ${d.uid}`);
118118+ dupes.push(d.uid);
119119+ } else {
120120+ allIds.push(d.uid);
121121+ }
122122+ }
123123+ for(const uid of dupes) {
124124+ const indexes = getAllIndexes(data, (d) => d.uid === uid);
125125+ // keep only the first one since its likely the freshest
126126+ const oldIndexes = indexes.slice(1);
127127+ for(const old of oldIndexes) {
128128+ data.splice(old, 1);
129129+ }
130130+ }
131131+109132 const groups = generateGroupPlays(data);
110133 groups.sort((a, b) => sortByNewestDate(a.date, b.date));
111134 return groups.map((x) => {
112135 x.plays.sort((a, b) => sortByNewestDate(a.playedAt, b.playedAt));
113113- return [{count: x.plays.length, date: x.date, uid: x.date.toISOString()}, ...x.plays];
136136+ return [{count: x.plays.length, date: x.date, uid: `${x.date.toISOString()}-${x.plays.length}`}, ...x.plays];
114137 }).flat(1);
115138}