import React, { useState, useEffect } from 'react'; import { connect, useSelector, useDispatch } from 'react-redux'; import { bindActionCreators } from 'redux'; import { find, groupBy, map } from 'lodash'; import VolumeControl from './VolumeControl'; import MuteControl from './MuteControl'; import Icon from '../Icon'; import Thumbnail from '../Thumbnail'; import LinksSentence from '../LinksSentence'; import * as coreActions from '../../services/core/actions'; import * as mopidyActions from '../../services/mopidy/actions'; import * as pusherActions from '../../services/pusher/actions'; import * as snapcastActions from '../../services/snapcast/actions'; import { sortItems, indexToArray } from '../../util/arrays'; import { formatImages, digestMopidyImages } from '../../util/format'; import { titleCase } from '../../util/helpers'; import { I18n } from '../../locale'; const Header = ({ stream, server }) => { const dispatch = useDispatch(); const current_server = useSelector((state) => state.mopidy.current_server); const { id, meta, status, } = stream || {}; const canBeSelected = server?.id && server.id !== current_server; const onClick = () => { if (!canBeSelected) return; dispatch(mopidyActions.setCurrentServer(server)); } const images = meta?.images ? formatImages(digestMopidyImages(server, meta.images)) : null; console.debug(images, meta.images) return (
{server?.name || id} {status === 'playing' && }
{meta && (
  • {meta?.name}
)}
); } const Clients = ({ clients }) => { const dispatch = useDispatch(); return (
{clients.map((client) => { const { id, name, mute, volume, } = client; return (
{titleCase(name)}
dispatch(snapcastActions.setClientMute(id, value))} /> dispatch(snapcastActions.setClientVolume(id, value)) } />
); })}
); } const Group = ({ item: { stream, server, clients, }, }) => { return (
); } const Outputs = () => { const groups = indexToArray(useSelector((state) => state.snapcast.groups || {})); const clients = useSelector((state) => state.snapcast.clients || {}); const streams = useSelector((state) => state.snapcast.streams || {}); const servers = indexToArray(useSelector((state) => state.mopidy.servers || {})); const groupsByStream = groupBy(groups, 'stream_id'); return ( <> {map(groupsByStream, (groups, stream_id) => { const clients_ids = groups.reduce((acc, curr) => [...acc, ...curr.clients_ids], []); const group = { stream: find(streams, (s) => s.id === stream_id), server: find(servers, (s) => s.snapcast_stream === stream_id), clients: clients_ids.map((id) => clients[id]).filter((c) => c.connected), }; return ; })} ); } const Commands = () => { const dispatch = useDispatch(); const commandsObj = useSelector((state) => state.pusher.commands || {}); if (!commandsObj) return null; let items = indexToArray(commandsObj); if (items.length <= 0) return null; items = sortItems(items, 'sort_order'); return (
{ items.map((command) => (
dispatch(pusherActions.runCommand(command.id))} >
)) }
); }; const OutputControl = ({ force_expanded, pusher_commands, snapcast_enabled, }) => { const [expanded, setExpanded] = useState(false); const handleClick = (e) => { if (!force_expanded && $(e.target).closest('.output-control').length <= 0) { setExpanded(false); } }; useEffect(() => { if (force_expanded && !expanded) { setExpanded(true); } }, [force_expanded]); useEffect(() => { if (expanded) { window.addEventListener('click', handleClick, false); } else { window.removeEventListener('click', handleClick, false); } }, [expanded]); // No customisable outputs if (!snapcast_enabled && !pusher_commands) { return ( ); } if (expanded) { const outputs = ; const commands = ; return ( {!outputs && !commands ? (

) : (
{commands} {outputs}
)}
); } return ( ); } const mapStateToProps = (state) => ({ pusher_connected: state.pusher.connected, snapcast_enabled: (state.pusher.config ? state.pusher.config.snapcast_enabled : null), }); const mapDispatchToProps = (dispatch) => ({ coreActions: bindActionCreators(coreActions, dispatch), snapcastActions: bindActionCreators(snapcastActions, dispatch), pusherActions: bindActionCreators(pusherActions, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(OutputControl);