Refining dark vs light modes; Fixing output control dropdown
This commit is contained in:
@ -338,7 +338,7 @@ export class App extends React.Component {
|
||||
uiActions={this.props.uiActions}
|
||||
slim_mode={this.props.slim_mode}
|
||||
/>
|
||||
{this.props.hotkeys_enabled && <Hotkeys />}
|
||||
{this.props.hotkeys_enabled && <Hotkeys history={this.props.history} />}
|
||||
<ContextMenu />
|
||||
<Dragger />
|
||||
<Notifications />
|
||||
|
||||
@ -54,120 +54,140 @@ class OutputControl extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderOutputs() {
|
||||
snapcastGroups() {
|
||||
const {
|
||||
snapcast_streams,
|
||||
snapcastActions,
|
||||
snapcast_groups,
|
||||
} = this.props;
|
||||
let has_outputs = false;
|
||||
|
||||
const groups = [];
|
||||
for (var key in this.props.snapcast_groups) {
|
||||
if (this.props.snapcast_groups.hasOwnProperty(key)) {
|
||||
groups.push(this.props.snapcast_groups[key]);
|
||||
for (var key in snapcast_groups) {
|
||||
if (snapcast_groups.hasOwnProperty(key)) {
|
||||
groups.push(snapcast_groups[key]);
|
||||
}
|
||||
}
|
||||
if (groups.length <= 0) return null;
|
||||
|
||||
const streams = Object.keys(snapcast_streams).map(
|
||||
(id) => ({ value: id, label: id })
|
||||
(id) => ({ value: id, label: id }),
|
||||
);
|
||||
|
||||
let snapcast_groups = null;
|
||||
if (groups.length > 0) {
|
||||
has_outputs = true;
|
||||
snapcast_groups = (
|
||||
<div>
|
||||
{
|
||||
groups.map(group => (
|
||||
<div className="output-control__item outputs__item--snapcast" key={group.id}>
|
||||
<div className="output-control__item__name">
|
||||
{group.name}
|
||||
</div>
|
||||
<div className="output-control__item__controls">
|
||||
<DropdownField
|
||||
icon="settings_input_component"
|
||||
name="Source"
|
||||
value={group.stream_id}
|
||||
options={streams}
|
||||
noLabel
|
||||
handleChange={(value) => this.props.snapcastActions.setGroupStream(group.id, value)}
|
||||
/>
|
||||
<MuteControl
|
||||
className="output-control__item__mute"
|
||||
noTooltip
|
||||
mute={group.mute}
|
||||
onMuteChange={(mute) => this.props.snapcastActions.setGroupMute(group.id, mute)}
|
||||
/>
|
||||
<VolumeControl
|
||||
className="output-control__item__volume"
|
||||
volume={group.volume}
|
||||
mute={group.mute}
|
||||
onVolumeChange={(percent, previousPercent) => this.props.snapcastActions.setGroupVolume(group.id, percent, previousPercent)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
groups.map((group) => (
|
||||
<div className="output-control__item outputs__item--snapcast" key={group.id}>
|
||||
<div className="output-control__item__name">
|
||||
{group.name}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let local_streaming = null;
|
||||
if (this.props.http_streaming_enabled) {
|
||||
has_outputs = true;
|
||||
local_streaming = (
|
||||
<div className="output-control__item outputs__item--icecast">
|
||||
<div className="output-control__item__name">
|
||||
Local browser
|
||||
</div>
|
||||
<div className="output-control__item__controls">
|
||||
<span className="output-control__item__action" onClick={(e) => this.props.coreActions.cachebustHttpStream()}>
|
||||
<Icon name="refresh" />
|
||||
</span>
|
||||
<VolumeControl
|
||||
className="output-control__item__volume"
|
||||
volume={this.props.http_streaming_volume}
|
||||
mute={this.props.http_streaming_mute}
|
||||
onVolumeChange={(percent) => this.props.coreActions.set({ http_streaming_volume: percent })}
|
||||
onMuteChange={(mute) => this.props.coreActions.set({ http_streaming_mute: mute })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let commands = null;
|
||||
if (this.props.pusher_commands) {
|
||||
let commands_items = [];
|
||||
for (var key in this.props.pusher_commands) {
|
||||
if (this.props.pusher_commands.hasOwnProperty(key)) {
|
||||
commands_items.push(this.props.pusher_commands[key]);
|
||||
<div className="output-control__item__controls">
|
||||
<DropdownField
|
||||
name="Source"
|
||||
value={group.stream_id}
|
||||
icon="settings_input_component"
|
||||
options={streams}
|
||||
noLabel
|
||||
handleChange={(value) => snapcastActions.setGroupStream(group.id, value)}
|
||||
/>
|
||||
<MuteControl
|
||||
className="output-control__item__mute"
|
||||
noTooltip
|
||||
mute={group.mute}
|
||||
onMuteChange={(mute) => snapcastActions.setGroupMute(group.id, mute)}
|
||||
/>
|
||||
<VolumeControl
|
||||
className="output-control__item__volume"
|
||||
volume={group.volume}
|
||||
mute={group.mute}
|
||||
onVolumeChange={(percent, previousPercent) => snapcastActions.setGroupVolume(group.id, percent, previousPercent)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
commands_items = helpers.sortItems(commands_items, 'sort_order');
|
||||
commands() {
|
||||
const {
|
||||
pusher_commands,
|
||||
pusherActions,
|
||||
} = this.props;
|
||||
|
||||
if (commands_items.length > 0) {
|
||||
has_outputs = true;
|
||||
commands = (
|
||||
<div className="output-control__item output-control__item--commands commands">
|
||||
{
|
||||
commands_items.map((command) => (
|
||||
<div
|
||||
key={command.id}
|
||||
className="commands__item commands__item--interactive"
|
||||
onClick={(e) => this.props.pusherActions.runCommand(command.id)}
|
||||
>
|
||||
<Icon className="commands__item__icon" name={command.icon} />
|
||||
<span className={`${command.colour}-background commands__item__background`} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
if (!pusher_commands) return null;
|
||||
|
||||
let items = [];
|
||||
for (var key in pusher_commands) {
|
||||
if (pusher_commands.hasOwnProperty(key)) {
|
||||
items.push(pusher_commands[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_outputs) {
|
||||
if (items.length <= 0) return null;
|
||||
|
||||
items = helpers.sortItems(items, 'sort_order');
|
||||
|
||||
return (
|
||||
<div className="output-control__item output-control__item--commands commands">
|
||||
{
|
||||
items.map((command) => (
|
||||
<div
|
||||
key={command.id}
|
||||
className="commands__item commands__item--interactive"
|
||||
onClick={(e) => pusherActions.runCommand(command.id)}
|
||||
>
|
||||
<Icon className="commands__item__icon" name={command.icon} />
|
||||
<span className={`${command.colour}-background commands__item__background`} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
localStreaming() {
|
||||
const {
|
||||
http_streaming_enabled,
|
||||
http_streaming_volume,
|
||||
http_streaming_mute,
|
||||
coreActions,
|
||||
} = this.props;
|
||||
|
||||
if (!http_streaming_enabled) return null;
|
||||
|
||||
return (
|
||||
<div className="output-control__item outputs__item--icecast">
|
||||
<div className="output-control__item__name">
|
||||
Local browser
|
||||
</div>
|
||||
<div className="output-control__item__controls">
|
||||
<span
|
||||
className="output-control__item__action"
|
||||
onClick={(e) => coreActions.cachebustHttpStream()}
|
||||
>
|
||||
<Icon name="refresh" />
|
||||
</span>
|
||||
<VolumeControl
|
||||
className="output-control__item__volume"
|
||||
volume={http_streaming_volume}
|
||||
mute={http_streaming_mute}
|
||||
onVolumeChange={(percent) => coreActions.set({ http_streaming_volume: percent })}
|
||||
onMuteChange={(mute) => coreActions.set({ http_streaming_mute: mute })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOutputs() {
|
||||
const snapcastGroups = this.snapcastGroups();
|
||||
const localStreaming = this.localStreaming();
|
||||
const commands = this.commands();
|
||||
|
||||
if (!snapcastGroups && !localStreaming && !commands) {
|
||||
return (
|
||||
<div className="output-control__items output-control__items--no-results">
|
||||
<p className="no-results">No outputs</p>
|
||||
@ -177,8 +197,8 @@ class OutputControl extends React.Component {
|
||||
return (
|
||||
<div className="output-control__items">
|
||||
{commands}
|
||||
{local_streaming}
|
||||
{snapcast_groups}
|
||||
{localStreaming}
|
||||
{snapcastGroups}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -20,6 +20,17 @@ class Hotkeys extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
const {
|
||||
play_state,
|
||||
mopidyActions,
|
||||
uiActions,
|
||||
mute,
|
||||
play_time_position,
|
||||
history,
|
||||
modal,
|
||||
dragging,
|
||||
} = this.props;
|
||||
let { volume } = this.props;
|
||||
const key = e.key.toLowerCase();
|
||||
|
||||
// Ignore text input fields
|
||||
@ -41,38 +52,38 @@ class Hotkeys extends React.Component {
|
||||
case ' ':
|
||||
case 'p': // Super-useful once you get used to it. This negates the issue where interactive elements
|
||||
// are in focus (ie slider) and <space> is reserved for that field's interactivity.
|
||||
if (this.props.play_state == 'playing') {
|
||||
this.props.mopidyActions.pause();
|
||||
this.props.uiActions.createNotification({ content: 'pause', type: 'shortcut' });
|
||||
if (play_state == 'playing') {
|
||||
mopidyActions.pause();
|
||||
uiActions.createNotification({ content: 'pause', type: 'shortcut' });
|
||||
} else {
|
||||
this.props.mopidyActions.play();
|
||||
this.props.uiActions.createNotification({ content: 'play_arrow', type: 'shortcut' });
|
||||
mopidyActions.play();
|
||||
uiActions.createNotification({ content: 'play_arrow', type: 'shortcut' });
|
||||
}
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case 'escape':
|
||||
if (this.props.dragging) {
|
||||
this.props.uiActions.dragEnd();
|
||||
if (dragging) {
|
||||
uiActions.dragEnd();
|
||||
prevent = true;
|
||||
} else if (this.props.modal) {
|
||||
window.history.back();
|
||||
} else if (modal) {
|
||||
window.history.back();
|
||||
prevent = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
this.props.history.push('/search');
|
||||
history.push('/search');
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
this.props.history.push('/queue');
|
||||
history.push('/queue');
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
this.props.history.push('/kiosk-mode');
|
||||
history.push('/kiosk-mode');
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
@ -87,80 +98,80 @@ class Hotkeys extends React.Component {
|
||||
break;
|
||||
|
||||
case '=':
|
||||
var { volume } = this.props;
|
||||
if (volume !== 'false') {
|
||||
volume += 5;
|
||||
if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
this.props.mopidyActions.setVolume(volume);
|
||||
if (this.props.mute) {
|
||||
this.props.mopidyActions.setMute(false);
|
||||
mopidyActions.setVolume(volume);
|
||||
if (mute) {
|
||||
mopidyActions.setMute(false);
|
||||
}
|
||||
this.props.uiActions.createNotification({ content: 'volume_up', type: 'shortcut' });
|
||||
uiActions.createNotification({ content: 'volume_up', type: 'shortcut' });
|
||||
}
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case '-':
|
||||
var { volume } = this.props;
|
||||
if (volume !== 'false') {
|
||||
volume -= 5;
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
this.props.mopidyActions.setVolume(volume);
|
||||
if (this.props.mute) {
|
||||
this.props.mopidyActions.setMute(false);
|
||||
mopidyActions.setVolume(volume);
|
||||
if (mute) {
|
||||
mopidyActions.setMute(false);
|
||||
}
|
||||
}
|
||||
this.props.uiActions.createNotification({ content: 'volume_down', type: 'shortcut' });
|
||||
uiActions.createNotification({ content: 'volume_down', type: 'shortcut' });
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
if (this.props.mute) {
|
||||
this.props.mopidyActions.setMute(false);
|
||||
this.props.uiActions.createNotification({ content: 'volume_up', type: 'shortcut' });
|
||||
if (mute) {
|
||||
mopidyActions.setMute(false);
|
||||
uiActions.createNotification({ content: 'volume_up', type: 'shortcut' });
|
||||
} else {
|
||||
this.props.mopidyActions.setMute(true);
|
||||
this.props.uiActions.createNotification({ content: 'volume_off', type: 'shortcut' });
|
||||
mopidyActions.setMute(true);
|
||||
uiActions.createNotification({ content: 'volume_off', type: 'shortcut' });
|
||||
}
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case ';':
|
||||
var new_position = this.props.play_time_position - 30000;
|
||||
var new_position = play_time_position - 30000;
|
||||
if (new_position < 0) {
|
||||
new_position = 0;
|
||||
}
|
||||
this.props.mopidyActions.setTimePosition(new_position);
|
||||
this.props.uiActions.createNotification({ content: 'fast_rewind', type: 'shortcut' });
|
||||
mopidyActions.setTimePosition(new_position);
|
||||
uiActions.createNotification({ content: 'fast_rewind', type: 'shortcut' });
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case "'":
|
||||
this.props.mopidyActions.setTimePosition(this.props.play_time_position + 30000);
|
||||
this.props.uiActions.createNotification({ content: 'fast_forward', type: 'shortcut' });
|
||||
mopidyActions.setTimePosition(play_time_position + 30000);
|
||||
uiActions.createNotification({ content: 'fast_forward', type: 'shortcut' });
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
this.props.mopidyActions.previous();
|
||||
this.props.uiActions.createNotification({ content: 'skip_previous', type: 'shortcut' });
|
||||
mopidyActions.previous();
|
||||
uiActions.createNotification({ content: 'skip_previous', type: 'shortcut' });
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
this.props.mopidyActions.next();
|
||||
this.props.uiActions.createNotification({ content: 'skip_next', type: 'shortcut' });
|
||||
mopidyActions.next();
|
||||
uiActions.createNotification({ content: 'skip_next', type: 'shortcut' });
|
||||
prevent = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (prevent) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +72,21 @@
|
||||
.icon {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__options {
|
||||
top: auto;
|
||||
bottom: 20px;
|
||||
right: auto;
|
||||
left: 0px;
|
||||
|
||||
&:before {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: -3px;
|
||||
left: 9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +130,11 @@
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.dark-theme & {
|
||||
color: colour(white);
|
||||
background: lighten(colour(grey), 4%);
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
|
||||
@ -9,7 +9,14 @@
|
||||
color: colour(white);
|
||||
transform-style: preserve-3d;
|
||||
border-left: 230px solid colour(grey);
|
||||
box-shadow: 0 -1px 8px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.6);
|
||||
|
||||
.light-theme & {
|
||||
background: colour(white);
|
||||
color: colour(darkest_grey);
|
||||
border-color: colour(white);
|
||||
box-shadow: 0 -1px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.current-track {
|
||||
position: absolute;
|
||||
@ -43,7 +50,11 @@
|
||||
|
||||
&__transition {
|
||||
z-index: 2;
|
||||
background: lighten(colour(white), 15%);
|
||||
background: colour(grey);
|
||||
|
||||
.light-theme & {
|
||||
background: lighten(colour(white), 15%);
|
||||
}
|
||||
|
||||
.text {
|
||||
@include animate(0.15s);
|
||||
@ -72,7 +83,7 @@
|
||||
margin-right: 12px;
|
||||
|
||||
&__image {
|
||||
background-color: colour(grey);
|
||||
background-color: colour(middle_grey);
|
||||
}
|
||||
|
||||
&:before {
|
||||
@ -151,6 +162,10 @@
|
||||
background: none;
|
||||
color: colour(white);
|
||||
|
||||
.light-theme & {
|
||||
color: colour(darkest_grey);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0;
|
||||
@ -186,6 +201,10 @@
|
||||
&.play {
|
||||
margin-bottom: -0.8rem;
|
||||
|
||||
.light-theme & {
|
||||
color: colour(turquoise);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user