import React, {Fragment, useCallback, useMemo} from 'react'; import StatusCardSkeleton, {type StatusCardSkeletonData} from "./StatusCardSkeleton"; import SkeletonParagraph from "../skeleton/SkeletonParagraph"; import {Link} from "react-router-dom"; import {sourceAdapter} from "../../status/ducks"; import type {RootState} from "../../store"; import {connect, type ConnectedProps} from "react-redux"; import Player from "../player/Player"; import {useStartSourceMutation, useListenSourceMutation} from "./sourceDucks"; import './statusCard.scss'; import Tooltip from '../Tooltip'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons' const ambiguousTypes = ['lastfm','listenbrainz','koito','librefm','maloja','rocksky']; export interface SourceStatusCardData extends StatusCardSkeletonData, PropsFromRedux { loading?: boolean } const statusToStatusType = (status: string) => { const lower = status.toLowerCase(); if(lower.includes('running') || lower.includes('polling') || lower.includes('received data')) { return 'active'; } if(lower.includes('idle') || lower.includes('awaiting data')) { return 'warn'; } return 'error'; } const SourceStatusCard = (props: SourceStatusCardData) => { const { loading = false, data, data: { display, name, type, status, } = {} } = props; let header: string | undefined = display; let body = ; const [startPut, startResult] = useStartSourceMutation(); const [listenPut, listenResult] = useListenSourceMutation(); const tryStart = useCallback((name: string, type: string, force?: boolean) => startPut({name, type, force}), [startPut]); const tryListen = useCallback((name: string, type: string, currentListening?: boolean) => { // cycle through states let nextListen: boolean | undefined; switch(currentListening) { case true: nextListen = false; break; case false: nextListen = undefined; break; case undefined: nextListen = true; break; } listenPut({name, type, listening: nextListen}); }, [listenPut]); let subtitleElement = null; if(data !== undefined) { const { display, name, canPoll, hasAuth, authed, status, tracksDiscovered, hasAuthInteraction, type, players = {}, sot, supportsUpstreamRecentlyPlayed, manualListening, systemListeningBehavior } = data; if(ambiguousTypes.includes(type)) { header = `${display} (Source)`; } let startText = ''; if(canPoll) { startText = status === 'Polling' ? 'Restart' : 'Start' } else { startText = status === 'Running' ? 'Reinit' : 'Init' } const ml = useMemo(() => { if(listenResult.status !== 'fulfilled' || listenResult.data === undefined) { return manualListening; } return (listenResult.data as any).listening; }, [manualListening, listenResult]); const manualListenElement = type !== 'azuracast' && type !== 'icecast' ? null : ( Monitoring :
tryListen(name, type, ml)} className="capitalize underline cursor-pointer inline mr-1 ml-1"> {ml !== undefined ? (ml ? 'Yes' : 'No') : null} {ml === undefined ? System {systemListeningBehavior ? '(Yes)' : '(No)'} : null}
{/* {ml !== undefined ?
tryListen(name, type, undefined)} className="capitalize underline cursor-pointer inline">Clear
: null} */}
); const startSourceElement = (
tryStart(name, type)} className="capitalize underline cursor-pointer inline mr-1">{startText}
(
tryStart(name, type, true)} className="capitalize underline cursor-pointer inline">Force
)
); if(manualListenElement !== null) { subtitleElement = {manualListenElement} | {startSourceElement} } else { subtitleElement = startSourceElement; } const platformIds = Object.keys(players); const discovered = (!hasAuth || authed) ? Tracks Discovered : Tracks Discovered; let upstreamRecent = null; if(supportsUpstreamRecentlyPlayed && (!hasAuth || authed)) { upstreamRecent =
See Recent from Source API
; } // TODO links body = (
{platformIds.map(x => )}
{discovered}: {tracksDiscovered}
{upstreamRecent} {canPoll && hasAuthInteraction ? (Re)authenticate : null} {type === 'ytmusic' && 'userCode' in data ?
Code: {data.userCode as string}
: null}
); } return ( {body} ); } const simpleSelectors = sourceAdapter.getSelectors(); const mapStateToProps = (state: RootState, props) => ({ data: simpleSelectors.selectById(state.sources, props.id) }); const connector = connect(mapStateToProps); type PropsFromRedux = ConnectedProps export default connector(SourceStatusCard);