Files
Iris/src/js/components/Snapcast.js

337 lines
10 KiB
JavaScript
Raw Normal View History

import React from 'react';
2018-09-24 21:37:59 +12:00
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Link from './Link';
2018-09-24 21:37:59 +12:00
import VolumeControl from './Fields/VolumeControl';
import MuteControl from './Fields/MuteControl';
2018-09-24 21:37:59 +12:00
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){
2018-03-23 16:54:56 +13:00
super(props);
2018-09-24 21:37:59 +12:00
this.state = {
clients_expanded: []
}
}
2018-03-21 21:40:47 +13:00
componentDidMount(){
2018-03-26 10:36:13 +13:00
if (this.props.pusher_connected && this.props.snapcast_enabled){
this.props.snapcastActions.getServer();
2018-03-21 21:40:47 +13:00
}
}
componentWillReceiveProps(newProps){
2018-03-26 10:36:13 +13:00
// Just connected
if (newProps.snapcast_enabled && !this.props.pusher_connected && newProps.pusher_connected){
this.props.snapcastActions.getServer();
2018-04-19 06:20:54 +12:00
// 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();
2018-03-21 21:40:47 +13:00
}
}
2018-09-24 21:37:59 +12:00
toggleClientExpanded(client_id){
var clients_expanded = this.state.clients_expanded;
var index = clients_expanded.indexOf(client_id);
if (index >= 0){
clients_expanded.splice(index, 1);
} else {
clients_expanded.push(client_id);
}
this.setState({clients_expanded: 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.clients;
}
if (!clients || clients.length <= 0){
return (
<div className="text mid_grey-text">
No clients
</div>
);
}
return (
2018-09-24 16:10:42 +12:00
<div className="list snapcast__clients">
{
clients.map(client => {
var class_name = "list__item list__item--no-interaction snapcast__client";
2018-09-24 21:37:59 +12:00
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}>
2018-09-25 17:00:04 +12:00
<div className="snapcast__client__header" onClick={e => this.toggleClientExpanded(client.id)}>
{client.name}
2018-09-25 17:00:04 +12:00
<div className="snapcast__client__header__icons">
{!client.connected ? <Icon name="power_off" className="disconnected" /> : null}
<Icon name="expand_less" />
</div>
2018-09-24 21:37:59 +12:00
</div>
2018-09-25 17:00:04 +12:00
<div className="snapcast__client__details">
<label className="field">
2018-09-25 17:00:04 +12:00
<div className="name">
Name
</div>
<div className="input">
<TextField
onChange={value => this.props.snapcastActions.setClientName(client.id, value)}
value={client.name}
2018-09-25 17:00:04 +12:00
/>
</div>
</label>
<label className="field dropdown">
2018-09-25 17:00:04 +12:00
<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 => {
return (
<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>
2018-09-25 17:00:04 +12:00
<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)}
/>
2018-09-25 17:00:04 +12:00
<VolumeControl
className="snapcast__client__volume-control"
volume={client.volume}
2018-09-25 17:00:04 +12:00
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}
2018-09-25 17:00:04 +12:00
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)}
2018-09-25 17:00:04 +12:00
/>
</div>
</div>
2018-09-24 21:37:59 +12:00
</div>
</div>
2018-09-24 21:37:59 +12:00
);
} else {
return (
2018-09-25 17:00:04 +12:00
<div className={class_name+" snapcast__client--collapsed"} key={client.id}>
<div className="snapcast__client__header" onClick={e => this.toggleClientExpanded(client.id)}>
{client.name}
2018-09-25 17:00:04 +12:00
<div className="snapcast__client__header__icons">
{!client.connected ? <Icon name="power_off" className="disconnected" /> : null}
<Icon name="expand_more" />
</div>
2018-09-24 21:37:59 +12:00
</div>
</div>
2018-09-24 21:37:59 +12:00
);
}
})
}
</div>
);
}
render(){
if (!this.props.snapcast_enabled){
return (
2018-09-18 16:59:10 +12:00
<p className="message warning">To enable Snapcast, edit your <code>mopidy.conf</code> file</p>
)
}
2018-04-29 21:10:57 +12:00
var streams = [];
for (var id in this.props.streams){
if (this.props.streams.hasOwnProperty(id)){
streams.push(this.props.streams[id]);
2018-04-29 21:10:57 +12:00
}
}
2018-03-22 16:59:04 +13:00
var 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>
2018-09-24 16:10:42 +12:00
<div className="snapcast__groups">
{
groups.map(group => {
2018-05-02 16:14:53 +12:00
group = helpers.collate(group, {clients: this.props.clients});
// Average our clients' volume for an overall group volume
var group_volume = 0;
for (var i = 0; i < group.clients.length; i++){
var client = group.clients[i];
group_volume += client.volume;
}
group_volume = group_volume / group.clients.length;
2018-05-02 16:14:53 +12:00
return (
2018-09-24 16:10:42 +12:00
<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>
2018-04-30 20:34:15 +12:00
</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 => {
return (
<option value={stream.id} key={stream.id}>
{stream.id} ({stream.status})
</option>
);
})
}
</select>
</div>
2018-05-01 21:03:05 +12:00
</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
2018-09-24 16:10:42 +12:00
className="snapcast__group__volume-control"
volume={group_volume}
onVolumeChange={(percent, old_percent) => this.props.snapcastActions.setGroupVolume(group.id, percent, old_percent)}
/>
</div>
2018-04-18 17:01:57 +12:00
</div>
<div className="field">
<div className="name">
Clients
</div>
<div className="input">
{this.renderClientsList(group, groups)}
</div>
2018-04-18 17:01:57 +12:00
</div>
2018-03-23 16:54:56 +13:00
</div>
);
})
}
</div>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
2018-03-26 10:36:13 +13:00
snapcast_enabled: state.pusher.config.snapcast_enabled,
2018-03-21 21:40:47 +13:00
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),
2018-10-09 21:39:38 +13:00
clients: (state.snapcast.clients ? state.snapcast.clients : null)
}
}
const mapDispatchToProps = (dispatch) => {
return {
coreActions: bindActionCreators(coreActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
snapcastActions: bindActionCreators(snapcastActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Snapcast)