Better UX on dropdown field; Adding search added_from in queue
This commit is contained in:
@ -1,11 +1,10 @@
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as helpers from '../../helpers';
|
||||
|
||||
import Icon from '../Icon';
|
||||
|
||||
export default class DropdownField extends React.Component {
|
||||
export default class DropdownField extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@ -18,6 +17,7 @@ export default class DropdownField extends React.Component {
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
changed: false,
|
||||
};
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
@ -33,11 +33,14 @@ export default class DropdownField extends React.Component {
|
||||
|
||||
setExpanded(expanded = !this.state.expanded) {
|
||||
if (expanded) {
|
||||
this.setState({ expanded });
|
||||
this.setState({ expanded, changed: false, });
|
||||
window.addEventListener('click', this.handleClick, false);
|
||||
} else {
|
||||
this.setState({ expanded });
|
||||
window.removeEventListener('click', this.handleClick, false);
|
||||
if (this.props.onClose && this.state.changed) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,8 +51,10 @@ export default class DropdownField extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(value, is_selected) {
|
||||
handleChange(value, is_selected) {
|
||||
const current_value = this.props.value;
|
||||
this.setState({ changed: true });
|
||||
|
||||
if (this.isMultiSelect()) {
|
||||
if (value == 'select-all') {
|
||||
var new_value = [];
|
||||
@ -64,6 +69,7 @@ export default class DropdownField extends React.Component {
|
||||
current_value.push(value);
|
||||
var new_value = current_value;
|
||||
}
|
||||
new_value = helpers.removeDuplicates(new_value);
|
||||
} else {
|
||||
var new_value = value;
|
||||
|
||||
@ -78,40 +84,55 @@ export default class DropdownField extends React.Component {
|
||||
return this.props.value instanceof Array;
|
||||
}
|
||||
|
||||
selectedOptions() {
|
||||
const {
|
||||
options: optionsProp,
|
||||
value,
|
||||
} = this.props;
|
||||
let selectedOptions = [];
|
||||
|
||||
if (optionsProp) {
|
||||
// Value not set, default to first option
|
||||
if (value === null || value === undefined) {
|
||||
selectedOptions = [optionsProp[0]];
|
||||
} else {
|
||||
if (this.isMultiSelect()) {
|
||||
for (let multiSelectValue of value) {
|
||||
selectedOptions = [
|
||||
...selectedOptions,
|
||||
...optionsProp.filter(option => option.value === multiSelectValue),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
selectedOptions = optionsProp.filter(option => option.value === value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedOptions;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.options) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
options: optionsProp,
|
||||
no_status_icon,
|
||||
no_label,
|
||||
button,
|
||||
className: classNameProp,
|
||||
name,
|
||||
value,
|
||||
icon,
|
||||
icon_type,
|
||||
selected_icon: selectedIconProp,
|
||||
valueAsLabel,
|
||||
} = this.props;
|
||||
const { expanded } = this.state;
|
||||
|
||||
let className = 'dropdown-field';
|
||||
if (this.state.expanded) {
|
||||
className += ' expanded';
|
||||
}
|
||||
if (this.props.no_status_icon) {
|
||||
className += ' no-status-icon';
|
||||
}
|
||||
if (this.props.no_label) {
|
||||
className += ' no-label';
|
||||
}
|
||||
if (this.props.button) {
|
||||
className += ' buttonify';
|
||||
}
|
||||
if (this.props.className) {
|
||||
className += ` ${this.props.className}`;
|
||||
}
|
||||
let current_value = null;
|
||||
if (this.props.value !== undefined) {
|
||||
current_value = this.props.value;
|
||||
} else if (this.props.options.length > 0) {
|
||||
current_value = this.props.options[0].value;
|
||||
}
|
||||
if (!optionsProp) return null;
|
||||
|
||||
let selected_icon = <Icon name="check" />;
|
||||
if (this.props.selected_icon) {
|
||||
selected_icon = <Icon name={this.props.selected_icon} />;
|
||||
}
|
||||
const selectedOptions = this.selectedOptions();
|
||||
|
||||
const options = Object.assign([], this.props.options);
|
||||
const options = Object.assign([], optionsProp);
|
||||
if (this.isMultiSelect()) {
|
||||
options.push({
|
||||
value: 'select-all',
|
||||
@ -120,29 +141,45 @@ export default class DropdownField extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
let className = `dropdown-field ${classNameProp}`;
|
||||
if (expanded) className += ' dropdown-field--expanded';
|
||||
if (no_status_icon) className += ' dropdown-field--no-status-icon';
|
||||
if (no_label) className += ' dropdown-field--no-label';
|
||||
if (button) className += ' dropdown-field--buttonify';
|
||||
|
||||
let selected_icon = <Icon name="check" />;
|
||||
if (selectedIconProp) {
|
||||
selected_icon = <Icon name={selectedIconProp} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} data-uid={this.uid}>
|
||||
<div className={`label${this.props.button ? ` button ${this.props.button}` : ''}`} onClick={(e) => this.setExpanded()}>
|
||||
{this.props.icon ? <Icon name={this.props.icon} type={this.props.icon_type ? this.props.icon_type : 'material'} /> : null}
|
||||
<div className={`dropdown-field__label${button ? ` button ${button}` : ''}`} onClick={(e) => this.setExpanded()}>
|
||||
{icon ? <Icon name={icon} type={icon_type ? icon_type : 'material'} /> : null}
|
||||
<span className="text">
|
||||
{this.props.name}
|
||||
{this.isMultiSelect() ? ` (${current_value.length})` : null}
|
||||
<span className="dropdown-field__label__value">
|
||||
{valueAsLabel && selectedOptions.length === 1 ? selectedOptions[0].label : name}
|
||||
</span>
|
||||
<span className="dropdown-field__label__name">
|
||||
{name}
|
||||
</span>
|
||||
{this.isMultiSelect() ? ` (${selectedOptions.length})` : null}
|
||||
</span>
|
||||
</div>
|
||||
<div className="options">
|
||||
<div className="liner">
|
||||
<div className="dropdown-field__options">
|
||||
<div className="dropdown-field__options__liner">
|
||||
{
|
||||
options.map((option) => {
|
||||
if (this.isMultiSelect()) {
|
||||
var is_selected = current_value.indexOf(option.value) > -1;
|
||||
} else {
|
||||
var is_selected = current_value === option.value;
|
||||
}
|
||||
const is_selected = selectedOptions.includes(option);
|
||||
return (
|
||||
<div className={`option ${option.className ? option.className : ''}`} key={option.value} onClick={(e) => this.handleChange(option.value, is_selected)}>
|
||||
{!this.props.no_status_icon && is_selected ? selected_icon : null}
|
||||
{option.label}
|
||||
</div>
|
||||
<div
|
||||
className={`dropdown-field__options__item ${option.className ? option.className : ''}`}
|
||||
key={option.value}
|
||||
onClick={(e) => this.handleChange(option.value, is_selected)}
|
||||
>
|
||||
{!no_status_icon && is_selected && selected_icon}
|
||||
{option.label}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
@ -151,4 +188,4 @@ export default class DropdownField extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import React from 'react';
|
||||
import Icon from './Icon';
|
||||
import ContextMenuTrigger from './ContextMenuTrigger';
|
||||
|
||||
export default class Header extends React.Component {
|
||||
export default class Header extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@ -13,7 +13,7 @@ export default class Header extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
handleContextMenuTrigger(e, options) {
|
||||
handleContextMenuTrigger(e, options) {
|
||||
// We have an override trigger (eg Album, Playlist)
|
||||
if (this.props.handleContextMenuTrigger) {
|
||||
return this.props.handleContextMenuTrigger(e);
|
||||
@ -25,10 +25,10 @@ export default class Header extends React.Component {
|
||||
title: this.props.title,
|
||||
options,
|
||||
};
|
||||
this.props.uiActions.showContextMenu(data);
|
||||
this.props.uiActions.showContextMenu(data);
|
||||
}
|
||||
|
||||
renderContextMenuTrigger() {
|
||||
renderContextMenuTrigger() {
|
||||
// No custom trigger, nor any options
|
||||
if (!this.props.handleContextMenuTrigger && !this.props.options) {
|
||||
return null;
|
||||
@ -43,13 +43,11 @@ export default class Header extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="options">
|
||||
<div className="header__options">
|
||||
{this.renderContextMenuTrigger()}
|
||||
<span className="items">
|
||||
<span className="liner">
|
||||
{this.props.options ? this.props.options : null}
|
||||
</span>
|
||||
</span>
|
||||
<div className="header__options__wrapper">
|
||||
{this.props.options || null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -64,4 +62,4 @@ export default class Header extends React.Component {
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,11 +223,11 @@ export default class Track extends React.Component {
|
||||
break;
|
||||
|
||||
case 'browse':
|
||||
var link = <URILink type="browse" uri={track.added_from.replace('iris:browse:', '')}>Browse</URILink>;
|
||||
var link = <URILink uri={track.added_from}>Browse</URILink>;
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
var link = <URILink type="search" uri={track.added_from.replace('iris:', '')}>Search</URILink>;
|
||||
var link = <URILink uri={track.added_from}>Search</URILink>;
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
|
||||
@ -42,7 +42,7 @@ export default memo((props) => {
|
||||
|
||||
case 'search':
|
||||
var exploded = uri.split('%3A');
|
||||
to = `/search/${exploded[1]}/${exploded[2]}`;
|
||||
to = `/search/${exploded[2]}/${exploded[3]}`;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@ -1027,15 +1027,7 @@ export const uriType = function (uri) {
|
||||
}
|
||||
|
||||
if (exploded[0] === 'iris') {
|
||||
switch (exploded[1]) {
|
||||
case 'search':
|
||||
case 'discover':
|
||||
case 'browse':
|
||||
case 'radio':
|
||||
return exploded[1];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return exploded[1];
|
||||
}
|
||||
|
||||
switch (exploded[1]) {
|
||||
@ -1153,14 +1145,14 @@ export const getFromUri = function (element, uri = '') {
|
||||
break;
|
||||
|
||||
case 'searchtype':
|
||||
if (exploded[0] == 'search') {
|
||||
return exploded[1];
|
||||
if (exploded[1] == 'search') {
|
||||
return exploded[2];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'searchterm':
|
||||
if (exploded[0] == 'search') {
|
||||
return exploded[2];
|
||||
if (exploded[1] == 'search') {
|
||||
return exploded[3];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import * as helpers from '../../helpers';
|
||||
|
||||
export default function reducer(core = {}, action) {
|
||||
switch (action.type) {
|
||||
switch (action.type) {
|
||||
case 'CORE_SET':
|
||||
return { ...core, ...action.data };
|
||||
|
||||
@ -14,17 +14,17 @@ export default function reducer(core = {}, action) {
|
||||
* */
|
||||
|
||||
case 'CURRENT_TRACK_LOADED':
|
||||
return {
|
||||
...core,
|
||||
return {
|
||||
...core,
|
||||
current_track: action.track,
|
||||
current_track_uri: action.uri,
|
||||
current_track_uri: action.uri,
|
||||
};
|
||||
|
||||
case 'CLEAR_CURRENT_TRACK':
|
||||
return {
|
||||
...core,
|
||||
return {
|
||||
...core,
|
||||
current_track: null,
|
||||
current_track_uri: null,
|
||||
current_track_uri: null,
|
||||
};
|
||||
|
||||
case 'NEXT_TRACK_LOADED':
|
||||
@ -36,7 +36,7 @@ export default function reducer(core = {}, action) {
|
||||
case 'PUSHER_QUEUE_METADATA':
|
||||
case 'PUSHER_QUEUE_METADATA_CHANGED':
|
||||
var tracklist = Object.assign([], core.current_tracklist);
|
||||
for (let i = 0; i < tracklist.length; i++) {
|
||||
for (let i = 0; i < tracklist.length; i++) {
|
||||
// load our metadata (if we have any for that tlid)
|
||||
if (action.queue_metadata[`tlid_${tracklist[i].tlid}`] !== undefined) {
|
||||
tracklist[i] = {
|
||||
@ -183,10 +183,10 @@ export default function reducer(core = {}, action) {
|
||||
|
||||
library_playlists = helpers.removeDuplicates(library_playlists);
|
||||
|
||||
return {
|
||||
...core,
|
||||
return {
|
||||
...core,
|
||||
library_playlists,
|
||||
library_playlists_started: true,
|
||||
library_playlists_started: true,
|
||||
};
|
||||
|
||||
|
||||
@ -203,14 +203,14 @@ export default function reducer(core = {}, action) {
|
||||
* */
|
||||
|
||||
case 'SEARCH_STARTED':
|
||||
return {
|
||||
...core,
|
||||
return {
|
||||
...core,
|
||||
search_results: {
|
||||
artists_uris: [],
|
||||
albums_uris: [],
|
||||
playlists_uris: [],
|
||||
tracks: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case 'SEARCH_RESULTS_LOADED':
|
||||
@ -270,8 +270,8 @@ export default function reducer(core = {}, action) {
|
||||
else if (core.search_results && core.search_results.tracks_more) var { tracks_more } = core.search_results;
|
||||
else var tracks_more = null;
|
||||
|
||||
return {
|
||||
...core,
|
||||
return {
|
||||
...core,
|
||||
search_results: {
|
||||
artists_more,
|
||||
artists_uris: helpers.removeDuplicates(artists_uris),
|
||||
@ -281,7 +281,7 @@ export default function reducer(core = {}, action) {
|
||||
playlists_uris: helpers.removeDuplicates(playlists_uris),
|
||||
tracks,
|
||||
tracks_more,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -180,24 +180,25 @@ class Artist extends React.Component {
|
||||
|
||||
<div className="col col--w5" />
|
||||
|
||||
{artist.related_artists && artist.related_artists.length > 0 ? (
|
||||
{artist.related_artists && artist.related_artists.length > 0 && (
|
||||
<div className="col col--w25 related-artists">
|
||||
<h4>Related artists</h4>
|
||||
<div className="list-wrapper"><RelatedArtists artists={artist.related_artists.slice(0, 6)} /></div>
|
||||
<Link to={`/artist/${encodeURIComponent(this.props.uri)}/related-artists`} scrollTo="#sub-views-menu" className="button button--default">All related artists</Link>
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
|
||||
<div className="cf" />
|
||||
|
||||
{artist.albums ? (
|
||||
{artist.albums && (
|
||||
<div className="albums">
|
||||
<h4>
|
||||
<div>Albums</div>
|
||||
<DropdownField
|
||||
icon="sort"
|
||||
icon="swap_vert"
|
||||
name="Sort"
|
||||
value={this.props.sort}
|
||||
valueAsLabel
|
||||
options={sort_options}
|
||||
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
||||
handleChange={(value) => { this.setSort(value); this.props.uiActions.hideContextMenu(); }}
|
||||
@ -206,6 +207,7 @@ class Artist extends React.Component {
|
||||
icon="filter_list"
|
||||
name="Filter"
|
||||
value={this.props.filter}
|
||||
valueAsLabel
|
||||
options={filter_options}
|
||||
handleChange={(value) => { this.setFilter(value); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
@ -226,7 +228,7 @@ class Artist extends React.Component {
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -144,37 +144,50 @@ class Queue extends React.Component {
|
||||
const items = [];
|
||||
|
||||
// Radio nests it's seed URIs in an encoded URI format
|
||||
if (uri_type === 'radio') {
|
||||
const radio_seeds = helpers.getFromUri('seeds', added_from_uri);
|
||||
|
||||
for (let seed of radio_seeds) {
|
||||
let item_type = helpers.uriType(seed);
|
||||
let item_library = this.props[`${item_type}s`];
|
||||
if (item_library && item_library[seed]) {
|
||||
items.push(item_library[seed]);
|
||||
switch (uri_type){
|
||||
case 'radio':
|
||||
const radio_seeds = helpers.getFromUri('seeds', added_from_uri);
|
||||
|
||||
for (let seed of radio_seeds) {
|
||||
let item_type = helpers.uriType(seed);
|
||||
let item_library = this.props[`${item_type}s`];
|
||||
if (item_library && item_library[seed]) {
|
||||
items.push(item_library[seed]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const item_library = this.props[`${uri_type}s`];
|
||||
if (item_library && item_library[added_from_uri]) {
|
||||
items.push(item_library[added_from_uri]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
items.push({
|
||||
uri: added_from_uri,
|
||||
name: `"${helpers.getFromUri('searchterm', added_from_uri)}" search`,
|
||||
})
|
||||
break;
|
||||
|
||||
default:
|
||||
const item_library = this.props[`${uri_type}s`];
|
||||
if (item_library && item_library[added_from_uri]) {
|
||||
items.push(item_library[added_from_uri]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (items.length <= 0) return null;
|
||||
|
||||
return (
|
||||
<div className="current-track__added-from">
|
||||
<URILink
|
||||
uri={items[0].uri}
|
||||
className="current-track__added-from__thumbnail"
|
||||
>
|
||||
<Thumbnail
|
||||
images={items[0].images}
|
||||
size="small"
|
||||
circle={helpers.uriType(items[0].uri) === 'artist'}
|
||||
/>
|
||||
</URILink>
|
||||
{items[0].images && (
|
||||
<URILink
|
||||
uri={items[0].uri}
|
||||
className="current-track__added-from__thumbnail"
|
||||
>
|
||||
<Thumbnail
|
||||
images={items[0].images}
|
||||
size="small"
|
||||
circle={helpers.uriType(items[0].uri) === 'artist'}
|
||||
/>
|
||||
</URILink>
|
||||
)}
|
||||
<div className="current-track__added-from__text">
|
||||
{'Playing from '}
|
||||
<LinksSentence items={items} />
|
||||
@ -299,26 +312,31 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
if (state.core.queue && state.core.tracks) {
|
||||
for (const queue_track of state.core.queue) {
|
||||
let track = { ...queue_track };
|
||||
let track = {
|
||||
...queue_track,
|
||||
playing: current_track && current_track.tlid == queue_track.tlid,
|
||||
};
|
||||
|
||||
// If we have the track in our index, merge it in.
|
||||
// We prioritise queue track over index track as queue has unique data, like which track
|
||||
// is playing and tlids.
|
||||
if (state.core.tracks.hasOwnProperty(track.uri)) {
|
||||
track = { ...state.core.tracks[track.uri], ...track, playing: current_track && current_track.tlid == track.tlid };
|
||||
track = {
|
||||
...state.core.tracks[track.uri],
|
||||
...track,
|
||||
};
|
||||
}
|
||||
|
||||
// Now merge in our queue metadata
|
||||
if (state.core.queue_metadata[`tlid_${track.tlid}`] !== undefined) {
|
||||
track = {
|
||||
|
||||
...track,
|
||||
...state.core.queue_metadata[`tlid_${track.tlid}`],
|
||||
};
|
||||
}
|
||||
|
||||
// Siphon off this track if it's a full representation of our current track (by tlid)
|
||||
if (current_track && current_track.uri == track.uri) {
|
||||
// Siphon off this track if it's a full representation of our current track
|
||||
if (track.playing) {
|
||||
current_track = track;
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ import * as uiActions from '../services/ui/actions';
|
||||
import * as mopidyActions from '../services/mopidy/actions';
|
||||
import * as spotifyActions from '../services/spotify/actions';
|
||||
|
||||
class Search extends React.Component {
|
||||
class Search extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@ -43,7 +43,7 @@ class Search extends React.Component {
|
||||
this.digestUri();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// Query changed
|
||||
if (nextProps.term !== this.props.term || nextProps.type !== this.props.type) {
|
||||
this.digestUri(nextProps);
|
||||
@ -90,10 +90,10 @@ class Search extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
search(type = this.state.type, term = this.state.term, provider) {
|
||||
search(type = this.state.type, term = this.state.term, provider) {
|
||||
this.props.uiActions.setWindowTitle(`Search: ${term}`);
|
||||
|
||||
if (type && term) {
|
||||
if (type && term) {
|
||||
if (provider == 'mopidy' || (this.props.mopidy_connected && this.props.uri_schemes_search_enabled)) {
|
||||
if (this.props.mopidy_search_results.query === undefined || this.props.mopidy_search_results.query != term) {
|
||||
this.props.mopidyActions.clearSearchResults();
|
||||
@ -126,6 +126,11 @@ class Search extends React.Component {
|
||||
this.props.uiActions.set(data);
|
||||
}
|
||||
|
||||
handleSourceChange(value) {
|
||||
this.props.uiActions.set({ uri_schemes_search_enabled: value });
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
renderArtists(artists, spotify_search_enabled) {
|
||||
return (
|
||||
<div>
|
||||
@ -133,9 +138,8 @@ class Search extends React.Component {
|
||||
<URILink type="search" uri={`search:all:${this.state.term}`}>
|
||||
Search
|
||||
</URILink>
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
|
||||
Artists
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
{` Artists`}
|
||||
</h4>
|
||||
<section className="grid-wrapper">
|
||||
<ArtistGrid artists={artists} show_source_icon />
|
||||
@ -150,11 +154,10 @@ class Search extends React.Component {
|
||||
<div>
|
||||
<h4>
|
||||
<URILink type="search" uri={`search:all:${this.state.term}`}>
|
||||
Search
|
||||
{`Search `}
|
||||
</URILink>
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
|
||||
Albums
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
{` Albums`}
|
||||
</h4>
|
||||
<section className="grid-wrapper">
|
||||
<AlbumGrid albums={albums} show_source_icon />
|
||||
@ -169,11 +172,10 @@ class Search extends React.Component {
|
||||
<div>
|
||||
<h4>
|
||||
<URILink type="search" uri={`search:all:${this.state.term}`}>
|
||||
Search
|
||||
{`Search `}
|
||||
</URILink>
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
|
||||
Playlists
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
{` Playlists`}
|
||||
</h4>
|
||||
<section className="grid-wrapper">
|
||||
<PlaylistGrid playlists={playlists} show_source_icon />
|
||||
@ -188,11 +190,10 @@ class Search extends React.Component {
|
||||
<div>
|
||||
<h4>
|
||||
<URILink type="search" uri={`search:all:${this.state.term}`}>
|
||||
Search
|
||||
{`Search `}
|
||||
</URILink>
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
|
||||
Tracks
|
||||
<Icon type="fontawesome" name="angle-right" />
|
||||
{` Tracks`}
|
||||
</h4>
|
||||
<section className="list-wrapper">
|
||||
<TrackList tracks={tracks} uri={`iris:search:${this.state.type}:${this.state.term}`} show_source_icon />
|
||||
@ -211,13 +212,11 @@ class Search extends React.Component {
|
||||
<h4>Artists</h4>
|
||||
</URILink>
|
||||
<ArtistGrid mini show_source_icon artists={artists.slice(0, 6)} />
|
||||
{artists.length >= 6 ? (
|
||||
{artists.length >= 6 && (
|
||||
<URILink type="search" uri={`search:artist:${this.state.term}`} className="button button--default">
|
||||
All artists (
|
||||
{artists.length}
|
||||
)
|
||||
</URILink>
|
||||
) : null}
|
||||
{`All artists (${artists.length})`}
|
||||
</URILink>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
@ -233,13 +232,11 @@ class Search extends React.Component {
|
||||
<h4>Albums</h4>
|
||||
</URILink>
|
||||
<AlbumGrid mini show_source_icon albums={albums.slice(0, 6)} />
|
||||
{albums.length >= 6 ? (
|
||||
{albums.length >= 6 && (
|
||||
<URILink type="search" uri={`search:album:${this.state.term}`} className="button button--default">
|
||||
All albums (
|
||||
{albums.length}
|
||||
)
|
||||
</URILink>
|
||||
) : null}
|
||||
{`All albums (${albums.length})`}
|
||||
</URILink>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
@ -255,13 +252,11 @@ class Search extends React.Component {
|
||||
<h4>Playlists</h4>
|
||||
</URILink>
|
||||
<PlaylistGrid mini show_source_icon playlists={playlists.slice(0, 6)} />
|
||||
{playlists.length >= 6 ? (
|
||||
{playlists.length >= 6 && (
|
||||
<URILink type="search" uri={`search:playlist:${this.state.term}`} className="button button--default">
|
||||
All playlists (
|
||||
{playlists.length}
|
||||
)
|
||||
</URILink>
|
||||
) : null}
|
||||
{`All playlists (${playlists.length})`}
|
||||
</URILink>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
@ -381,19 +376,25 @@ class Search extends React.Component {
|
||||
const options = (
|
||||
<span>
|
||||
<DropdownField
|
||||
icon="sort"
|
||||
icon="swap_vert"
|
||||
name="Sort"
|
||||
value={this.props.sort}
|
||||
valueAsLabel
|
||||
options={sort_options}
|
||||
selected_icon={this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down'}
|
||||
handleChange={(value) => { this.setSort(value); this.props.uiActions.hideContextMenu(); }}
|
||||
handleChange={(value) => { this.setSort(value); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
<DropdownField
|
||||
icon="cloud"
|
||||
name="Sources"
|
||||
value={this.props.uri_schemes_search_enabled}
|
||||
options={provider_options}
|
||||
handleChange={(value) => { this.props.uiActions.set({ uri_schemes_search_enabled: value }); this.props.uiActions.hideContextMenu(); }}
|
||||
handleChange={(value) => this.handleSourceChange(value)}
|
||||
onClose={() => {
|
||||
this.props.spotifyActions.clearSearchResults();
|
||||
this.props.mopidyActions.clearSearchResults();
|
||||
this.search()
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
@ -407,7 +408,7 @@ class Search extends React.Component {
|
||||
<SearchForm
|
||||
history={this.props.history}
|
||||
term={this.state.term}
|
||||
onSubmit={(term) => this.handleSubmit(term)}
|
||||
onSubmit={(term) => this.handleSubmit(term)}
|
||||
/>
|
||||
|
||||
<div className="content-wrapper">
|
||||
@ -464,4 +465,4 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Search);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Search);
|
||||
|
||||
@ -277,7 +277,7 @@ class LibraryAlbums extends React.Component {
|
||||
const sort_options = [
|
||||
{
|
||||
value: null,
|
||||
label: 'Default',
|
||||
label: 'As loaded',
|
||||
},
|
||||
{
|
||||
value: 'name',
|
||||
@ -302,16 +302,17 @@ class LibraryAlbums extends React.Component {
|
||||
];
|
||||
|
||||
const options = (
|
||||
<span>
|
||||
<div className="header__options__wrapper">
|
||||
<FilterField
|
||||
initialValue={this.state.filter}
|
||||
handleChange={(value) => this.setState({ filter: value, limit: this.state.per_page })}
|
||||
onSubmit={e => this.props.uiActions.hideContextMenu()}
|
||||
/>
|
||||
<DropdownField
|
||||
icon="sort"
|
||||
icon="swap_vert"
|
||||
name="Sort"
|
||||
value={this.props.sort}
|
||||
valueAsLabel
|
||||
options={sort_options}
|
||||
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
||||
handleChange={(val) => { this.setSort(val); this.props.uiActions.hideContextMenu(); }}
|
||||
@ -320,6 +321,7 @@ class LibraryAlbums extends React.Component {
|
||||
icon="visibility"
|
||||
name="View"
|
||||
value={this.props.view}
|
||||
valueAsLabel
|
||||
options={view_options}
|
||||
handleChange={(val) => { this.props.uiActions.set({ library_albums_view: val }); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
@ -327,10 +329,11 @@ class LibraryAlbums extends React.Component {
|
||||
icon="cloud"
|
||||
name="Source"
|
||||
value={this.props.source}
|
||||
valueAsLabel
|
||||
options={source_options}
|
||||
handleChange={(val) => { this.props.uiActions.set({ library_albums_source: val }); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -257,20 +257,20 @@ class LibraryArtists extends React.Component {
|
||||
|
||||
const sort_options = [
|
||||
{
|
||||
label: 'Default',
|
||||
value: null,
|
||||
label: 'As loaded',
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
value: 'name',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
label: 'Followers',
|
||||
value: 'followers',
|
||||
label: 'Followers',
|
||||
},
|
||||
{
|
||||
label: 'Popularity',
|
||||
value: 'popularity',
|
||||
label: 'Popularity',
|
||||
},
|
||||
];
|
||||
|
||||
@ -282,9 +282,10 @@ class LibraryArtists extends React.Component {
|
||||
onSubmit={e => this.props.uiActions.hideContextMenu()}
|
||||
/>
|
||||
<DropdownField
|
||||
icon="sort"
|
||||
icon="swap_vert"
|
||||
name="Sort"
|
||||
value={this.props.sort}
|
||||
valueAsLabel
|
||||
options={sort_options}
|
||||
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
||||
handleChange={(value) => { this.setSort(value); this.props.uiActions.hideContextMenu(); }}
|
||||
@ -293,6 +294,7 @@ class LibraryArtists extends React.Component {
|
||||
icon="visibility"
|
||||
name="View"
|
||||
value={this.props.view}
|
||||
valueAsLabel
|
||||
options={view_options}
|
||||
handleChange={(value) => { this.props.uiActions.set({ library_artists_view: value }); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
@ -300,6 +302,7 @@ class LibraryArtists extends React.Component {
|
||||
icon="cloud"
|
||||
name="Source"
|
||||
value={this.props.source}
|
||||
valueAsLabel
|
||||
options={source_options}
|
||||
handleChange={(value) => { this.props.uiActions.set({ library_artists_source: value }); this.props.uiActions.hideContextMenu(); }}
|
||||
/>
|
||||
|
||||
@ -208,7 +208,7 @@ class LibraryPlaylists extends React.Component {
|
||||
const sort_options = [
|
||||
{
|
||||
value: null,
|
||||
label: 'Default',
|
||||
label: 'As loaded',
|
||||
},
|
||||
{
|
||||
value: 'name',
|
||||
@ -240,9 +240,10 @@ class LibraryPlaylists extends React.Component {
|
||||
onSubmit={e => this.props.uiActions.hideContextMenu()}
|
||||
/>
|
||||
<DropdownField
|
||||
icon="sort"
|
||||
icon="swap_vert"
|
||||
name="Sort"
|
||||
value={this.props.sort}
|
||||
valueAsLabel
|
||||
options={sort_options}
|
||||
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
||||
handleChange={(value) => { this.setSort(value); this.props.uiActions.hideContextMenu(); }}
|
||||
@ -250,6 +251,7 @@ class LibraryPlaylists extends React.Component {
|
||||
<DropdownField
|
||||
icon="visibility"
|
||||
name="View"
|
||||
valueAsLabel
|
||||
value={this.props.view}
|
||||
options={view_options}
|
||||
handleChange={(value) => { this.props.uiActions.set({ library_playlists_view: value }); this.props.uiActions.hideContextMenu(); }}
|
||||
@ -257,6 +259,7 @@ class LibraryPlaylists extends React.Component {
|
||||
<DropdownField
|
||||
icon="cloud"
|
||||
name="Source"
|
||||
valueAsLabel
|
||||
value={this.props.source}
|
||||
options={source_options}
|
||||
handleChange={(value) => { this.props.uiActions.set({ library_playlists_source: value }); this.props.uiActions.hideContextMenu(); }}
|
||||
|
||||
Reference in New Issue
Block a user