Adding advanced server controls

This commit is contained in:
James Barnsley
2018-04-29 21:10:57 +12:00
parent 562004153c
commit 4b5d7d6237
13 changed files with 769 additions and 476 deletions

View File

@ -4291,118 +4291,102 @@ input[type="submit"] {
left: 0; left: 0;
right: 0; } } right: 0; } }
.snapcast .group.list { .snapcast .group:not(:first-child) {
padding-top: 20px; }
.snapcast .group .clients {
padding: 0 10px; } padding: 0 10px; }
.snapcast .group.list:not(:first-child) { .snapcast .group .clients .client {
padding-top: 15px; } border: 0;
.snapcast .group.list .list-item { padding: 0;
padding: 20px 0 0 0; } clear: both; }
.snapcast .group.list .list-item:not(.header) { .snapcast .group .clients .client::after {
border: 0; } content: '';
.snapcast .group.list .list-item > .name { clear: both;
display: block; }
.snapcast .group .clients .client:not(:first-child) {
padding-top: 8px; }
.snapcast .group .clients .client > .name {
width: 40%; width: 40%;
padding-bottom: 4px; padding-bottom: 4px;
position: relative; } position: relative;
.snapcast .group.list .list-item > .name .status { display: block;
box-sizing: border-box;
border: 0; }
.snapcast .group .clients .client > .name input {
width: 100%;
padding-right: 30px; }
.snapcast .group .clients .client > .name .tools {
position: absolute; position: absolute;
top: 0; top: -2px;
left: 0; right: 0;
z-index: 2; z-index: 2; }
padding: 12px; .snapcast .group .clients .client > .name .tools .disconnected {
cursor: default; } padding: 12px 0;
.snapcast .group.list .list-item > .name .status .fa { cursor: default;
font-size: 12px; } font-size: 12px; }
.snapcast .group.list .list-item > .name input { .snapcast .group .clients .client > .volume {
padding-left: 30px; }
.snapcast .group.list .list-item > .volume {
width: 28%; width: 28%;
padding-left: 2%; } padding-left: 2%; }
.snapcast .group.list .list-item > .latency { .snapcast .group .clients .client > .latency {
width: 28%; width: 28%;
padding-left: 2%; } padding-left: 2%; }
.snapcast .group.list .list-item.header { .snapcast .group .clients .client > .volume input,
padding-top: 0; .snapcast .group .clients .client > .latency input {
vertical-align: bottom; width: 20%; }
cursor: default; } .snapcast .group .clients .client .volume-control {
.snapcast .group.list .list-item.header .name { display: inline-block;
font-size: 14px; vertical-align: middle;
color: #FFFFFF; padding-top: 4px;
text-transform: none; width: 80%;
font-weight: 600; padding-right: 10px;
line-height: 14px; } box-sizing: border-box; }
.snapcast .group.list .list-item.client { .snapcast .group .clients .client .volume-control .mute-control {
padding: 20px 0 0 0; } display: inline-block;
.snapcast .group.list .list-item.client::after { float: left;
content: ''; padding: 0 10px;
clear: both;
display: block; }
.snapcast .group.list .list-item.client > .name {
display: block;
box-sizing: border-box;
border: 0; } border: 0; }
.snapcast .group.list .list-item.client > .name input { .snapcast .group .clients .client .volume-control .slider-wrapper {
width: 100%; } margin-left: 25px; }
.snapcast .group.list .list-item.client > .volume input, .snapcast .group .clients .client .volume-control.muted .slider .progress {
.snapcast .group.list .list-item.client > .latency input { background: #a3a19f; }
width: 20%; } .snapcast .group .clients .client .latency-control {
.snapcast .group.list .list-item.client .volume-control { display: inline-block;
display: inline-block; vertical-align: middle;
vertical-align: middle; padding-top: 4px;
padding-top: 4px; width: 80%;
width: 80%; padding-right: 10px;
padding-right: 10px; box-sizing: border-box; }
box-sizing: border-box; } .snapcast .group .clients .client .latency-control .zero {
.snapcast .group.list .list-item.client .volume-control .mute-control { position: absolute;
display: inline-block; top: -4px;
float: left; left: 50%;
padding: 0 10px; bottom: 0;
border: 0; } width: 1px;
.snapcast .group.list .list-item.client .volume-control .slider-wrapper { background: #383734;
margin-left: 40px; } z-index: 1; }
.snapcast .group.list .list-item.client .volume-control.muted .slider .progress { .snapcast .group .clients .client .latency-control .progress.positive {
background: #a3a19f; } border-top-left-radius: 0;
.snapcast .group.list .list-item.client .latency-control { border-bottom-left-radius: 0; }
display: inline-block; .snapcast .group .clients .client .latency-control .progress.negative {
vertical-align: middle; border-top-right-radius: 0;
padding-top: 4px; border-bottom-right-radius: 0; }
width: 80%;
padding-right: 10px;
box-sizing: border-box; }
.snapcast .group.list .list-item.client .latency-control .zero {
position: absolute;
top: -4px;
left: 50%;
bottom: 0;
width: 1px;
background: #383734;
z-index: 1; }
.snapcast .group.list .list-item.client .latency-control .progress.positive {
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.snapcast .group.list .list-item.client .latency-control .progress.negative {
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
@media (max-width: 800px) { @media (max-width: 800px) {
.snapcast .group.list .list-item { .snapcast .group .clients .client {
padding-left: 0 !important; padding-left: 0 !important;
padding-right: 0 !important; } padding-right: 0 !important; }
.snapcast .group.list .list-item.header { .snapcast .group .clients .client .col {
display: block; }
.snapcast .group.list .list-item.header .volume,
.snapcast .group.list .list-item.header .latency {
display: none !important; }
.snapcast .group.list .list-item .col {
float: none !important; float: none !important;
width: auto !important; width: auto !important;
padding-left: 0; padding-left: 0;
padding-right: 0; } padding-right: 0; }
.snapcast .group.list .list-item .col::after { .snapcast .group .clients .client .col::after {
content: ''; content: '';
clear: both; clear: both;
display: block; } display: block; }
.snapcast .group.list .list-item .col:not(.header) { .snapcast .group .clients .client .col:not(.header) {
line-height: 45px; } line-height: 45px; }
.snapcast .group.list .list-item .col.volume { .snapcast .group .clients .client .col.volume {
padding-bottom: 20px !important; } } padding-bottom: 20px !important; } }
aside { aside {
@ -4549,7 +4533,6 @@ aside {
position: relative; position: relative;
margin: 0 -10px -1px -10px; margin: 0 -10px -1px -10px;
padding: 14px 30px 13px 10px; padding: 14px 30px 13px 10px;
cursor: pointer;
border-radius: 3px; border-radius: 3px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05);
border-top: 1px solid rgba(255, 255, 255, 0.05); } border-top: 1px solid rgba(255, 255, 255, 0.05); }
@ -4604,91 +4587,95 @@ aside {
.list .list-item .uri-placeholder { .list .list-item .uri-placeholder {
word-break: break-all; } word-break: break-all; }
.list.track-list .list-item .col.name { .list.track-list .list-item {
width: 40%; } cursor: pointer; }
.list.track-list .list-item .col.name {
.list.track-list .list-item .col.artists, .list.track-list .list-item .col.album { width: 40%; }
width: 25%; } .list.track-list .list-item .col.artists, .list.track-list .list-item .col.album {
.list.track-list .list-item .col.duration {
width: 5%; }
:root .dragging .list.track-list .list-item.hover, .list.track-list .list-item.touch-drag-hover {
border-top: 3px solid #32b5f2;
margin-top: -3px;
background: transparent; }
.list.track-list .list-item.has-touch-drag-zone {
padding-left: 45px; }
.list.track-list .list-item.has-touch-drag-zone .drag-zone {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 34px;
font-size: 14px;
display: block;
color: #a3a19f; }
.list.track-list .list-item.has-touch-drag-zone .drag-zone .fa {
position: absolute;
top: 50%;
left: 10px;
margin-top: -6px;
pointer-events: none; }
.list.playlist-list .list-item .col.name {
width: 50%; }
.list.playlist-list .list-item .col.owner {
width: 20%; }
.list.playlist-list .list-item .col.source, .list.playlist-list .list-item .col.can_edit, .list.playlist-list .list-item .col.tracks_total, .list.playlist-list .list-item .col.popularity {
width: 10%; }
.list.album-list .list-item .col.name, .list.album-list .list-item .col.artists {
width: 30%; }
.list.album-list .list-item .col.added_at, .list.album-list .list-item .col.release_date {
width: 15%; }
.list.album-list .list-item .col.tracks_total {
width: 10%; }
.list.library-local-album-list .list-item .col.name, .list.library-local-album-list .list-item .col.artists {
width: 45%; }
.list.library-local-album-list .list-item .col.tracks_total {
width: 10%; }
.list.artist-list .list-item .col.name {
width: 70%; }
.list.artist-list .list-item .col.followers_total, .list.artist-list .list-item .col.popularity {
width: 15%; }
.list.queue-track-list .list-item {
padding-right: 60px; }
.list.queue-track-list .list-item .col.name, .list.queue-track-list .list-item .col.artists, .list.queue-track-list .list-item .col.album {
width: 25%; } width: 25%; }
.list.queue-track-list .list-item .col.added { .list.track-list .list-item .col.duration {
width: 5%; }
:root .dragging .list.track-list .list-item.hover, .list.track-list .list-item.touch-drag-hover {
border-top: 3px solid #32b5f2;
margin-top: -3px;
background: transparent; }
.list.track-list .list-item.has-touch-drag-zone {
padding-left: 45px; }
.list.track-list .list-item.has-touch-drag-zone .drag-zone {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 34px;
font-size: 14px;
display: block;
color: #a3a19f; }
.list.track-list .list-item.has-touch-drag-zone .drag-zone .fa {
position: absolute;
top: 50%;
left: 10px;
margin-top: -6px;
pointer-events: none; }
.list.playlist-list .list-item {
cursor: pointer; }
.list.playlist-list .list-item .col.name {
width: 50%; }
.list.playlist-list .list-item .col.owner {
width: 20%; } width: 20%; }
.list.queue-track-list .list-item .col.added .by { .list.playlist-list .list-item .col.source, .list.playlist-list .list-item .col.can_edit, .list.playlist-list .list-item .col.tracks_total, .list.playlist-list .list-item .col.popularity {
padding-right: 0.25em; } width: 10%; }
.list.queue-track-list .list-item .col.added .from:before {
content: '(';
display: inline-block; }
.list.queue-track-list .list-item .col.added .from:after {
content: ')';
display: inline-block; }
.list.queue-history-track-list .list-item .col.name { .list.album-list .list-item {
width: 60%; } cursor: pointer; }
.list.album-list .list-item .col.name, .list.album-list .list-item .col.artists {
width: 30%; }
.list.album-list .list-item .col.added_at, .list.album-list .list-item .col.release_date {
width: 15%; }
.list.album-list .list-item .col.tracks_total {
width: 10%; }
.list.queue-history-track-list .list-item .col.played_at, .list.queue-history-track-list .list-item .col.source { .list.library-local-album-list .list-item {
width: 20%; } cursor: pointer; }
.list.library-local-album-list .list-item .col.name, .list.library-local-album-list .list-item .col.artists {
width: 45%; }
.list.library-local-album-list .list-item .col.tracks_total {
width: 10%; }
.list.artist-list .list-item {
cursor: pointer; }
.list.artist-list .list-item .col.name {
width: 70%; }
.list.artist-list .list-item .col.followers_total, .list.artist-list .list-item .col.popularity {
width: 15%; }
.list.queue-track-list {
cursor: pointer; }
.list.queue-track-list .list-item {
padding-right: 60px; }
.list.queue-track-list .list-item .col.name, .list.queue-track-list .list-item .col.artists, .list.queue-track-list .list-item .col.album {
width: 25%; }
.list.queue-track-list .list-item .col.added {
width: 20%; }
.list.queue-track-list .list-item .col.added .by {
padding-right: 0.25em; }
.list.queue-track-list .list-item .col.added .from:before {
content: '(';
display: inline-block; }
.list.queue-track-list .list-item .col.added .from:after {
content: ')';
display: inline-block; }
.list.queue-history-track-list .list-item {
cursor: pointer; }
.list.queue-history-track-list .list-item .col.name {
width: 60%; }
.list.queue-history-track-list .list-item .col.played_at, .list.queue-history-track-list .list-item .col.source {
width: 20%; }
.list.related-artist-list { .list.related-artist-list {
padding-bottom: 20px; } padding-bottom: 20px;
cursor: pointer; }
.list.related-artist-list .artist { .list.related-artist-list .artist {
display: block; display: block;
border: 0; border: 0;
@ -4758,7 +4745,8 @@ aside {
width: auto !important; width: auto !important;
float: none !important; float: none !important;
padding-left: 0.25em; } padding-left: 0.25em; }
.list.queue-track-list .list-item .added .by { .list.queue-track-list .list-item .added .by,
.list.queue-track-list .list-item .added .empty {
display: none; } display: none; }
.list.queue-track-list .list-item .added .from { .list.queue-track-list .list-item .added .from {
padding-left: 14px; padding-left: 14px;
@ -5343,6 +5331,8 @@ main header {
display: block; } display: block; }
.dropdown-field.no-status-icon .options .option { .dropdown-field.no-status-icon .options .option {
padding-left: 12px; } padding-left: 12px; }
.dropdown-field.no-label .label > .text {
display: none; }
.dropdown-field .options { .dropdown-field .options {
color: #000000; color: #000000;
position: absolute; position: absolute;

View File

@ -4772,11 +4772,14 @@ exports.getQueueMetadata = getQueueMetadata;
exports.queueMetadataChanged = queueMetadataChanged; exports.queueMetadataChanged = queueMetadataChanged;
exports.addQueueMetadata = addQueueMetadata; exports.addQueueMetadata = addQueueMetadata;
exports.getSnapcast = getSnapcast; exports.getSnapcast = getSnapcast;
exports.snapcastServerLoaded = snapcastServerLoaded;
exports.setSnapcastClientName = setSnapcastClientName; exports.setSnapcastClientName = setSnapcastClientName;
exports.setSnapcastClientMute = setSnapcastClientMute; exports.setSnapcastClientMute = setSnapcastClientMute;
exports.setSnapcastClientVolume = setSnapcastClientVolume; exports.setSnapcastClientVolume = setSnapcastClientVolume;
exports.setSnapcastClientLatency = setSnapcastClientLatency; exports.setSnapcastClientLatency = setSnapcastClientLatency;
exports.setSnapcastClientGroup = setSnapcastClientGroup;
exports.deleteSnapcastClient = deleteSnapcastClient; exports.deleteSnapcastClient = deleteSnapcastClient;
exports.setSnapcastGroupStream = setSnapcastGroupStream;
/** /**
* Actions and Action Creators * Actions and Action Creators
@ -4989,6 +4992,13 @@ function getSnapcast() {
}; };
} }
function snapcastServerLoaded(server) {
return {
type: 'PUSHER_SNAPCAST_SERVER_LOADED',
server: server
};
}
function setSnapcastClientName(id, name) { function setSnapcastClientName(id, name) {
return { return {
type: 'PUSHER_SET_SNAPCAST_CLIENT_NAME', type: 'PUSHER_SET_SNAPCAST_CLIENT_NAME',
@ -5021,6 +5031,15 @@ function setSnapcastClientLatency(id, latency) {
}; };
} }
function setSnapcastClientGroup(id, group_id) {
console.log(id, group_id);
return {
type: 'PUSHER_SET_SNAPCAST_CLIENT_GROUP',
id: id,
group_id: group_id
};
}
function deleteSnapcastClient(id) { function deleteSnapcastClient(id) {
return { return {
type: 'PUSHER_DELETE_SNAPCAST_CLIENT', type: 'PUSHER_DELETE_SNAPCAST_CLIENT',
@ -5028,6 +5047,14 @@ function deleteSnapcastClient(id) {
}; };
} }
function setSnapcastGroupStream(id, stream_id) {
return {
type: 'PUSHER_SET_SNAPCAST_GROUP_STREAM',
id: id,
stream_id: stream_id
};
}
/***/ }), /***/ }),
/* 18 */ /* 18 */
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
@ -19458,6 +19485,9 @@ var DropdownField = function (_React$Component) {
if (this.props.no_status_icon) { if (this.props.no_status_icon) {
className += ' no-status-icon'; className += ' no-status-icon';
} }
if (this.props.no_label) {
className += ' no-label';
}
if (this.props.button) { if (this.props.button) {
className += ' buttonify'; className += ' buttonify';
} }
@ -24492,6 +24522,14 @@ var Track = function (_React$Component) {
); );
break; break;
case "search":
var link = _react2.default.createElement(
_URILink2.default,
{ type: 'search', uri: track.added_from.replace("iris:", "") },
'search'
);
break;
default: default:
var link = _react2.default.createElement( var link = _react2.default.createElement(
_URILink2.default, _URILink2.default,
@ -24528,7 +24566,7 @@ var Track = function (_React$Component) {
} else { } else {
var added = _react2.default.createElement( var added = _react2.default.createElement(
'span', 'span',
{ className: 'placeholder' }, { className: 'empty' },
'-' '-'
); );
} }
@ -49459,7 +49497,17 @@ function reducer() {
return Object.assign({}, pusher, { config: action.config }); return Object.assign({}, pusher, { config: action.config });
case 'PUSHER_SNAPCAST': case 'PUSHER_SNAPCAST':
return Object.assign({}, pusher, { snapcast_clients: action.snapcast_clients, snapcast_groups: action.snapcast_groups }); return Object.assign({}, pusher, {
snapcast_streams: action.snapcast_streams,
snapcast_groups: action.snapcast_groups,
snapcast_clients: action.snapcast_clients
});
case 'PUSHER_SNAPCAST_GROUP_UPDATED':
var snapcast_groups = Object.assign({}, pusher.snapcast_groups);
var group = snapcast_groups[action.key];
snapcast_groups[action.key] = Object.assign({}, group, action.group);
return Object.assign({}, pusher, { snapcast_groups: snapcast_groups });
case 'PUSHER_SNAPCAST_CLIENT_UPDATED': case 'PUSHER_SNAPCAST_CLIENT_UPDATED':
var snapcast_clients = Object.assign({}, pusher.snapcast_clients); var snapcast_clients = Object.assign({}, pusher.snapcast_clients);
@ -51432,40 +51480,53 @@ var PusherMiddleware = function () {
**/ **/
case 'PUSHER_GET_SNAPCAST': case 'PUSHER_GET_SNAPCAST':
request(store, 'snapcast_instruct', action.data).then(function (response) { request(store, 'snapcast_instruct', action.data).then(function (response) {
var groups = {}; store.dispatch(pusherActions.snapcastServerLoaded(response.server));
var clients = {};
// Loop all the groups
for (var i = 0; i < response.server.groups.length; i++) {
var group = response.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
};
}
store.dispatch({
type: 'PUSHER_SNAPCAST',
snapcast_clients: clients,
snapcast_groups: groups
});
}, function (error) { }, function (error) {
store.dispatch(coreActions.handleException('Could not get Snapcast server', error, error.message)); store.dispatch(coreActions.handleException('Could not get Snapcast server', error, error.message));
}); });
break; break;
case 'PUSHER_SNAPCAST_SERVER_LOADED':
console.log(action);
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': case 'PUSHER_SET_SNAPCAST_CLIENT_NAME':
var client = store.getState().pusher.snapcast_clients[action.id]; var client = store.getState().pusher.snapcast_clients[action.id];
var data = { var data = {
@ -51572,6 +51633,24 @@ var PusherMiddleware = function () {
}); });
break; break;
case 'PUSHER_SET_SNAPCAST_CLIENT_GROUP':
var group = store.getState().pusher.snapcast_groups[action.group_id];
var clients_ids = Object.assign([], group.clients_ids, [action.id]);
var data = {
method: 'Group.SetClients',
params: {
id: action.group_id,
clients: clients_ids
}
};
request(store, 'snapcast_instruct', data).then(function (response) {
store.dispatch(pusherActions.snapcastServerLoaded(response.server));
}, function (error) {
store.dispatch(coreActions.handleException('Error', error, error.message));
});
break;
case 'PUSHER_DELETE_SNAPCAST_CLIENT': case 'PUSHER_DELETE_SNAPCAST_CLIENT':
var data = { var data = {
method: 'Server.DeleteClient', method: 'Server.DeleteClient',
@ -51590,6 +51669,29 @@ var PusherMiddleware = function () {
}); });
break; 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(function (response) {
store.dispatch({
type: 'PUSHER_SNAPCAST_GROUP_UPDATED',
key: action.id,
group: {
stream_id: action.stream_id
}
});
}, function (error) {
store.dispatch(coreActions.handleException('Error', error, error.message));
});
break;
// This action is irrelevant to us, pass it on to the next middleware // This action is irrelevant to us, pass it on to the next middleware
default: default:
return next(action); return next(action);
@ -51722,6 +51824,7 @@ var MopidyMiddleware = function () {
break; break;
case 'event:trackPlaybackEnded': case 'event:trackPlaybackEnded':
// This doesn't fire when it's the last track in the queue... bug in Mopidy?
store.dispatch(mopidyActions.clearCurrentTrack()); store.dispatch(mopidyActions.clearCurrentTrack());
store.dispatch(mopidyActions.getTimePosition()); store.dispatch(mopidyActions.getTimePosition());
break; break;
@ -68022,7 +68125,7 @@ var Search = function (_React$Component) {
_react2.default.createElement( _react2.default.createElement(
'section', 'section',
{ className: 'list-wrapper' }, { className: 'list-wrapper' },
_react2.default.createElement(_TrackList2.default, { tracks: tracks, uri: this.props.params.query, show_source_icon: true }), _react2.default.createElement(_TrackList2.default, { tracks: tracks, uri: 'iris:' + this.props.params.query, show_source_icon: true }),
_react2.default.createElement(_LazyLoadListener2.default, { enabled: this.props['tracks_more'] && spotify_search_enabled, loadMore: function loadMore() { _react2.default.createElement(_LazyLoadListener2.default, { enabled: this.props['tracks_more'] && spotify_search_enabled, loadMore: function loadMore() {
return _this2.loadMore('tracks'); return _this2.loadMore('tracks');
} }) } })
@ -68127,7 +68230,7 @@ var Search = function (_React$Component) {
var tracks_section = _react2.default.createElement( var tracks_section = _react2.default.createElement(
'section', 'section',
{ className: 'list-wrapper' }, { className: 'list-wrapper' },
_react2.default.createElement(_TrackList2.default, { tracks: tracks, uri: this.props.params.query, show_source_icon: true }), _react2.default.createElement(_TrackList2.default, { tracks: tracks, uri: 'iris:' + this.props.params.query, show_source_icon: true }),
_react2.default.createElement(_LazyLoadListener2.default, { loading: this.props['tracks_more'] && spotify_search_enabled, loadMore: function loadMore() { _react2.default.createElement(_LazyLoadListener2.default, { loading: this.props['tracks_more'] && spotify_search_enabled, loadMore: function loadMore() {
return _this2.loadMore('tracks'); return _this2.loadMore('tracks');
} }) } })
@ -70288,6 +70391,10 @@ var _TextField = __webpack_require__(257);
var _TextField2 = _interopRequireDefault(_TextField); var _TextField2 = _interopRequireDefault(_TextField);
var _DropdownField = __webpack_require__(40);
var _DropdownField2 = _interopRequireDefault(_DropdownField);
var _helpers = __webpack_require__(1); var _helpers = __webpack_require__(1);
var helpers = _interopRequireWildcard(_helpers); var helpers = _interopRequireWildcard(_helpers);
@ -70372,17 +70479,34 @@ var Snapcast = function (_React$Component) {
); );
} }
var streams = [];
for (var key in this.props.snapcast_streams) {
if (this.props.snapcast_streams.hasOwnProperty(key)) {
streams.push(this.props.snapcast_streams[key]);
}
}
// Construct a simple array of our groups index // Construct a simple array of our groups index
var groups = []; var groups = [];
var groups_dropdown = [];
for (var group_id in this.props.snapcast_groups) { for (var group_id in this.props.snapcast_groups) {
if (this.props.snapcast_groups.hasOwnProperty(group_id)) { if (this.props.snapcast_groups.hasOwnProperty(group_id)) {
var group = this.props.snapcast_groups[group_id]; var group = this.props.snapcast_groups[group_id];
// Append to our groups dropdown source array
groups_dropdown.push({
label: group.name ? group.name : group.stream_id,
value: group.id
});
// Merge the group's clients into this group (also as a simple array) // Merge the group's clients into this group (also as a simple array)
var clients = []; var clients = [];
for (var i = 0; i < group.clients_ids.length; i++) { for (var i = 0; i < group.clients_ids.length; i++) {
if (this.props.snapcast_clients.hasOwnProperty(group.clients_ids[i])) { if (this.props.snapcast_clients.hasOwnProperty(group.clients_ids[i])) {
clients.push(this.props.snapcast_clients[group.clients_ids[i]]); var client = this.props.snapcast_clients[group.clients_ids[i]];
if (client.connected || this.props.snapcast_show_disconnected_clients) {
clients.push(client);
}
} }
} }
@ -70395,104 +70519,156 @@ var Snapcast = function (_React$Component) {
return _react2.default.createElement( return _react2.default.createElement(
'div', 'div',
{ className: 'snapcast' }, { className: 'snapcast' },
_react2.default.createElement(
'div',
{ className: 'field checkbox' },
_react2.default.createElement(
'div',
{ className: 'input' },
_react2.default.createElement(
'button',
{ onClick: function onClick(e) {
return _this2.props.pusherActions.getSnapcast();
} },
'Refresh'
),
_react2.default.createElement(
'label',
null,
_react2.default.createElement('input', {
type: 'checkbox',
name: 'snapcast_show_disconnected_clients',
checked: this.props.snapcast_show_disconnected_clients,
onChange: function onChange(e) {
return _this2.props.uiActions.set({ snapcast_show_disconnected_clients: !_this2.props.snapcast_show_disconnected_clients });
} }),
_react2.default.createElement(
'span',
{ className: 'label' },
'Show disconnected clients'
)
)
)
),
groups.map(function (group) { groups.map(function (group) {
return _react2.default.createElement( return _react2.default.createElement(
'div', 'div',
{ className: 'list group', key: group.id }, { className: 'group', key: group.id },
_react2.default.createElement(
'h3',
{ className: 'name' },
group.name ? group.name : group.stream_id
),
_react2.default.createElement( _react2.default.createElement(
'div', 'div',
{ className: 'list-item header' }, { className: 'field dropdown' },
_react2.default.createElement( _react2.default.createElement(
'div', 'div',
{ className: 'col name' }, { className: 'name' },
group.name ? group.name : 'Untitled group' 'Source'
), ),
_react2.default.createElement( _react2.default.createElement(
'div', 'div',
{ className: 'col volume' }, { className: 'input' },
'Volume'
),
_react2.default.createElement(
'div',
{ className: 'col latency' },
'Latency'
)
),
group.clients.map(function (client) {
var name = client.config.name ? client.config.name : client.host.name;
return _react2.default.createElement(
'div',
{ className: 'list-item client', key: client.id },
_react2.default.createElement( _react2.default.createElement(
'div', 'select',
{ className: 'col name' }, { onChange: function onChange(e) {
_react2.default.createElement(_TextField2.default, { return _this2.props.pusherActions.setSnapcastGroupStream(group.id, e.target.value);
onChange: function onChange(value) { }, value: group.stream_id },
return _this2.props.pusherActions.setSnapcastClientName(client.id, value); streams.map(function (stream) {
}, return _react2.default.createElement(
value: name 'option',
}), { value: stream.id, key: stream.id },
client.connected ? _react2.default.createElement( stream.id,
'span', ' (',
{ className: 'status has-tooltip' }, stream.status,
_react2.default.createElement(_reactFontawesome2.default, { className: 'green-text', name: 'circle' }), ')'
_react2.default.createElement( );
'span',
{ className: 'tooltip' },
'Connected'
)
) : _react2.default.createElement(
'span',
{ className: 'status has-tooltip' },
_react2.default.createElement(_reactFontawesome2.default, { className: 'grey-text', name: 'circle' }),
_react2.default.createElement(
'span',
{ className: 'tooltip' },
'Not connected'
)
)
),
_react2.default.createElement(
'div',
{ className: 'col volume' },
_react2.default.createElement(_VolumeControl2.default, {
volume: client.config.volume.percent,
mute: client.config.volume.muted,
onVolumeChange: function onVolumeChange(percent) {
return _this2.props.pusherActions.setSnapcastClientVolume(client.id, percent);
},
onMuteChange: function onMuteChange(mute) {
return _this2.props.pusherActions.setSnapcastClientMute(client.id, mute);
}
}),
_react2.default.createElement(_TextField2.default, {
className: 'tiny',
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientVolume(client.id, parseInt(value));
},
value: client.config.volume.percent
})
),
_react2.default.createElement(
'div',
{ className: 'col latency' },
_react2.default.createElement(_LatencyControl2.default, {
max: '100',
value: client.config.latency,
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value));
}
}),
_react2.default.createElement(_TextField2.default, {
className: 'tiny',
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value));
},
value: String(client.config.latency)
}) })
) )
); )
}) ),
_react2.default.createElement(
'div',
{ className: 'field' },
_react2.default.createElement(
'div',
{ className: 'name' },
'Clients'
),
_react2.default.createElement(
'div',
{ className: 'input' },
_react2.default.createElement(
'div',
{ className: 'list clients' },
group.clients.map(function (client) {
var name = client.config.name ? client.config.name : client.host.name;
return _react2.default.createElement(
'div',
{ className: 'list-item client', key: client.id },
_react2.default.createElement(
'div',
{ className: 'col name' },
_react2.default.createElement(_TextField2.default, {
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientName(client.id, value);
},
value: name
}),
_react2.default.createElement(
'div',
{ className: 'tools' },
client.connected ? null : _react2.default.createElement(_reactFontawesome2.default, { className: 'disconnected red-text', name: 'plug' }),
_react2.default.createElement(_DropdownField2.default, { icon: 'cog', name: 'Group', no_label: true, value: group.id, options: groups_dropdown, handleChange: function handleChange(val) {
_this2.props.pusherActions.setSnapcastClientGroup(client.id, group.id);_this2.props.uiActions.hideContextMenu();
} })
)
),
_react2.default.createElement(
'div',
{ className: 'col volume' },
_react2.default.createElement(_VolumeControl2.default, {
volume: client.config.volume.percent,
mute: client.config.volume.muted,
onVolumeChange: function onVolumeChange(percent) {
return _this2.props.pusherActions.setSnapcastClientVolume(client.id, percent);
},
onMuteChange: function onMuteChange(mute) {
return _this2.props.pusherActions.setSnapcastClientMute(client.id, mute);
}
}),
_react2.default.createElement(_TextField2.default, {
className: 'tiny',
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientVolume(client.id, parseInt(value));
},
value: client.config.volume.percent
})
),
_react2.default.createElement(
'div',
{ className: 'col latency' },
_react2.default.createElement(_LatencyControl2.default, {
max: '100',
value: client.config.latency,
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value));
}
}),
_react2.default.createElement(_TextField2.default, {
className: 'tiny',
onChange: function onChange(value) {
return _this2.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value));
},
value: String(client.config.latency)
})
)
);
})
)
)
)
); );
}) })
); );
@ -70506,6 +70682,8 @@ var mapStateToProps = function mapStateToProps(state, ownProps) {
return { return {
snapcast_enabled: state.pusher.config.snapcast_enabled, snapcast_enabled: state.pusher.config.snapcast_enabled,
pusher_connected: state.pusher.connected, pusher_connected: state.pusher.connected,
snapcast_show_disconnected_clients: state.ui.snapcast_show_disconnected_clients !== undefined ? state.ui.snapcast_show_disconnected_clients : false,
snapcast_streams: state.pusher.snapcast_streams,
snapcast_groups: state.pusher.snapcast_groups, snapcast_groups: state.pusher.snapcast_groups,
snapcast_clients: state.pusher.snapcast_clients snapcast_clients: state.pusher.snapcast_clients
}; };

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@
"start_url": "/iris/index.html", "start_url": "/iris/index.html",
"background_color": "#08d58f", "background_color": "#08d58f",
"theme_color": "#181818", "theme_color": "#181818",
"display": "standalone", "display": "fullscreen",
"app": { "app": {
"background": {} "background": {}
} }

View File

@ -18,9 +18,12 @@
<link rel="manifest" href="/iris/assets/manifest.json"> <link rel="manifest" href="/iris/assets/manifest.json">
<!-- Apple app --> <!-- Apple app -->
<link rel="apple-touch-startup-image" href="/iris/assets/logo-full-512.png"> <meta name="apple-mobile-web-app-title" content="Iris" />
<link rel="apple-touch-icon" sizes="192x192" href="/iris/assets/logo-app-192.png"> <meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" sizes="512x512" href="/iris/assets/logo-app-512.png"> <meta name="apple-mobile-web-app-status-bar-style" content="#121212">
<link rel="apple-touch-startup-image" href="/iris/assets/logo-full-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="/iris/assets/logo-app-192.png" />
<link rel="apple-touch-icon" sizes="512x512" href="/iris/assets/logo-app-512.png" />
<!-- Raven Sentry --> <!-- Raven Sentry -->
<script src="https://cdn.ravenjs.com/3.17.0/raven.min.js" crossorigin="anonymous"></script> <script src="https://cdn.ravenjs.com/3.17.0/raven.min.js" crossorigin="anonymous"></script>
@ -73,7 +76,7 @@
// Release details // Release details
// These are automatically injected to built HTML // These are automatically injected to built HTML
var build = "1524541685"; var build = "1524953170";
var version = "3.17.4"; var version = "3.17.4";
// Construct the script tag // Construct the script tag

View File

@ -51,6 +51,9 @@ export default class DropdownField extends React.Component{
if (this.props.no_status_icon){ if (this.props.no_status_icon){
className += ' no-status-icon'; className += ' no-status-icon';
} }
if (this.props.no_label){
className += ' no-label';
}
if (this.props.button){ if (this.props.button){
className += ' buttonify'; className += ' buttonify';
} }

View File

@ -9,6 +9,7 @@ import ArtistSentence from './ArtistSentence'
import VolumeControl from './Fields/VolumeControl' import VolumeControl from './Fields/VolumeControl'
import LatencyControl from './Fields/LatencyControl' import LatencyControl from './Fields/LatencyControl'
import TextField from './Fields/TextField' import TextField from './Fields/TextField'
import DropdownField from './Fields/DropdownField'
import * as helpers from '../helpers' import * as helpers from '../helpers'
import * as coreActions from '../services/core/actions' import * as coreActions from '../services/core/actions'
@ -58,12 +59,26 @@ class Snapcast extends React.Component{
) )
} }
var streams = [];
for (var key in this.props.snapcast_streams){
if (this.props.snapcast_streams.hasOwnProperty(key)){
streams.push(this.props.snapcast_streams[key]);
}
}
// Construct a simple array of our groups index // Construct a simple array of our groups index
var groups = []; var groups = [];
var groups_dropdown = [];
for (var group_id in this.props.snapcast_groups){ for (var group_id in this.props.snapcast_groups){
if (this.props.snapcast_groups.hasOwnProperty(group_id)){ if (this.props.snapcast_groups.hasOwnProperty(group_id)){
var group = this.props.snapcast_groups[group_id]; var group = this.props.snapcast_groups[group_id];
// Append to our groups dropdown source array
groups_dropdown.push({
label: (group.name ? group.name : group.stream_id),
value: group.id
});
// Merge the group's clients into this group (also as a simple array) // Merge the group's clients into this group (also as a simple array)
var clients = []; var clients = [];
for (var i = 0; i < group.clients_ids.length; i++){ for (var i = 0; i < group.clients_ids.length; i++){
@ -107,65 +122,81 @@ class Snapcast extends React.Component{
{ {
groups.map(group => { groups.map(group => {
return ( return (
<div className="list group" key={group.id}> <div className="group" key={group.id}>
<div className="list-item header"> <h3 className="name">
<div className="col name"> {group.name ? group.name : group.stream_id}
{group.name ? group.name : 'Untitled group'} </h3>
<div className="field dropdown">
<div className="name">
Source
</div> </div>
<div className="col volume"> <div className="input">
Volume <select onChange={e => this.props.pusherActions.setSnapcastGroupStream(group.id, e.target.value)} value={group.stream_id}>
</div> {
<div className="col latency"> streams.map(stream => {
Latency return (
<option value={stream.id} key={stream.id}>
{stream.id} ({stream.status})
</option>
);
})
}
</select>
</div>
</div>
<div className="field">
<div className="name">
Clients
</div>
<div className="input">
<div className="list clients">
{
group.clients.map(client => {
var name = client.config.name ? client.config.name : client.host.name;
return (
<div className="list-item client" key={client.id}>
<div className="col name">
<TextField
onChange={value => this.props.pusherActions.setSnapcastClientName(client.id, value)}
value={name}
/>
<div className="tools">
{client.connected ? null : <FontAwesome className="disconnected red-text" name="plug" />}
<DropdownField icon="cog" name="Group" no_label value={group.id} options={groups_dropdown} handleChange={val => {this.props.pusherActions.setSnapcastClientGroup(client.id, group.id); this.props.uiActions.hideContextMenu()}} />
</div>
</div>
<div className="col volume">
<VolumeControl
volume={client.config.volume.percent}
mute={client.config.volume.muted}
onVolumeChange={percent => this.props.pusherActions.setSnapcastClientVolume(client.id, percent)}
onMuteChange={mute => this.props.pusherActions.setSnapcastClientMute(client.id, mute)}
/>
<TextField
className="tiny"
onChange={value => this.props.pusherActions.setSnapcastClientVolume(client.id, parseInt(value))}
value={client.config.volume.percent}
/>
</div>
<div className="col latency">
<LatencyControl
max="100"
value={client.config.latency}
onChange={value => this.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value))}
/>
<TextField
className="tiny"
onChange={value => this.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value))}
value={String(client.config.latency)}
/>
</div>
</div>
);
})
}
</div>
</div> </div>
</div> </div>
{
group.clients.map(client => {
var name = client.config.name ? client.config.name : client.host.name;
return (
<div className="list-item client" key={client.id}>
<div className="col name">
<TextField
onChange={value => this.props.pusherActions.setSnapcastClientName(client.id, value)}
value={name}
/>
{client.connected ? <span className="status has-tooltip">
<FontAwesome className="green-text" name="circle" />
<span className="tooltip">Connected</span>
</span> : <span className="status has-tooltip">
<FontAwesome className="grey-text" name="circle" />
<span className="tooltip">Not connected</span>
</span>}
</div>
<div className="col volume">
<VolumeControl
volume={client.config.volume.percent}
mute={client.config.volume.muted}
onVolumeChange={percent => this.props.pusherActions.setSnapcastClientVolume(client.id, percent)}
onMuteChange={mute => this.props.pusherActions.setSnapcastClientMute(client.id, mute)}
/>
<TextField
className="tiny"
onChange={value => this.props.pusherActions.setSnapcastClientVolume(client.id, parseInt(value))}
value={client.config.volume.percent}
/>
</div>
<div className="col latency">
<LatencyControl
max="100"
value={client.config.latency}
onChange={value => this.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value))}
/>
<TextField
className="tiny"
onChange={value => this.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value))}
value={String(client.config.latency)}
/>
</div>
</div>
);
})
}
</div> </div>
); );
}) })
@ -180,6 +211,7 @@ const mapStateToProps = (state, ownProps) => {
snapcast_enabled: state.pusher.config.snapcast_enabled, snapcast_enabled: state.pusher.config.snapcast_enabled,
pusher_connected: state.pusher.connected, pusher_connected: state.pusher.connected,
snapcast_show_disconnected_clients: (state.ui.snapcast_show_disconnected_clients !== undefined ? state.ui.snapcast_show_disconnected_clients : false), snapcast_show_disconnected_clients: (state.ui.snapcast_show_disconnected_clients !== undefined ? state.ui.snapcast_show_disconnected_clients : false),
snapcast_streams: state.pusher.snapcast_streams,
snapcast_groups: state.pusher.snapcast_groups, snapcast_groups: state.pusher.snapcast_groups,
snapcast_clients: state.pusher.snapcast_clients snapcast_clients: state.pusher.snapcast_clients
} }

View File

@ -203,6 +203,13 @@ export function getSnapcast(){
} }
} }
export function snapcastServerLoaded(server){
return {
type: 'PUSHER_SNAPCAST_SERVER_LOADED',
server: server
}
}
export function setSnapcastClientName(id, name){ export function setSnapcastClientName(id, name){
return { return {
type: 'PUSHER_SET_SNAPCAST_CLIENT_NAME', type: 'PUSHER_SET_SNAPCAST_CLIENT_NAME',
@ -235,6 +242,15 @@ export function setSnapcastClientLatency(id, latency){
} }
} }
export function setSnapcastClientGroup(id, group_id){
console.log(id,group_id);
return {
type: 'PUSHER_SET_SNAPCAST_CLIENT_GROUP',
id: id,
group_id: group_id
}
}
export function deleteSnapcastClient(id){ export function deleteSnapcastClient(id){
return { return {
type: 'PUSHER_DELETE_SNAPCAST_CLIENT', type: 'PUSHER_DELETE_SNAPCAST_CLIENT',
@ -242,3 +258,11 @@ export function deleteSnapcastClient(id){
} }
} }
export function setSnapcastGroupStream(id, stream_id){
return {
type: 'PUSHER_SET_SNAPCAST_GROUP_STREAM',
id: id,
stream_id: stream_id
}
}

View File

@ -586,43 +586,7 @@ const PusherMiddleware = (function(){
request(store, 'snapcast_instruct', action.data) request(store, 'snapcast_instruct', action.data)
.then( .then(
response => { response => {
var groups = {}; store.dispatch(pusherActions.snapcastServerLoaded(response.server));
var clients = {};
var streams = {};
// Loop all the groups
for (var i = 0; i < response.server.groups.length; i++){
var group = response.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 < response.server.streams.length; i++){
var stream = response.server.streams[i];
streams[stream.id] = stream;
}
store.dispatch({
type: 'PUSHER_SNAPCAST',
snapcast_clients: clients,
snapcast_groups: groups,
snapcast_streams: streams
});
}, },
error => { error => {
store.dispatch(coreActions.handleException( store.dispatch(coreActions.handleException(
@ -634,6 +598,47 @@ const PusherMiddleware = (function(){
); );
break break
case 'PUSHER_SNAPCAST_SERVER_LOADED':
console.log(action);
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': case 'PUSHER_SET_SNAPCAST_CLIENT_NAME':
var client = store.getState().pusher.snapcast_clients[action.id]; var client = store.getState().pusher.snapcast_clients[action.id];
var data = { var data = {
@ -772,6 +777,32 @@ const PusherMiddleware = (function(){
); );
break break
case 'PUSHER_SET_SNAPCAST_CLIENT_GROUP':
var group = store.getState().pusher.snapcast_groups[action.group_id];
var clients_ids = Object.assign([], group.clients_ids, [action.id]);
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': case 'PUSHER_DELETE_SNAPCAST_CLIENT':
var data = { var data = {
method: 'Server.DeleteClient', method: 'Server.DeleteClient',
@ -798,6 +829,37 @@ const PusherMiddleware = (function(){
); );
break 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(
'Error',
error,
error.message
));
}
);
break
// This action is irrelevant to us, pass it on to the next middleware // This action is irrelevant to us, pass it on to the next middleware
default: default:
return next(action); return next(action);

View File

@ -55,7 +55,17 @@ export default function reducer(pusher = {}, action){
return Object.assign({}, pusher, { config: action.config }); return Object.assign({}, pusher, { config: action.config });
case 'PUSHER_SNAPCAST': case 'PUSHER_SNAPCAST':
return Object.assign({}, pusher, { snapcast_clients: action.snapcast_clients, snapcast_groups: action.snapcast_groups }); return Object.assign({}, pusher, {
snapcast_streams: action.snapcast_streams,
snapcast_groups: action.snapcast_groups,
snapcast_clients: action.snapcast_clients
});
case 'PUSHER_SNAPCAST_GROUP_UPDATED':
var snapcast_groups = Object.assign({}, pusher.snapcast_groups);
var group = snapcast_groups[action.key];
snapcast_groups[action.key] = Object.assign({}, group, action.group);
return Object.assign({}, pusher, { snapcast_groups: snapcast_groups });
case 'PUSHER_SNAPCAST_CLIENT_UPDATED': case 'PUSHER_SNAPCAST_CLIENT_UPDATED':
var snapcast_clients = Object.assign({}, pusher.snapcast_clients); var snapcast_clients = Object.assign({}, pusher.snapcast_clients);

View File

@ -33,6 +33,12 @@
} }
} }
&.no-label {
.label > .text {
display: none;
}
}
.options { .options {
color: $black; color: $black;
position: absolute; position: absolute;

View File

@ -13,7 +13,6 @@
position: relative; position: relative;
margin: 0 -10px -1px -10px; margin: 0 -10px -1px -10px;
padding: 14px 30px 13px 10px; padding: 14px 30px 13px 10px;
cursor: pointer;
border-radius: 3px; border-radius: 3px;
border-bottom: 1px solid rgba(255,255,255,0.05); border-bottom: 1px solid rgba(255,255,255,0.05);
border-top: 1px solid rgba(255,255,255,0.05); border-top: 1px solid rgba(255,255,255,0.05);
@ -96,6 +95,8 @@
&.track-list { &.track-list {
.list-item { .list-item {
cursor: pointer;
.col { .col {
&.name { &.name {
width: 40%; width: 40%;
@ -142,6 +143,8 @@
} }
&.playlist-list .list-item { &.playlist-list .list-item {
cursor: pointer;
.col { .col {
&.name { &.name {
width: 50%; width: 50%;
@ -159,6 +162,8 @@
} }
&.album-list .list-item { &.album-list .list-item {
cursor: pointer;
.col { .col {
&.name, &.name,
&.artists { &.artists {
@ -175,6 +180,8 @@
} }
&.library-local-album-list .list-item{ &.library-local-album-list .list-item{
cursor: pointer;
.col { .col {
&.name, &.name,
&.artists { &.artists {
@ -187,6 +194,8 @@
} }
&.artist-list .list-item { &.artist-list .list-item {
cursor: pointer;
.col { .col {
&.name { &.name {
width: 70%; width: 70%;
@ -199,6 +208,8 @@
} }
&.queue-track-list { &.queue-track-list {
cursor: pointer;
.list-item { .list-item {
padding-right: 60px; padding-right: 60px;
.col { .col {
@ -230,6 +241,8 @@
} }
&.queue-history-track-list .list-item { &.queue-history-track-list .list-item {
cursor: pointer;
.col { .col {
&.name { &.name {
width: 60%; width: 60%;
@ -243,6 +256,7 @@
&.related-artist-list { &.related-artist-list {
padding-bottom: 20px; padding-bottom: 20px;
cursor: pointer;
.artist { .artist {
display: block; display: block;

View File

@ -1,79 +1,60 @@
.snapcast { .snapcast {
.group.list { .group {
padding: 0 10px;
&:not(:first-child){ &:not(:first-child){
padding-top: 15px; padding-top: 20px;
} }
.list-item { .clients {
padding: 20px 0 0 0; padding: 0 10px;
&:not(.header){ .client {
border: 0;
}
& > .name {
width: 40%;
padding-bottom: 4px;
position: relative;
.status {
position: absolute;
top: 0;
left: 0;
z-index: 2;
padding: 12px;
cursor: default;
.fa {
font-size: 12px;
}
}
input {
padding-left: 30px;
}
}
& > .volume {
width: 28%;
padding-left: 2%;
}
& > .latency {
width: 28%;
padding-left: 2%;
}
&.header {
padding-top: 0;
vertical-align: bottom;
cursor: default;
.name {
font-size: 14px;
color: $white;
text-transform: none;
font-weight: 600;
line-height: 14px;
}
}
&.client {
@include clearfix(); @include clearfix();
padding: 20px 0 0 0; border: 0;
padding: 0;
clear: both;
&:not(:first-child){
padding-top: 8px;
}
& > .name { & > .name {
width: 40%;
padding-bottom: 4px;
position: relative;
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
border: 0; border: 0;
input { input {
width: 100%; width: 100%;
padding-right: 30px;
} }
.tools {
position: absolute;
top: -2px;
right: 0;
z-index: 2;
.disconnected {
padding: 12px 0;
cursor: default;
font-size: 12px;
}
}
}
& > .volume {
width: 28%;
padding-left: 2%;
}
& > .latency {
width: 28%;
padding-left: 2%;
} }
& > .volume, & > .volume,
@ -137,35 +118,25 @@
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
} }
}
@include responsive($bp_medium){ @include responsive($bp_medium){
padding-left: 0 !important;
padding-right: 0 !important;
padding-left: 0 !important; .col {
padding-right: 0 !important; @include clearfix();
float: none !important;
width: auto !important;
padding-left: 0;
padding-right: 0;
&.header { &:not(.header){
display: block; line-height: 45px;
}
.volume, &.volume {
.latency { padding-bottom: 20px !important;
display: none !important; }
}
}
.col {
@include clearfix();
float: none !important;
width: auto !important;
padding-left: 0;
padding-right: 0;
&:not(.header){
line-height: 45px;
}
&.volume {
padding-bottom: 20px !important;
} }
} }
} }