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:
@ -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>
|
||||
);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -678,6 +678,10 @@ footer {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&--inline {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h1 &,
|
||||
h2 & {
|
||||
line-height: 1.4em;
|
||||
|
||||
Reference in New Issue
Block a user