Files
Iris/src/js/components/Snapcast.js
James Barnsley e4d53a2d35 Auto eslint fix
2019-09-05 15:36:11 +12:00

342 lines
10 KiB
JavaScript
Executable File

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Link from './Link';
import VolumeControl from './Fields/VolumeControl';
import MuteControl from './Fields/MuteControl';
import LatencyControl from './Fields/LatencyControl';
import TextField from './Fields/TextField';
import DropdownField from './Fields/DropdownField';
import Icon from './Icon';
import * as helpers from '../helpers';
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
import * as pusherActions from '../services/pusher/actions';
import * as snapcastActions from '../services/snapcast/actions';
class Snapcast extends React.Component {
constructor(props) {
super(props);
this.state = {
clients_expanded: [],
};
}
componentDidMount() {
if (this.props.pusher_connected && this.props.snapcast_enabled) {
this.props.snapcastActions.getServer();
}
}
componentWillReceiveProps(newProps) {
// Just connected
if (newProps.snapcast_enabled && !this.props.pusher_connected && newProps.pusher_connected) {
this.props.snapcastActions.getServer();
// Just enabled
// This is the more probable scenario as we don't know if we're enabled until pusher connects
// and then gets the config from the server
} else if (!this.props.snapcast_enabled && newProps.snapcast_enabled && newProps.pusher_connected) {
this.props.snapcastActions.getServer();
}
}
toggleClientExpanded(client_id) {
const { clients_expanded } = this.state;
const index = clients_expanded.indexOf(client_id);
if (index >= 0) {
clients_expanded.splice(index, 1);
} else {
clients_expanded.push(client_id);
}
this.setState({ clients_expanded });
}
renderClientsList(group, groups) {
if (!this.props.show_disconnected_clients && group.clients) {
var clients = helpers.applyFilter('connected', true, group.clients);
} else {
var { clients } = group;
}
if (!clients || clients.length <= 0) {
return (
<div className="text mid_grey-text">
No clients
</div>
);
}
return (
<div className="list snapcast__clients">
{
clients.map((client) => {
let class_name = 'list__item list__item--no-interaction snapcast__client';
if (client.connected) {
class_name += ' snapcast__client--connected';
} else {
class_name += ' snapcast__client--disconnected';
}
if (this.state.clients_expanded.includes(client.id)) {
return (
<div className={`${class_name} snapcast__client--expanded`} key={client.id}>
<div className="snapcast__client__header" onClick={(e) => this.toggleClientExpanded(client.id)}>
{client.name}
<div className="snapcast__client__header__icons">
{!client.connected ? <Icon name="power_off" className="disconnected" /> : null}
<Icon name="expand_less" />
</div>
</div>
<div className="snapcast__client__details">
<label className="field">
<div className="name">
Name
</div>
<div className="input">
<TextField
onChange={(value) => this.props.snapcastActions.setClientName(client.id, value)}
value={client.name}
/>
</div>
</label>
<label className="field dropdown">
<div className="name">
Group
</div>
<div className="input">
<select onChange={(e) => this.props.snapcastActions.setClientGroup(client.id, e.target.value)} value={group.id}>
{
groups.map((group) => (
<option value={group.id} key={group.id}>
{group.name ? group.name : `Group ${group.id.substring(0, 3)}`}
</option>
))
}
<option value={group.id}>
New group
</option>
</select>
</div>
</label>
<div className="snapcast__client__volume field">
<div className="name">
Volume
</div>
<div className="input">
<MuteControl
className="snapcast__client__mute-control"
mute={client.mute}
onMuteChange={(mute) => this.props.snapcastActions.setClientMute(client.id, mute)}
/>
<VolumeControl
className="snapcast__client__volume-control"
volume={client.volume}
onVolumeChange={(percent) => this.props.snapcastActions.setClientVolume(client.id, percent)}
/>
</div>
</div>
<div className="snapcast__client__latency field">
<div className="name">
Latency
</div>
<div className="input">
<LatencyControl
max="150"
value={client.latency}
onChange={(value) => this.props.snapcastActions.setClientLatency(client.id, parseInt(value))}
/>
<TextField
className="tiny"
type="number"
onChange={(value) => this.props.snapcastActions.setClientLatency(client.id, parseInt(value))}
value={String(client.latency)}
/>
</div>
</div>
</div>
</div>
);
}
return (
<div className={`${class_name} snapcast__client--collapsed`} key={client.id}>
<div className="snapcast__client__header" onClick={(e) => this.toggleClientExpanded(client.id)}>
{client.name}
<div className="snapcast__client__header__icons">
{!client.connected ? <Icon name="power_off" className="disconnected" /> : null}
<Icon name="expand_more" />
</div>
</div>
</div>
);
})
}
</div>
);
}
render() {
if (!this.props.snapcast_enabled) {
return (
<p className="message warning">
To enable Snapcast, edit your
<code>mopidy.conf</code>
{' '}
file
</p>
);
}
const streams = [];
for (var id in this.props.streams) {
if (this.props.streams.hasOwnProperty(id)) {
streams.push(this.props.streams[id]);
}
}
const groups = [];
for (var id in this.props.groups) {
if (this.props.groups.hasOwnProperty(id)) {
groups.push(this.props.groups[id]);
}
}
return (
<div className="snapcast">
<div className="field checkbox">
<div className="name">
Display
</div>
<div className="input">
<button
className="button button--default button--small"
onClick={(e) => this.props.snapcastActions.getServer()}
>
Refresh
</button>
<label>
<input
type="checkbox"
name="show_disconnected_clients"
checked={this.props.show_disconnected_clients}
onChange={(e) => this.props.uiActions.set({ snapcast_show_disconnected_clients: !this.props.show_disconnected_clients })}
/>
<span className="label">
Show disconnected clients
</span>
</label>
</div>
</div>
<div className="snapcast__groups">
{
groups.map((group) => {
group = helpers.collate(group, { clients: this.props.clients });
// Average our clients' volume for an overall group volume
let group_volume = 0;
for (let i = 0; i < group.clients.length; i++) {
const client = group.clients[i];
group_volume += client.volume;
}
group_volume /= group.clients.length;
return (
<div className="snapcast__group" key={group.id}>
<div className="field">
<div className="name">
Name
</div>
<div className="input">
<div className="text">
{group.name}
{' '}
&nbsp;
<span className="mid_grey-text">
(
{group.id}
)
</span>
</div>
</div>
</div>
<div className="field dropdown">
<div className="name">
Stream
</div>
<div className="input">
<select onChange={(e) => this.props.snapcastActions.setGroupStream(group.id, e.target.value)} value={group.stream_id}>
{
streams.map((stream) => (
<option value={stream.id} key={stream.id}>
{stream.id}
{' '}
(
{stream.status}
)
</option>
))
}
</select>
</div>
</div>
<div className="field">
<div className="name">
Volume
</div>
<div className="input">
<MuteControl
className="snapcast__group__mute-control"
mute={group.muted}
onMuteChange={(mute) => this.props.snapcastActions.setGroupMute(group.id, mute)}
/>
<VolumeControl
className="snapcast__group__volume-control"
volume={group_volume}
onVolumeChange={(percent, old_percent) => this.props.snapcastActions.setGroupVolume(group.id, percent, old_percent)}
/>
</div>
</div>
<div className="field">
<div className="name">
Clients
</div>
<div className="input">
{this.renderClientsList(group, groups)}
</div>
</div>
</div>
);
})
}
</div>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => ({
snapcast_enabled: state.pusher.config.snapcast_enabled,
pusher_connected: state.pusher.connected,
show_disconnected_clients: (state.ui.snapcast_show_disconnected_clients !== undefined ? state.ui.snapcast_show_disconnected_clients : false),
streams: (state.snapcast.streams ? state.snapcast.streams : null),
groups: (state.snapcast.groups ? state.snapcast.groups : null),
clients: (state.snapcast.clients ? state.snapcast.clients : null),
});
const mapDispatchToProps = (dispatch) => ({
coreActions: bindActionCreators(coreActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
snapcastActions: bindActionCreators(snapcastActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Snapcast);