import { Accordion, For, Span, Stack, Text, Box, AbsoluteCenter, Button, Separator, HStack, Flex, Badge, IconButton, Container, Collapsible } from '@chakra-ui/react'; import { JsonPlayObject, PlayActivity } from '../../../core/Atomic.js'; 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 dayjs, { Dayjs } from 'dayjs'; import doy from 'dayjs/plugin/dayOfYear.js'; import { VscDebugRestart } from "react-icons/vsc"; import { GroupedVirtuoso } from 'react-virtuoso' import { ActivityDetails } from '../ActivityDetail.js'; import { sortByNewestPlayDate, sortByNewestSeenDate } from '../../../core/PlayUtils.js'; import "./PlayList.scss"; dayjs.extend(doy); export interface ActivityLogProps { data: PlayActivity[] sortBy?: 'played' | 'seen' render?: 'virtCollapse' | 'virtAccordian' | 'accordian' } interface GroupInfo { count: number date: Dayjs } interface GroupData { plays: PlayActivity[] date: Dayjs } const generateGroupInfo = (data: PlayActivity[]): GroupInfo[] => { const groupsReduced = data.reduce((acc: { groups: GroupInfo[], active?: GroupInfo }, curr, index) => { const date = dayjs(curr.play.data.playDate); if (acc.active === undefined) { return { ...acc, active: { count: 1, date } }; } if (acc.active.date.dayOfYear() !== date.dayOfYear()) { return { groups: [...acc.groups, acc.active], active: { count: 1, date } } } return { groups: acc.groups, active: { ...acc.active, count: acc.active.count + 1 } }; }, { groups: [] }); return groupsReduced.groups.concat(groupsReduced.active); } const generateGroupPlays = (data: PlayActivity[]): GroupData[] => { const groupsReduced = data.reduce((acc: { groups: GroupData[], active?: GroupData }, curr, index) => { const date = dayjs(curr.play.data.playDate); if (acc.active === undefined) { return { ...acc, active: { plays: [curr], date } }; } if (acc.active.date.dayOfYear() !== date.dayOfYear()) { return { groups: [...acc.groups, acc.active], active: { plays: [curr], date } } } return { groups: acc.groups, active: { ...acc.active, plays: acc.active.plays.concat(curr) } }; }, { groups: [] }); return groupsReduced.groups.concat(groupsReduced.active); } export const PlayList = (props: ActivityLogProps) => { const { data = [], sortBy = 'played', render = 'accordian' } = props; const sorted = useMemo(() => props.data.toSorted((a, b) => { if(sortBy === 'played') { return sortByNewestPlayDate(a.play, b.play) } return sortByNewestSeenDate(a.play, b.play); }), [data, sortBy]); if (render === 'accordian') { return } if (render === 'virtCollapse') { return } if (render === 'virtAccordian') { return ; } } const VirtualizedCollapse = (props: { data: PlayActivity[] }) => { const { data, } = props; const groups = useMemo(() => generateGroupInfo(data), [data]); return ( { // return
{args.children}
} // }} groupCounts={groups.map(x => x.count)} groupContent={(index) => { const gData = groups[index]; let headerText: string; if (gData.date.isToday()) { headerText = 'Today'; } else { headerText = gData.date.format('MMM DD'); if (gData.date.year() !== dayjs().year()) { headerText += `, ${gData.date.year()}`; } } return ( {headerText} ) }} itemContent={(index, groupIndex) => { const activity = data[index]; const { play } = activity; return ( {play.data.track} {play.data.artists.join(' / ')} {play.meta?.source} {activity.status === 'error' ? : null} ) }} /> ); } const VirtualizedAccordian = (props: { data: PlayActivity[] }) => { const { data, } = props; const groups = useMemo(() => generateGroupInfo(data), [data]); return ( { // @ts-ignore if (args.children.length === 1 && args.children[0].type.name === 'Group') { // @ts-ignore return
{args.children}
} // @ts-ignore return {args.children} }), Group: (args) => { return
{args.children}
} }} groupCounts={groups.map(x => x.count)} groupContent={(index) => { const gData = groups[index]; let headerText: string; if (gData.date.isToday()) { headerText = 'Today'; } else { headerText = gData.date.format('MMM DD'); if (gData.date.year() !== dayjs().year()) { headerText += `, ${gData.date.year()}`; } } return ( {headerText} ) }} itemContent={(index, groupIndex) => { const activity = data[index]; const { play } = activity; return ( {play.data.track} {play.data.artists.join(' / ')} {play.meta?.source} {activity.status === 'error' ? : null} ) }} /> ); } const PlainAccordian = (props: { data: PlayActivity[], sortBy: 'played' | 'seen' }) => { const { data = [], sortBy } = props; const groups = generateGroupPlays(data); return ( {groups.map((g) => { let headerText: string; if (g.date.isToday()) { headerText = 'Today'; } else { headerText = g.date.format('MMM DD'); if (g.date.year() !== dayjs().year()) { headerText += `, ${g.date.year()}`; } } return ( {headerText} {g.plays.map((activity, index) => { const { play } = activity; return ( {play.data.track} {play.data.artists.join(' / ')} {play.meta?.source} {activity.status === 'error' ? : null} ) })} ) })} ); } const StatusBadge = (props: ComponentProps & { data: PlayActivity }) => { const { data, ...rest } = props; let badgeColor = undefined, badgeText = capitalize(data.status); switch (data.status) { case 'queued': badgeColor = 'gray'; break; case 'scrobbled': badgeColor = 'green'; break; case 'error': badgeColor = 'red'; break; } return {badgeText} } export const ListContainer = (props?: ComponentProps) => { return }