Relocating stream checkbox to Outputs panel

- Making it easier to switch current device streaming on/off
- Refactor `Stream` to functional component
This commit is contained in:
James Barnsley
2023-08-12 10:06:07 +12:00
parent 45cd06b2e1
commit 41fcfa9556
6 changed files with 78 additions and 98 deletions

View File

@ -8,7 +8,13 @@ import Thumbnail from '../Thumbnail';
import LinksSentence from '../LinksSentence';
import DropdownField from './DropdownField';
import * as pusherActions from '../../services/pusher/actions';
import * as snapcastActions from '../../services/snapcast/actions';
import {
setGroupStream,
setClientMute,
setClientVolume,
setStreamingEnabled,
controlStream,
} from '../../services/snapcast/actions';
import { sortItems, indexToArray } from '../../util/arrays';
import { titleCase } from '../../util/helpers';
import { I18n, i18n } from '../../locale';
@ -38,10 +44,10 @@ const Header = ({
let onClick = null;
switch (playbackStatus) {
case 'playing':
if (canPause) onClick = () => dispatch(snapcastActions.controlStream(id, 'pause'));
if (canPause) onClick = () => dispatch(controlStream(id, 'pause'));
break;
default:
if (canPlay) onClick = () => dispatch(snapcastActions.controlStream(id, 'play'));
if (canPlay) onClick = () => dispatch(controlStream(id, 'play'));
break;
}
@ -88,6 +94,7 @@ const Header = ({
};
const Group = ({
setExpanded,
group: {
id: groupId,
name: groupName,
@ -111,6 +118,7 @@ const Group = ({
className="text"
to={`/settings/services/snapcast/${groupId}`}
scrollTo="#services-snapcast-groups"
onClick={() => setExpanded(false)}
>
{titleCase(groupName)}
</Link>
@ -121,7 +129,7 @@ const Group = ({
options={allStreams.map((s) => ({ value: s.id, label: titleCase(s.id) }))}
noLabel
handleChange={
(value) => dispatch(snapcastActions.setGroupStream(groupId, value))
(value) => dispatch(setGroupStream(groupId, value))
}
/>
</h5>
@ -143,14 +151,14 @@ const Group = ({
noTooltip
mute={mute}
onMuteChange={
(value) => dispatch(snapcastActions.setClientMute(clientId, value))
(value) => dispatch(setClientMute(clientId, value))
}
/>
<VolumeControl
volume={volume}
mute={mute}
onVolumeChange={
(value) => dispatch(snapcastActions.setClientVolume(clientId, value))
(value) => dispatch(setClientVolume(clientId, value))
}
/>
</div>
@ -162,11 +170,13 @@ const Group = ({
);
};
const Outputs = () => {
const Outputs = ({ setExpanded }) => {
const dispatch = useDispatch();
const allGroups = indexToArray(useSelector((state) => state.snapcast.groups || {}));
const allStreams = useSelector((state) => state.snapcast.streams || {});
const allServers = indexToArray(useSelector((state) => state.mopidy.servers || {}));
const groupsByStream = groupBy(allGroups, 'stream_id');
const { streaming_enabled } = useSelector((state) => state?.snapcast || {});
return (
<ErrorBoundary>
@ -180,10 +190,29 @@ const Outputs = () => {
return (
<div className="output-control__stream" key={`stream_${id}`}>
<Header stream={stream} />
{groups.map((group) => <Group group={group} key={`group_${group.id}`} />)}
{
groups.map(
(group) => (
<Group setExpanded={setExpanded} group={group} key={`group_${group.id}`} />
)
)
}
</div>
);
})}
<div className="field checkbox" style={{ paddingLeft: 12 }}>
<label>
<input
type="checkbox"
name="streaming_enabled"
checked={streaming_enabled}
onChange={() => dispatch(setStreamingEnabled(!streaming_enabled))}
/>
<span className="label">
<I18n path="snapcast.stream_on_this_device" />
</span>
</label>
</div>
</ErrorBoundary>
);
}
@ -241,7 +270,7 @@ const OutputControl = ({ force_expanded }) => {
</button>
<div className="output-control__inner">
{!isEmpty(commands) && <Commands commands={commands} />}
{snapcastEnabled && <Outputs />}
{snapcastEnabled && <Outputs setExpanded={setExpanded} />}
</div>
</span>
);

View File

@ -41,18 +41,6 @@ const Snapcast = (props) => {
<I18n path="snapcast.enabled" />
</span>
</label>
<label>
<input
type="checkbox"
name="streaming_enabled"
checked={streaming_enabled}
disabled={!enabled}
onChange={() => actions.setStreamingEnabled(!streaming_enabled)}
/>
<span className="label">
<I18n path="snapcast.streaming_enabled" />
</span>
</label>
<label>
<input
type="checkbox"

View File

@ -1,85 +1,43 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as coreActions from '../services/core/actions';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { SnapStream } from './SnapStream.tsx';
class Stream extends React.Component {
constructor(props) {
super(props);
if (props.enabled && props.streaming_enabled) {
this.start();
}
}
start = () => {
const {
host,
port,
ssl,
} = this.props;
if (this.snapstream) {
this.snapstream.play();
} else {
const baseUrl = `${ssl ? 'wss' : 'ws'}://${host}:${port}`;
this.snapstream = new SnapStream(baseUrl);
}
}
stop = () => {
if (this.snapstream) {
this.snapstream.stop();
this.snapstream = null;
}
}
componentDidUpdate = ({
streaming_enabled: prevStreamingEnabled,
}) => {
const {
enabled,
streaming_enabled,
} = this.props;
if (!prevStreamingEnabled && enabled && streaming_enabled) {
this.start();
}
if (!enabled || !streaming_enabled) {
this.stop();
}
}
render = () => null;
}
const mapStateToProps = (state) => {
const Stream = () => {
const [stream, setStream] = useState(null);
const {
snapcast: {
enabled,
streaming_enabled,
host,
port,
ssl,
},
pusher: {
username,
},
} = state;
return {
enabled,
streaming_enabled,
host,
port,
ssl,
username,
};
};
} = useSelector((state) => state?.snapcast || {});
const mapDispatchToProps = (dispatch) => ({
coreActions: bindActionCreators(coreActions, dispatch),
});
const start = () => {
if (stream) {
stream.play();
} else {
setStream(new SnapStream(`${ssl ? 'wss' : 'ws'}://${host}:${port}`));
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Stream);
const stop = () => {
if (stream) {
stream.stop();
setStream(null);
}
}
useEffect(
() => {
if (enabled && streaming_enabled) {
start();
} else {
stop();
}
}, [enabled, streaming_enabled],
);
return null;
}
export default Stream;

View File

@ -213,6 +213,7 @@ playback_controls:
snapcast:
enabled: Enabled
streaming_enabled: Streaming enabled
stream_on_this_device: Stream on this device
show_disconnected_clients: Show disconnected clients
no_connected_clients: No connected clients
host: Host

View File

@ -207,7 +207,7 @@ class Settings extends React.Component {
<I18n path="settings.services.title" />
<a name="services" />
</h4>
<Services />
<h4 className="underline">
@ -456,7 +456,7 @@ class Settings extends React.Component {
<a
target="_blank"
rel="noopener noreferrer"
className="flag flag--dark"
className="flag flag--dark flag--inline"
href={`https://github.com/jaedb/Iris/releases/tag/${pusher.version.latest}`}
>
<Icon name="cloud_download" className="blue-text" />

View File

@ -678,6 +678,10 @@ footer {
font-size: 12px;
}
&--inline {
vertical-align: middle;
}
h1 &,
h2 & {
line-height: 1.4em;