Context menu on related artists; Starting breadcrumb for Browse

This commit is contained in:
James Barnsley
2020-05-03 19:55:41 +12:00
parent f1b6ada66b
commit 67fc8c4ee0
10 changed files with 358 additions and 252 deletions

View File

@ -136,35 +136,35 @@ export class App extends React.Component {
}
}
componentDidUpdate(prevProps) {
componentDidUpdate({
location: prevLocation,
}) {
const {
location = {},
allow_reporting,
uiActions,
context_menu,
} = this.props;
// When we have navigated to a new route
if (this.props.location !== prevProps.location) {
if (location !== prevLocation) {
// Log our pageview
if (this.props.allow_reporting) {
ReactGA.set({ page: this.props.location.pathname });
ReactGA.pageview(this.props.location.pathname);
if (allow_reporting) {
ReactGA.set({ page: location.pathname });
ReactGA.pageview(location.pathname);
}
// If the location has a "scroll_position" state variable, scroll to it.
// This is invisibly injected to the history by the Link component when navigating, so
// hitting back in the browser allows us to restore the position
const location_state = this.props.location.state
? this.props.location.state
: {};
const location_state = location.state || {};
if (location_state.scroll_position) {
scrollTo(parseInt(location_state.scroll_position), false);
}
// Hide our sidebar
this.props.uiActions.toggleSidebar(false);
// Unselect any tracks
this.props.uiActions.setSelectedTracks([]);
// Close context menu
if (this.props.context_menu) {
this.props.uiActions.hideContextMenu();
}
uiActions.toggleSidebar(false);
uiActions.setSelectedTracks([]);
if (context_menu) uiActions.hideContextMenu();
}
}
@ -177,14 +177,16 @@ export class App extends React.Component {
*
* @param e Event
* */
handleFocusAndBlur(e) {
this.props.uiActions.setWindowFocus(document.hasFocus());
handleFocusAndBlur() {
const { uiActions: { setWindowFocus } } = this.props;
setWindowFocus(document.hasFocus());
}
handleInstallPrompt(e) {
const { uiActions: { installPrompt } } = this.props;
e.preventDefault();
console.log('Install prompt detected');
this.props.uiActions.installPrompt(e);
installPrompt(e);
}
render() {

View File

@ -32,6 +32,9 @@ export default ({
{
...location.state,
scroll_position: main.scrollTop,
previous: {
pathname: location.pathname,
},
},
);
@ -53,7 +56,7 @@ export default ({
return (
<Link
onClick={onClick}
onContextMenu={onContextMenu || null}
onContextMenu={onContextMenu}
className={`${className} ${active}`}
to={to}
>

View File

@ -3,36 +3,55 @@ import React, { memo } from 'react';
import Thumbnail from './Thumbnail';
import URILink from './URILink';
export default memo((props) => {
if (!props.artists) {
return null;
}
export default memo(({ artists, uiActions: { showContextMenu } = {} }) => {
if (!artists) return null;
const onContextMenu = (e, item) => {
if (showContextMenu){
e.preventDefault();
showContextMenu({
e,
context: 'artist',
uris: [item.uri],
items: [item],
});
}
};
return (
<div className="related-artists">
{
props.artists.map((artist, index) => {
let { images } = artist;
if (Array.isArray(images)) {
images = images[0];
}
{artists.map((artist) => {
let { images } = artist;
if (Array.isArray(images)) {
images = images[0];
}
if (artist.uri) {
return (
<URILink type="artist" uri={artist.uri} key={artist.uri} className="related-artists__item related-artists__item--link">
<Thumbnail className="related-artists__item__thumbnail" circle size="small" images={images} />
<span className="related-artists__item__name">{ artist.name }</span>
</URILink>
);
}
return (
<span key={artist.uri} className="related-artists__item">
<Thumbnail className="related-artists__item__thumbnail" circle size="small" images={images} />
<span className="related-artists__item__name">{ artist.name }</span>
</span>
);
})
}
if (artist.uri) {
return (
<URILink
type="artist"
uri={artist.uri}
key={artist.uri}
className="related-artists__item related-artists__item--link"
handleContextMenu={(e) => onContextMenu(e, artist)}
>
<Thumbnail className="related-artists__item__thumbnail" circle size="small" images={images} />
<span className="related-artists__item__name">{ artist.name }</span>
</URILink>
);
}
return (
<span key={artist.uri} className="related-artists__item">
<Thumbnail
className="related-artists__item__thumbnail"
circle
size="small"
images={images}
/>
<span className="related-artists__item__name">{ artist.name }</span>
</span>
);
})}
</div>
);
});
});

View File

@ -13,6 +13,7 @@ import {
formatImages,
formatTrack,
formatTracks,
formatSimpleObject,
getTrackIcon,
} from '../../util/format';
import {
@ -2399,6 +2400,17 @@ const MopidyMiddleware = (function () {
type: 'MOPIDY_DIRECTORY_FLUSH',
});
if (action.uri) {
request(socket, store, 'library.lookup', { uris: [action.uri] })
.then((response) => {
if (!response[action.uri] || !response[action.uri].length) return;
store.dispatch({
type: 'MOPIDY_DIRECTORY_LOADED',
directory: formatSimpleObject(response[action.uri][0]),
});
});
}
request(socket, store, 'library.browse', { uri: action.uri })
.then((response) => {
const tracks_uris = [];

View File

@ -104,7 +104,10 @@ export default function reducer(mopidy = {}, action) {
return { ...mopidy, directory: null };
case 'MOPIDY_DIRECTORY_LOADED':
return { ...mopidy, directory: action.directory };
return {
...mopidy,
directory: { ...mopidy.directory, ...action.directory },
};
/**

View File

@ -117,6 +117,7 @@ class Artist extends React.Component {
}
renderOverview() {
const { uiActions } = this.props;
const artist = collate(
this.props.artist,
{
@ -193,8 +194,19 @@ class Artist extends React.Component {
{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 className="list-wrapper">
<RelatedArtists
artists={artist.related_artists.slice(0, 6)}
uiActions={uiActions}
/>
</div>
<Link
to={`/artist/${encodeURIComponent(this.props.uri)}/related-artists`}
scrollTo="#sub-views-menu"
className="button button--default"
>
All related artists
</Link>
</div>
)}

View File

@ -472,7 +472,7 @@ class Discover extends React.Component {
<div className="col col--w25 others">
<section>
<h4>Artists</h4>
<RelatedArtists artists={artists} />
<RelatedArtists artists={artists} uiActions={this.props.uiActions} />
</section>
<br />
<br />

View File

@ -97,29 +97,14 @@ class LibraryBrowseDirectory extends React.Component {
}
if (parent_uri.startsWith('file://')) {
const uri = parent_uri.replace('file:///', '');
const uri_elements = uri.split('/');
parent_uri = parent_uri.substring(0, parent_uri.lastIndexOf('/'));
return (
<h4 className="breadcrumbs">
{uri_elements.map((uri_element, index) => {
// Reconstruct a URL to this element
let uri = 'file://';
for (let i = 0; i <= index; i++) {
uri += `/${uri_elements[i]}`;
}
return (
<span key={uri}>
{index > 0 && (
<Icon type="fontawesome" name="angle-right" />
)}
<URILink type="browse" uri={uri}>
{decodeURI(uri_element)}
</URILink>
</span>
);
})}
<Icon type="fontawesome" name="angle-left" />
<URILink type="browse" uri={parent_uri}>
{decodeURI(parent_uri)}
</URILink>
</h4>
);
}
@ -156,24 +141,38 @@ class LibraryBrowseDirectory extends React.Component {
);
}
render() {
let title = 'Directory';
const uri_exploded = this.props.uri.split(':');
if (uri_exploded.length > 0) {
title = uri_exploded[0];
title = title.charAt(0).toUpperCase() + title.slice(1);
}
render = () => {
const {
uri,
directory,
load_queue,
uiActions,
view,
} = this.props;
const { limit } = this.state;
if (!this.props.directory || isLoading(this.props.load_queue, ['mopidy_browse'])) {
let title = 'Directory';
if (!directory || isLoading(load_queue, ['mopidy_browse'])) {
return (
<div className="view library-local-view">
<Header icon="music" title={title} uiActions={this.props.uiActions} />
<Header icon="music" title={title} uiActions={uiActions} />
<Loader body loading />
</div>
);
}
let subdirectories = (this.props.directory.subdirectories && this.props.directory.subdirectories.length > 0 ? this.props.directory.subdirectories : null);
if (directory.name) {
title = directory.name;
} else {
const uri_exploded = uri.split(':');
if (uri_exploded.length > 0) {
title = uri_exploded[0];
title = title.charAt(0).toUpperCase() + title.slice(1);
}
};
let subdirectories = (directory.subdirectories && directory.subdirectories.length > 0 ? directory.subdirectories : null);
subdirectories = sortItems(subdirectories, 'name');
const total_items = (tracks ? tracks.length : 0) + (subdirectories ? subdirectories.length : 0);
@ -182,7 +181,7 @@ class LibraryBrowseDirectory extends React.Component {
let tracks = null;
let limit_remaining = this.state.limit - subdirectories;
if (limit_remaining > 0) {
all_tracks = (this.props.directory.tracks && this.props.directory.tracks.length > 0 ? this.props.directory.tracks : null);
all_tracks = (directory.tracks && directory.tracks.length > 0 ? directory.tracks : null);
all_tracks = sortItems(all_tracks, 'name');
tracks = all_tracks.slice(0, limit_remaining);
}
@ -203,18 +202,18 @@ class LibraryBrowseDirectory extends React.Component {
<DropdownField
icon="visibility"
name="View"
value={this.props.view}
value={view}
valueAsLabel
options={view_options}
handleChange={(value) => { this.props.uiActions.set({ library_directory_view: value }); this.props.uiActions.hideContextMenu(); }}
handleChange={(value) => { uiActions.set({ library_directory_view: value }); uiActions.hideContextMenu(); }}
/>
{tracks && (
<a className="button button--no-hover" onClick={(e) => { this.props.uiActions.hideContextMenu(); this.playAll(e, all_tracks); }}>
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.playAll(e, all_tracks); }}>
<Icon name="play_circle_filled" />
Play all
</a>
)}
<a className="button button--no-hover" onClick={(e) => { this.props.uiActions.hideContextMenu(); this.goBack(e); }}>
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.goBack(e); }}>
<Icon name="keyboard_backspace" />
Back
</a>
@ -223,7 +222,7 @@ class LibraryBrowseDirectory extends React.Component {
return (
<div className="view library-local-view">
<Header options={options} uiActions={this.props.uiActions}>
<Header options={options} uiActions={uiActions}>
<Icon name="folder" type="material" />
{title}
</Header>
@ -237,14 +236,14 @@ class LibraryBrowseDirectory extends React.Component {
{tracks && (
<TrackList
tracks={tracks}
uri={`iris:browse:${this.props.uri}`}
uri={`iris:browse:${uri}`}
className="library-local-track-list"
/>
)}
<LazyLoadListener
loadKey={total_items > this.state.limit ? this.state.limit : total_items}
showLoader={this.state.limit < total_items}
loadKey={total_items > limit ? limit : total_items}
showLoader={limit < total_items}
loadMore={() => this.loadMore()}
/>