Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
Something went wrong. Try again.
4.7 kB · 135 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135import { Badge, HStack, Separator, Spinner } from "@chakra-ui/react";import { useSSEContext, useSSEEvent } from "@flamefrontend/sse-runtime-react";import React, { type ComponentProps, type PropsWithChildren, type ReactNode, useCallback, useEffect, useState } from "react";import { useTimeout } from 'react-use-timeout';import { COMPONENT_STATE, type ComponentCommonApiJson, type ComponentsApiJson, componentStateToFriendly, type MsSseEvent, type PlayApiCommon } from "../../core/Api";import type {Second} from "../../core/Atomic";import { capitalizeWords } from "../../core/StringUtils";import { TextTip } from "./ToggleTip";
export const PlayStateBadge = (props: PropsWithChildren<ComponentProps<typeof Badge>> & { state: PlayApiCommon['state'], suffix?: React.JSX.Element, hasDeadQueue?: boolean }) => {
const { state, suffix, children, ...rest } = props;
let badgeColor = undefined; let badgeText: string | ReactNode = capitalizeWords(state);
switch (state) { case 'queued': badgeColor = 'gray'; break; case 'scrobbled': case 'discovered': badgeColor = 'green'; break; case 'failed': case ('failed TBR' as PlayApiCommon['state']): badgeColor = 'red'; if(props.hasDeadQueue) { badgeText = <TextTip text='Failed TBR'><span>Failed, <strong>T</strong>o <strong>B</strong>e <strong>R</strong>etried</span></TextTip>; } break; case 'duped': case 'discarded': badgeColor = 'orange'; break; }
return <Badge variant="surface" colorPalette={badgeColor} {...rest}>{children ?? badgeText}{suffix}</Badge>}
const DEFAULT_EXPIRES = 10000;
export const NewBadge = (props: ComponentProps<typeof Badge>) => <Badge variant="surface" colorPalette="blue" {...props}/>;
export const EphemeralElement = (props: { expires?: Second | boolean, children: React.ReactNode }) => {
const { expires = DEFAULT_EXPIRES, children } = props; let expiresTime: Second | undefined; if(expires === true) { expiresTime = DEFAULT_EXPIRES; } else if(expires !== false) { expiresTime = expires; } const [shouldShow, setShouldShow] = useState<boolean>(true); const hide = useCallback(() => { setShouldShow(false);
}, [setShouldShow]); const hideTimeout = useTimeout(hide, expiresTime ?? DEFAULT_EXPIRES); useEffect(() => { if (expiresTime !== undefined) { hideTimeout.start(); } }, []);
if (shouldShow) { return children; } return null;}
export const ComponentStateBadge = (props: ComponentProps<typeof Badge> & { data: Pick<ComponentsApiJson, 'state' | 'errors' | 'authType'>, componentId?: number, live?: boolean, separator?: boolean | React.JSX.Element, suffix?: React.JSX.Element, loading?: boolean}) => {
const { data, suffix, separator, loading = false, ...rest } = props;
const [componentState, setComponentState] = useState(data.state);
useEffect(() =>{ setComponentState(data.state); },[data.state, setComponentState]);
const client = useSSEContext<MsSseEvent>(); const connection = useSSEEvent(client, 'componentUpdate', (payload) => { if(props.componentId !== undefined && props.live && payload.componentId === props.componentId && payload.data.state !== undefined) { setComponentState(payload.data.state); } });
let badgeColor = undefined;
switch (componentState) { case COMPONENT_STATE.STOPPED: badgeColor = 'gray'; break; case COMPONENT_STATE.RUNNING: badgeColor = 'green'; break; case COMPONENT_STATE.INITIALIZING: badgeColor = 'cyan'; break; case COMPONENT_STATE.ERROR: case COMPONENT_STATE.NOT_READY: badgeColor = 'red'; break; case COMPONENT_STATE.IDLE: badgeColor = 'yellow'; break; case COMPONENT_STATE.IGNORED: badgeColor = 'orange'; break; }
let sep: React.JSX.Element | undefined; if(suffix !== undefined) { if(separator === true) { sep = <Separator orientation="vertical" borderColor="var(--chakra-colors-color-palette-muted)" ml="2" height="5"/>; } else if(separator !== false) { sep = separator; } }
return (<Badge variant="surface" colorPalette={badgeColor} {...rest}> <HStack gap="0">{loading ? <Spinner animationDuration="0.8s" /> : undefined}{componentStateToFriendly(componentState)}{sep}{suffix}</HStack></Badge> );}