diff --git a/package-lock.json b/package-lock.json
index d4699f5f..dcc7d5f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -174,7 +174,7 @@
"react-icons": "^5.6.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.15.0",
- "react-virtuoso": "^4.18.4",
+ "react-virtuoso": "^4.18.7",
"sass": "^1.99.0",
"shiki": "^4.0.2",
"sinon": "^21.0.2",
diff --git a/package.json b/package.json
index b4ab3343..984a9754 100644
--- a/package.json
+++ b/package.json
@@ -213,7 +213,7 @@
"react-icons": "^5.6.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.15.0",
- "react-virtuoso": "^4.18.4",
+ "react-virtuoso": "^4.18.7",
"sass": "^1.99.0",
"shiki": "^4.0.2",
"sinon": "^21.0.2",
@@ -251,4 +251,4 @@
"public"
]
}
-}
\ No newline at end of file
+}
diff --git a/src/client/components/playActivity/PlayList.tsx b/src/client/components/playActivity/PlayList.tsx
index 99a460f6..ffaf82c5 100644
--- a/src/client/components/playActivity/PlayList.tsx
+++ b/src/client/components/playActivity/PlayList.tsx
@@ -4,11 +4,11 @@ import { ShortDateDisplay } from '../DateDisplay.js';
import { TextMuted } from '../TextMuted.js';
import { LuChevronRight } from "react-icons/lu"
import { capitalize } from '../../../core/StringUtils.js';
-import { ComponentProps, useMemo, forwardRef, Fragment } from "react"
+import React, { ComponentProps, useMemo, forwardRef, Fragment } from "react"
import dayjs, { Dayjs } from 'dayjs';
import doy from 'dayjs/plugin/dayOfYear.js';
import { VscDebugRestart } from "react-icons/vsc";
-import { GroupedVirtuoso } from 'react-virtuoso'
+import { GroupedVirtuoso, Components, LogLevel } from 'react-virtuoso'
import { ActivityDetailFetchable, ActivityDetails } from '../ActivityDetail.js';
import { sortByNewestPlayDate, sortByNewestSeenDate } from '../../../core/PlayUtils.js';
import "./PlayList.scss";
@@ -192,7 +192,7 @@ const VirtualizedCollapse = (props: { data: PlayApiCommon[] }) => {
paddingBlock: "var(--chakra-spacing-4)",
paddingInline: "var(--chakra-spacing-4)"
}}>
-
+
)
@@ -201,18 +201,7 @@ const VirtualizedCollapse = (props: { data: PlayApiCommon[] }) => {
);
}
-const VirtualizedAccordian = (props: { data: PlayApiCommon[] }) => {
- const {
- data,
- } = props;
- const groups = useMemo(() => generateGroupInfo(data), [data]);
- return (
- {
+const CustomList: Components['List'] = React.forwardRef((args, ref) => {
// @ts-ignore
if (args.children.length === 1 && args.children[0].type.name === 'Group') {
// @ts-ignore
@@ -220,14 +209,59 @@ const VirtualizedAccordian = (props: { data: PlayApiCommon[] }) => {
}
// @ts-ignore
return {args.children}
- }),
- Group: (args) => {
- return {args.children}
- }
- }}
- groupCounts={groups.map(x => x.count)}
- groupContent={(index) => {
- const gData = groups[index];
+});
+
+const CustomGroup: Components['Group'] = React.forwardRef((args) => {
+ return {args.children}
;
+});
+
+const ItemComponent = React.memo((props: {index: number, activity}) => {
+ const {index, activity} = props;
+ const { play } = activity;
+ console.log(`render ${play.data.track}`);
+ return (
+
+
+
+
+
+ {play.data.track}
+ {play.data.artists.map(x => x.name).join(' / ')}
+
+
+ {play.meta?.source}
+
+
+
+
+
+ {activity.state === 'failed' ?
+
+ : null}
+
+
+
+
+
+
+
+
+
+ )
+});
+
+const VirtualizedAccordian = (props: { data: PlayApiCommon[] }) => {
+ const {
+ data,
+ } = props;
+
+ const groups = useMemo(() => generateGroupInfo(data), [data]);
+
+ const GroupComponent = (props: {index: number}) => {
+ const gData = groups[props.index];
let headerText: string;
if (gData.date.isToday()) {
headerText = 'Today';
@@ -238,7 +272,7 @@ const VirtualizedAccordian = (props: { data: PlayApiCommon[] }) => {
}
}
return (
-
+
{headerText}
@@ -247,46 +281,24 @@ const VirtualizedAccordian = (props: { data: PlayApiCommon[] }) => {
-
+
- )
- }}
- itemContent={(index, groupIndex) => {
- const activity = data[index];
- const { play } = activity;
- return (
-
-
-
-
-
- {play.data.track}
- {play.data.artists.map(x => x.name).join(' / ')}
-
-
- {play.meta?.source}
-
-
-
-
-
- {activity.state === 'failed' ?
-
- : null}
-
+ );
+ }
-
-
-
-
-
-
-
- )
+ return (
+ x.count)}
+ groupContent={(index) => }
+ itemContent={(index) => }
/>
);
}
diff --git a/src/stories/List.stories.tsx b/src/stories/List.stories.tsx
index 7a0a15ba..a83cfe0c 100644
--- a/src/stories/List.stories.tsx
+++ b/src/stories/List.stories.tsx
@@ -58,56 +58,61 @@ let playData: PlayApiCommonDetailed[] = [];
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const List = meta.story({
- parameters: {
- msw: {
- handlers: [
- http.get<{uid: string}>('/api/plays/:uid', async ({ params }) => {
- const existing = playData.find(x => x.uid === params.uid);
- if(existing !== undefined) {
- return HttpResponse.json(existing);
- }
- return HttpResponse.json(generatePlayApiCommonDetailed());
- }),
- ],
- },
+ args: {
+ render: "accordian"
},
- loaders: [
- async () => {
- const queued = normalizePlays(generateArray(7,() => generatePlayWithLifecycle()), {endDate: dayjs()}).map(x => {
- const jsonPlay = asJsonPlayObject(x);
- return generatePlayApiCommonDetailed({playOpts: [{state: 'queued', play: jsonPlay}], inputOpts: [{play: jsonPlay}]})
- });
- const scrobbledPlay = asJsonPlayObject(await playWithLifecycleScrobble(generatePlayWithLifecycle({lifecycleSteps: {preCompare: [true, 'skipped', true]}})));
- const scrobbledApi = generatePlayApiCommonDetailed({
- playOpts: [{ play: scrobbledPlay, state: 'scrobbled'}],
- inputOpts: [{play: scrobbledPlay}]
- });
+ parameters: {
+ msw: {
+ handlers: [
+ http.get<{uid: string}>('/api/plays/:uid', async ({ params }) => {
+ const existing = playData.find(x => x.uid === params.uid);
+ if(existing !== undefined) {
+ return HttpResponse.json(existing);
+ }
+ return HttpResponse.json(generatePlayApiCommonDetailed());
+ }),
+ ],
+ },
+},
- const scrobbleErrorPlay = asJsonPlayObject(await playWithLifecycleScrobble(generatePlayWithLifecycle(), {error: true}));
+ //render: function Render(args) { return () }
+ loaders: [
+ async () => {
+ const queued = normalizePlays(generateArray(7,() => generatePlayWithLifecycle()), {endDate: dayjs()}).map(x => {
+ const jsonPlay = asJsonPlayObject(x);
+ return generatePlayApiCommonDetailed({playOpts: [{state: 'queued', play: jsonPlay}], inputOpts: [{play: jsonPlay}]})
+ });
- const scrobbleError = generatePlayApiCommonDetailed({
- playOpts: [{ play: scrobbleErrorPlay, state: 'failed'}],
- inputOpts: [{play: scrobbleErrorPlay}]
- });
+ const scrobbledPlay = asJsonPlayObject(await playWithLifecycleScrobble(generatePlayWithLifecycle({lifecycleSteps: {preCompare: [true, 'skipped', true]}})));
+ const scrobbledApi = generatePlayApiCommonDetailed({
+ playOpts: [{ play: scrobbledPlay, state: 'scrobbled'}],
+ inputOpts: [{play: scrobbledPlay}]
+ });
- const promisedScrobbled = generateArray(10,() => playWithLifecycleScrobble(generatePlayWithLifecycle({lifecycleSteps: {preCompare: [true, 'skipped', true]}})));
- const promised = await Promise.all(promisedScrobbled);
- const yesterdayScrobbled = normalizePlays(promised, {endDate: dayjs().subtract(1, 'd').subtract(100, 'm')}).map((x) => {
- const jPlay = asJsonPlayObject(x);
- return generatePlayApiCommonDetailed({
- playOpts: [{ play: jPlay, state: 'scrobbled'}],
- inputOpts: [{play: jPlay}]
- });
+ const scrobbleErrorPlay = asJsonPlayObject(await playWithLifecycleScrobble(generatePlayWithLifecycle(), {error: true}));
+
+ const scrobbleError = generatePlayApiCommonDetailed({
+ playOpts: [{ play: scrobbleErrorPlay, state: 'failed'}],
+ inputOpts: [{play: scrobbleErrorPlay}]
});
- playData = [
- ...queued,
- scrobbledApi,
- scrobbleError,
- ...yesterdayScrobbled
- ];
- return {data: playData};
- }
- ],
- //render: function Render(args) { return () }
+
+ const promisedScrobbled = generateArray(10,() => playWithLifecycleScrobble(generatePlayWithLifecycle({lifecycleSteps: {preCompare: [true, 'skipped', true]}})));
+ const promised = await Promise.all(promisedScrobbled);
+ const yesterdayScrobbled = normalizePlays(promised, {endDate: dayjs().subtract(1, 'd').subtract(100, 'm')}).map((x) => {
+ const jPlay = asJsonPlayObject(x);
+ return generatePlayApiCommonDetailed({
+ playOpts: [{ play: jPlay, state: 'scrobbled'}],
+ inputOpts: [{play: jPlay}]
+ });
+ });
+ playData = [
+ ...queued,
+ scrobbledApi,
+ scrobbleError,
+ ...yesterdayScrobbled
+ ];
+ return {data: playData};
+ }
+]
});
\ No newline at end of file