Mobile-friendly outputs control
This commit is contained in:
@ -26,12 +26,20 @@ class OutputControl extends React.Component{
|
||||
}
|
||||
|
||||
handleClick(e){
|
||||
if ($(e.target).closest('.output-control').length <= 0){
|
||||
if (!this.props.force_expanded && $(e.target).closest('.output-control').length <= 0){
|
||||
this.setExpanded(false);
|
||||
}
|
||||
}
|
||||
|
||||
setExpanded(expanded = !this.state.expanded){
|
||||
componentWillReceiveProps(nextProps){
|
||||
|
||||
// When force expanded triggered
|
||||
if (!this.props.force_expanded && nextProps.force_expanded){
|
||||
this.setExpanded(true, nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
setExpanded(expanded = !this.state.expanded, props = this.props){
|
||||
if (expanded){
|
||||
this.setState({expanded: expanded});
|
||||
window.addEventListener("click", this.handleClick, false);
|
||||
@ -39,8 +47,8 @@ class OutputControl extends React.Component{
|
||||
// Re-check our snapcast clients
|
||||
// TODO: Once we have push events, remove this as it'll (marginally)
|
||||
// slow down the reveal/render
|
||||
if (this.props.pusher_connected && this.props.snapcast_enabled){
|
||||
this.props.pusherActions.getSnapcast();
|
||||
if (props.pusher_connected && props.snapcast_enabled){
|
||||
this.props.snapcastActions.getServer();
|
||||
}
|
||||
} else {
|
||||
this.setState({expanded: expanded});
|
||||
@ -131,7 +139,7 @@ class OutputControl extends React.Component{
|
||||
|
||||
if (!local_streaming && !snapcast_clients){
|
||||
return (
|
||||
<div className="output-control__items">
|
||||
<div className="output-control__items output-control__items--no-results">
|
||||
<p className="no-results">No outputs</p>
|
||||
</div>
|
||||
);
|
||||
@ -146,7 +154,7 @@ class OutputControl extends React.Component{
|
||||
}
|
||||
|
||||
render(){
|
||||
if (this.state.expanded || this.props.force_expanded){
|
||||
if (this.state.expanded){
|
||||
return (
|
||||
<span className="output-control">
|
||||
<a className="control speakers active" onClick={e => this.setExpanded()}><Icon name="speaker" /></a>
|
||||
|
||||
@ -624,365 +624,6 @@ const PusherMiddleware = (function(){
|
||||
break
|
||||
|
||||
|
||||
/**
|
||||
* Snapcast actions
|
||||
**/
|
||||
case 'PUSHER_GET_SNAPCAST':
|
||||
request(store, 'snapcast_instruct', action.data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch(pusherActions.snapcastServerLoaded(response.server));
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not get Snapcast server',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SNAPCAST_SERVER_LOADED':
|
||||
var groups = {};
|
||||
var clients = {};
|
||||
var streams = {};
|
||||
|
||||
// Loop all the groups
|
||||
for (var i = 0; i < action.server.groups.length; i++){
|
||||
var group = action.server.groups[i];
|
||||
var clients_ids = [];
|
||||
|
||||
// And now this groups' clients
|
||||
for (var j = 0; j < group.clients.length; j++){
|
||||
var client = group.clients[j];
|
||||
clients[client.id] = client;
|
||||
clients_ids.push(client.id);
|
||||
}
|
||||
|
||||
groups[group.id] = {
|
||||
id: group.id,
|
||||
muted: group.muted,
|
||||
name: group.name,
|
||||
stream_id: group.stream_id,
|
||||
clients_ids: clients_ids
|
||||
}
|
||||
}
|
||||
|
||||
// Loop all the streams
|
||||
for (var i = 0; i < action.server.streams.length; i++){
|
||||
var stream = action.server.streams[i];
|
||||
streams[stream.id] = stream;
|
||||
}
|
||||
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST',
|
||||
snapcast_clients: clients,
|
||||
snapcast_groups: groups,
|
||||
snapcast_streams: streams
|
||||
});
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_NAME':
|
||||
var client = store.getState().pusher.snapcast_clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetName',
|
||||
params: {
|
||||
id: action.id,
|
||||
name: action.name
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
name: response.name
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_MUTE':
|
||||
var client = store.getState().pusher.snapcast_clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetVolume',
|
||||
params: {
|
||||
id: action.id,
|
||||
volume: {
|
||||
muted: action.mute,
|
||||
percent: client.config.volume.percent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
volume: response.volume
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_VOLUME':
|
||||
var client = store.getState().pusher.snapcast_clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetVolume',
|
||||
params: {
|
||||
id: action.id,
|
||||
volume: {
|
||||
muted: client.config.volume.muted,
|
||||
percent: action.percent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
volume: response.volume
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_LATENCY':
|
||||
var client = store.getState().pusher.snapcast_clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetLatency',
|
||||
params: {
|
||||
id: action.id,
|
||||
latency: action.latency
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
latency: response.latency
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_GROUP':
|
||||
|
||||
var group = store.getState().pusher.snapcast_groups[action.group_id];
|
||||
var clients_ids = group.clients_ids;
|
||||
var clients_ids_index = clients_ids.indexOf(action.id);
|
||||
|
||||
// Not in group (yet), so add it
|
||||
if (clients_ids_index <= -1){
|
||||
clients_ids.push(action.id);
|
||||
|
||||
// Already there, so remove it
|
||||
} else {
|
||||
clients_ids.splice(clients_ids_index, 1);
|
||||
}
|
||||
|
||||
var data = {
|
||||
method: 'Group.SetClients',
|
||||
params: {
|
||||
id: action.group_id,
|
||||
clients: clients_ids
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch(pusherActions.snapcastServerLoaded(response.server));
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_DELETE_SNAPCAST_CLIENT':
|
||||
var data = {
|
||||
method: 'Server.DeleteClient',
|
||||
params: {
|
||||
id: action.id
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_REMOVED',
|
||||
key: action.data.params.id
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_GROUP_STREAM':
|
||||
var group = store.getState().pusher.snapcast_groups[action.id];
|
||||
var data = {
|
||||
method: 'Group.SetStream',
|
||||
params: {
|
||||
id: action.id,
|
||||
stream_id: action.stream_id
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_GROUP_UPDATED',
|
||||
key: action.id,
|
||||
group: {
|
||||
stream_id: action.stream_id
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not change stream',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_GROUP_MUTE':
|
||||
var group = store.getState().pusher.snapcast_groups[action.id];
|
||||
var data = {
|
||||
method: 'Group.SetMute',
|
||||
params: {
|
||||
id: action.id,
|
||||
mute: action.mute
|
||||
}
|
||||
}
|
||||
|
||||
request(store, 'snapcast_instruct', data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_GROUP_UPDATED',
|
||||
key: action.id,
|
||||
group: {
|
||||
muted: response.mute
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not toggle mute',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_GROUP_VOLUME':
|
||||
var clients_to_update = [];
|
||||
var group = store.getState().pusher.snapcast_groups[action.id];
|
||||
var change = action.percent - action.old_percent;
|
||||
|
||||
for (var i = 0; i < group.clients_ids.length; i++){
|
||||
|
||||
// Apply the change proportionately to each client
|
||||
var client = store.getState().pusher.snapcast_clients[group.clients_ids[i]];
|
||||
var current_percent = client.config.volume.percent;
|
||||
var new_percent = current_percent + change;
|
||||
|
||||
// Only change if the client is within min/max limits
|
||||
if ((change > 0 && current_percent < 100) || (change < 0 && current_percent > 0)){
|
||||
clients_to_update.push({
|
||||
id: client.id,
|
||||
percent: new_percent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Loop our required changes, and post each to Snapcast
|
||||
for (var i = 0; i < clients_to_update.length; i++){
|
||||
var update = clients_to_update[i];
|
||||
var percent = update.percent + ((group.clients_ids.length - clients_to_update.length) * change);
|
||||
|
||||
// Make sure we're not creating an impossible percent
|
||||
if (percent < 0){
|
||||
percent = 0;
|
||||
} else if (percent > 100){
|
||||
percent = 100;
|
||||
}
|
||||
|
||||
store.dispatch(pusherActions.setSnapcastClientVolume(update.id, percent));
|
||||
}
|
||||
|
||||
// This action is irrelevant to us, pass it on to the next middleware
|
||||
default:
|
||||
return next(action);
|
||||
|
||||
@ -119,12 +119,62 @@
|
||||
}
|
||||
|
||||
@include responsive($bp_medium){
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: 0px;
|
||||
bottom: auto;
|
||||
width: 100vw;
|
||||
|
||||
& > .control {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__items {
|
||||
width: 86vw;
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: auto;
|
||||
padding: 4px 0 0;
|
||||
background: lighten($dark_grey, 17.5%);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&:after {
|
||||
right: 9px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--no-results {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
@include clearfix();
|
||||
padding: 10px 18px 6px;
|
||||
|
||||
&:not(:first-child){
|
||||
border-color: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: $white;
|
||||
font-weight: 400;
|
||||
width: 30%;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
&__controls {
|
||||
width: 70%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&__mute.control {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,44 +264,31 @@
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
height: 150px;
|
||||
height: 135px;
|
||||
|
||||
section {
|
||||
&.settings {
|
||||
display: block;
|
||||
right: 0;
|
||||
bottom: 100px;
|
||||
bottom: 90px;
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
&.volume {
|
||||
width: auto;
|
||||
display: block;
|
||||
right: 110px;
|
||||
left: 22px;
|
||||
bottom: 100px;
|
||||
right: 100px;
|
||||
left: 10px;
|
||||
bottom: 88px;
|
||||
}
|
||||
|
||||
&.progress {
|
||||
display: block;
|
||||
bottom: 55px;
|
||||
left: -10px;
|
||||
right: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.output-control {
|
||||
position: absolute;
|
||||
bottom: 150px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
& > .control {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__items {
|
||||
width: 100%;
|
||||
left: -15px;
|
||||
right: -15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +98,7 @@ footer {
|
||||
main {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
z-index: 1;
|
||||
|
||||
.content-wrapper {
|
||||
padding: 40px 40px 80px;
|
||||
|
||||
Reference in New Issue
Block a user