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
Something went wrong. Try again.
6.6 kB · 176 lines
TSX
at debugStartup
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177import 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 = <SkeletonParagraph/>;
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 : (<Fragment> <span>Monitoring <Tooltip message="test" style={{ display: 'inline-flex' }}> <FontAwesomeIcon color="white" icon={faQuestionCircle}/></Tooltip> : </span> <div onClick={() => tryListen(name, type, ml)} className="capitalize underline cursor-pointer inline mr-1 ml-1"> {ml !== undefined ? (ml ? 'Yes' : 'No') : null} {ml === undefined ? <span>System {systemListeningBehavior ? '(Yes)' : '(No)'}</span> : null} </div> {/* {ml !== undefined ? <div onClick={() => tryListen(name, type, undefined)} className="capitalize underline cursor-pointer inline">Clear </div> : null} */} </Fragment>);
const startSourceElement = (<Fragment> <div onClick={() => tryStart(name, type)} className="capitalize underline cursor-pointer inline mr-1">{startText} </div> (<div onClick={() => tryStart(name, type, true)} className="capitalize underline cursor-pointer inline">Force </div>) </Fragment>);
if(manualListenElement !== null) { subtitleElement = <Fragment>{manualListenElement} | {startSourceElement}</Fragment> } else { subtitleElement = startSourceElement; }
const platformIds = Object.keys(players);
const discovered = (!hasAuth || authed) ? <Link to={`/recent?type=${type}&name=${name}`}>Tracks Discovered</Link> : <span>Tracks Discovered</span>;
let upstreamRecent = null; if(supportsUpstreamRecentlyPlayed && (!hasAuth || authed)) { upstreamRecent = <div><Link to={`/recent?type=${type}&name=${name}&upstream=1`}>See Recent from Source API</Link></div>; }
// TODO links body = (<div className="statusCardBody"> {platformIds.map(x => <Player key={x} data={players[x]} sot={sot}/>)} <div>{discovered}: {tracksDiscovered}</div> {upstreamRecent} {canPoll && hasAuthInteraction ? <a target="_blank" href={`/api/source/auth?name=${name}&type=${type}`}>(Re)authenticate</a> : null} {type === 'ytmusic' && 'userCode' in data ? <div>Code: <strong>{data.userCode as string}</strong></div> : null} </div>); } return ( <StatusCardSkeleton loading={loading} title={header} subtitle={name} status={status} subtitleRight={subtitleElement} statusType={statusToStatusType(status)}> {body} </StatusCardSkeleton> );}
const simpleSelectors = sourceAdapter.getSelectors();
const mapStateToProps = (state: RootState, props) => ({ data: simpleSelectors.selectById(state.sources, props.id)});
const connector = connect(mapStateToProps);
type PropsFromRedux = ConnectedProps<typeof connector>
export default connector(SourceStatusCard);