All views done this time; Language file mapping for modals.. Will need to do destructuring in second sweep

This commit is contained in:
James Barnsley
2020-07-19 20:41:14 +12:00
parent 9f186d281c
commit 2de98afee4
21 changed files with 1807 additions and 1330 deletions

View File

@ -19,6 +19,7 @@ import {
} from '../../util/helpers';
import { sortItems, applyFilter } from '../../util/arrays';
import { collate } from '../../util/format';
import { i18n, I18n } from '../../locale';
class LibraryAlbums extends React.Component {
constructor(props) {
@ -32,26 +33,44 @@ class LibraryAlbums extends React.Component {
}
componentDidMount() {
const {
location: {
state = {},
},
uiActions: {
setWindowTitle,
},
mopidy_connected,
mopidy_library_albums_status,
source,
mopidyActions,
spotifyActions,
googleActions,
google_available,
google_library_albums_status,
spotify_available,
spotify_library_albums_status,
} = this.props;
// Restore any limit defined in our location state
const state = (this.props.location.state ? this.props.location.state : {});
if (state.limit) {
this.setState({
limit: state.limit,
});
}
this.props.uiActions.setWindowTitle('Albums');
setWindowTitle(i18n('library.albums.title'));
if (this.props.mopidy_connected && this.props.mopidy_library_albums_status != 'finished' && this.props.mopidy_library_albums_status != 'started' && (this.props.source == 'all' || this.props.source == 'local')) {
this.props.mopidyActions.getLibraryAlbums();
if (mopidy_connected && mopidy_library_albums_status !== 'finished' && mopidy_library_albums_status !== 'started' && (source === 'all' || source === 'local')) {
mopidyActions.getLibraryAlbums();
}
if (this.props.google_available && this.props.google_library_albums_status != 'finished' && this.props.google_library_albums_status != 'started' && (this.props.source == 'all' || this.props.source == 'google')) {
this.props.googleActions.getLibraryAlbums();
if (google_available && google_library_albums_status !== 'finished' && google_library_albums_status !== 'started' && (source === 'all' || source === 'google')) {
googleActions.getLibraryAlbums();
}
if (this.props.spotify_available && this.props.spotify_library_albums_status != 'finished' && this.props.spotify_library_albums_status != 'started' && (this.props.source == 'all' || this.props.source == 'spotify')) {
this.props.spotifyActions.getLibraryAlbums();
if (spotify_available && spotify_library_albums_status !== 'finished' && spotify_library_albums_status !== 'started' && (source == 'all' || source === 'spotify')) {
spotifyActions.getLibraryAlbums();
}
}
@ -71,7 +90,7 @@ class LibraryAlbums extends React.Component {
spotify_library_albums_status,
} = this.props;
if (mopidy_connected && (source == 'all' || source == 'local')) {
if (mopidy_connected && (source === 'all' || source === 'local')) {
if (!prev_mopidy_connected) mopidyActions.getLibraryAlbums();
// Filter changed, but we haven't got this provider's library yet
@ -80,7 +99,7 @@ class LibraryAlbums extends React.Component {
}
}
if (google_available && (source == 'all' || source == 'google')) {
if (google_available && (source === 'all' || source === 'google')) {
// Filter changed, but we haven't got this provider's library yet
if (source !== 'all' && source !== 'google' && google_library_albums_status !== 'finished' && google_library_albums_status !== 'started') {
googleActions.getLibraryAlbums();
@ -95,22 +114,32 @@ class LibraryAlbums extends React.Component {
}
}
handleContextMenu(e, item) {
const data = {
handleContextMenu = (e, item) => {
const {
uiActions: {
showContextMenu,
},
} = this.props;
showContextMenu({
e,
context: 'album',
uris: [item.uri],
items: [item],
};
this.props.uiActions.showContextMenu(data);
});
}
moreURIsToLoad() {
moreURIsToLoad = () => {
const {
albums,
library_albums,
} = this.props;
const uris = [];
if (this.props.albums && this.props.library_albums) {
for (let i = 0; i < this.props.library_albums.length; i++) {
const uri = this.props.library_albums[i];
if (!this.props.albums.hasOwnProperty(uri) && uriSource(uri) == 'local') {
if (albums && library_albums) {
for (let i = 0; i < library_albums.length; i++) {
const uri = library_albums[i];
if (!albums.hasOwnProperty(uri) && uriSource(uri) == 'local') {
uris.push(uri);
}
@ -122,29 +151,42 @@ class LibraryAlbums extends React.Component {
return uris;
}
loadMore() {
const new_limit = this.state.limit + this.state.per_page;
loadMore = () => {
const {
limit,
per_page,
} = this.state;
const {
location: {
state,
},
history,
} = this.props;
const new_limit = limit + per_page;
this.setState({ limit: new_limit });
// Set our pagination to location state
const state = (this.props.location && this.props.location.state ? this.props.location.state : {});
state.limit = new_limit;
this.props.history.replace({ state });
history.replace({ state: { ...state, limit: new_limit } });
}
setSort(value) {
let reverse = false;
if (this.props.sort == value) reverse = !this.props.sort_reverse;
setSort = (value) => {
const {
sort,
sort_reverse,
uiActions: {
set,
},
} = this.props;
const data = {
let reverse = false;
if (sort === value) reverse = !sort_reverse;
set({
library_albums_sort_reverse: reverse,
library_albums_sort: value,
};
this.props.uiActions.set(data);
});
}
renderView() {
renderView = () => {
let albums = [];
// Spotify library items
@ -224,7 +266,7 @@ class LibraryAlbums extends React.Component {
<LazyLoadListener
loadKey={total_albums > this.state.limit ? this.state.limit : total_albums}
showLoader={this.state.limit < total_albums}
loadMore={() => this.loadMore()}
loadMore={this.loadMore}
/>
</section>
);
@ -248,63 +290,63 @@ class LibraryAlbums extends React.Component {
const source_options = [
{
value: 'all',
label: 'All',
label: i18n('fields.filters.all'),
},
{
value: 'local',
label: 'Local',
label: i18n('services.spotify.local'),
},
];
if (this.props.spotify_available) {
source_options.push({
value: 'spotify',
label: 'Spotify',
label: i18n('services.spotify.title'),
});
}
if (this.props.google_available) {
source_options.push({
value: 'google',
label: 'Google',
label: i18n('services.google.title'),
});
}
const view_options = [
{
value: 'thumbnails',
label: 'Thumbnails',
label: i18n('fields.filters.thumbnails'),
},
{
value: 'list',
label: 'List',
label: i18n('fields.filters.list'),
},
];
const sort_options = [
{
value: null,
label: 'As loaded',
label: i18n('fields.filters.as_loaded'),
},
{
value: 'name',
label: 'Name',
label: i18n('fields.filters.name'),
},
{
value: 'artists.first.name',
label: 'Artist',
label: i18n('fields.filters.artist'),
},
{
value: 'last_modified',
label: 'Updated',
label: i18n('fields.filters.updated'),
},
{
value: 'tracks_uris.length',
label: 'Tracks',
label: i18n('fields.filters.tracks'),
},
{
value: 'uri',
label: 'Source',
label: i18n('fields.filters.source'),
},
];
@ -317,7 +359,7 @@ class LibraryAlbums extends React.Component {
/>
<DropdownField
icon="swap_vert"
name="Sort"
name={i18n('fields.sort')}
value={this.props.sort}
valueAsLabel
options={sort_options}
@ -326,7 +368,7 @@ class LibraryAlbums extends React.Component {
/>
<DropdownField
icon="visibility"
name="View"
name={i18n('fields.view')}
value={this.props.view}
valueAsLabel
options={view_options}
@ -334,7 +376,7 @@ class LibraryAlbums extends React.Component {
/>
<DropdownField
icon="cloud"
name="Source"
name={i18n('fields.source')}
value={this.props.source}
valueAsLabel
options={source_options}
@ -347,7 +389,7 @@ class LibraryAlbums extends React.Component {
<div className="view library-albums-view">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="album" type="material" />
My albums
<I18n path="library.albums.title" />
</Header>
{this.renderView()}
</div>

View File

@ -17,6 +17,7 @@ import {
uriSource,
} from '../../util/helpers';
import { sortItems, applyFilter } from '../../util/arrays';
import { I18n, i18n } from '../../locale';
class LibraryArtists extends React.Component {
constructor(props) {
@ -38,7 +39,7 @@ class LibraryArtists extends React.Component {
});
}
this.props.uiActions.setWindowTitle('Artists');
this.props.uiActions.setWindowTitle(i18n('library.artists.title'));
if (!this.props.mopidy_library_artists && this.props.mopidy_connected && (this.props.source == 'all' || this.props.source == 'local')) {
this.props.mopidyActions.getLibraryArtists();
@ -224,55 +225,55 @@ class LibraryArtists extends React.Component {
const source_options = [
{
value: 'all',
label: 'All',
label: i18n('fields.filters.all'),
},
{
value: 'local',
label: 'Local',
label: i18n('services.mopidy.local'),
},
];
if (this.props.spotify_available) {
source_options.push({
value: 'spotify',
label: 'Spotify',
label: i18n('services.spotify.title'),
});
}
if (this.props.google_available) {
source_options.push({
value: 'google',
label: 'Google',
label: i18n('services.google.title'),
});
}
const view_options = [
{
label: 'Thumbnails',
value: 'thumbnails',
label: i18n('fields.filters.thumbnails'),
},
{
label: 'List',
value: 'list',
label: i18n('fields.filters.list'),
},
];
const sort_options = [
{
value: null,
label: 'As loaded',
label: i18n('fields.filters.as_loaded'),
},
{
value: 'name',
label: 'Name',
label: i18n('fields.filters.name'),
},
{
value: 'followers',
label: 'Followers',
label: i18n('fields.filters.followers'),
},
{
value: 'popularity',
label: 'Popularity',
label: i18n('fields.filters.popularity'),
},
];
@ -285,7 +286,7 @@ class LibraryArtists extends React.Component {
/>
<DropdownField
icon="swap_vert"
name="Sort"
name={i18n('fields.sort')}
value={this.props.sort}
valueAsLabel
options={sort_options}
@ -294,7 +295,7 @@ class LibraryArtists extends React.Component {
/>
<DropdownField
icon="visibility"
name="View"
name={i18n('fields.view')}
value={this.props.view}
valueAsLabel
options={view_options}
@ -302,7 +303,7 @@ class LibraryArtists extends React.Component {
/>
<DropdownField
icon="cloud"
name="Source"
name={i18n('fields.source')}
value={this.props.source}
valueAsLabel
options={source_options}
@ -315,7 +316,7 @@ class LibraryArtists extends React.Component {
<div className="view library-artists-view">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="recent_actors" type="material" />
My artists
<I18n path="library.artists.title" />
</Header>
{this.renderView()}
</div>

View File

@ -9,11 +9,12 @@ import ErrorBoundary from '../../components/ErrorBoundary';
import * as uiActions from '../../services/ui/actions';
import * as mopidyActions from '../../services/mopidy/actions';
import { formatImages } from '../../util/format';
import { I18n, i18n } from '../../locale';
class LibraryBrowse extends React.Component {
componentDidMount() {
this.loadDirectory();
this.props.uiActions.setWindowTitle('Browse');
this.props.uiActions.setWindowTitle(i18n('library.browse.title'));
}
componentDidUpdate = ({ mopidy_connected: prev_mopidy_connected}) => {
@ -97,7 +98,7 @@ class LibraryBrowse extends React.Component {
<div className="view library-local-view">
<Header>
<Icon name="folder" type="material" />
Browse
<I18n path="library.browse.title" />
</Header>
<section className="content-wrapper">
<div className="grid grid--tiles">

View File

@ -19,6 +19,7 @@ import {
isLoading,
} from '../../util/helpers';
import { arrayOf, sortItems } from '../../util/arrays';
import { i18n, I18n } from '../../locale';
class LibraryBrowseDirectory extends React.Component {
constructor(props) {
@ -40,7 +41,7 @@ class LibraryBrowseDirectory extends React.Component {
});
}
this.props.uiActions.setWindowTitle('Browse');
this.props.uiActions.setWindowTitle(i18n('library.browse_directory.title'));
this.loadDirectory();
}
@ -151,7 +152,7 @@ class LibraryBrowseDirectory extends React.Component {
} = this.props;
const { limit } = this.state;
let title = 'Directory';
let title = i18n('library.browse_directory.title');
if (!directory || isLoading(load_queue, ['mopidy_browse'])) {
return (
@ -188,12 +189,12 @@ class LibraryBrowseDirectory extends React.Component {
const view_options = [
{
label: 'Thumbnails',
value: 'thumbnails',
label: i18n('fields.filters.thumbnails'),
},
{
label: 'List',
value: 'list',
label: i18n('fields.filters.list'),
},
];
@ -210,12 +211,12 @@ class LibraryBrowseDirectory extends React.Component {
{tracks && (
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.playAll(e, all_tracks); }}>
<Icon name="play_circle_filled" />
Play all
<I18n path="actions.play_all" />
</a>
)}
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.goBack(e); }}>
<Icon name="keyboard_backspace" />
Back
<I18n path="actions.back" />
</a>
</span>
);

View File

@ -15,6 +15,7 @@ import * as uiActions from '../../services/ui/actions';
import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { applyFilter, removeDuplicates, sortItems } from '../../util/arrays';
import { I18n, i18n } from '../../locale';
class LibraryPlaylists extends React.Component {
constructor(props) {
@ -36,7 +37,7 @@ class LibraryPlaylists extends React.Component {
});
}
this.props.uiActions.setWindowTitle('Playlists');
this.props.uiActions.setWindowTitle(i18n('library.playlists.title'));
if (!this.props.mopidy_library_playlists && this.props.mopidy_connected && (this.props.source == 'all' || this.props.source == 'local')) {
this.props.mopidyActions.getLibraryPlaylists();
@ -182,60 +183,60 @@ class LibraryPlaylists extends React.Component {
const source_options = [
{
value: 'all',
label: 'All',
label: i18n('fields.filters.all'),
},
{
value: 'local',
label: 'Local',
label: i18n('services.mopidy.local'),
},
];
if (this.props.spotify_available) {
source_options.push({
value: 'spotify',
label: 'Spotify',
label: i18n('services.spotify.title'),
});
}
const view_options = [
{
value: 'thumbnails',
label: 'Thumbnails',
label: i18n('fields.filters.thumbnails'),
},
{
value: 'list',
label: 'List',
label: i18n('fields.filters.list'),
},
];
const sort_options = [
{
value: null,
label: 'As loaded',
label: i18n('fields.filters.as_loaded'),
},
{
value: 'name',
label: 'Name',
label: i18n('fields.filters.name'),
},
{
value: 'last_modified',
label: 'Updated',
label: i18n('fields.filters.updated'),
},
{
value: 'can_edit',
label: 'Editable',
label: i18n('fields.filters.editable'),
},
{
value: 'owner.id',
label: 'Owner',
label: i18n('fields.filters.owner'),
},
{
value: 'tracks_total',
label: 'Tracks',
label: i18n('fields.filters.tracks'),
},
{
value: 'source',
label: 'Source',
label: i18n('fields.filters.source'),
},
];
@ -248,7 +249,7 @@ class LibraryPlaylists extends React.Component {
/>
<DropdownField
icon="swap_vert"
name="Sort"
name={i18n('fields.sort')}
value={this.props.sort}
valueAsLabel
options={sort_options}
@ -257,7 +258,7 @@ class LibraryPlaylists extends React.Component {
/>
<DropdownField
icon="visibility"
name="View"
name={i18n('fields.view')}
valueAsLabel
value={this.props.view}
options={view_options}
@ -265,7 +266,7 @@ class LibraryPlaylists extends React.Component {
/>
<DropdownField
icon="cloud"
name="Source"
name={i18n('fields.source')}
valueAsLabel
value={this.props.source}
options={source_options}
@ -273,7 +274,7 @@ class LibraryPlaylists extends React.Component {
/>
<Link className="button button--no-hover" to="/playlist/create">
<Icon name="add_box" />
New
<I18n path="actions.new" />
</Link>
</span>
);
@ -282,7 +283,7 @@ class LibraryPlaylists extends React.Component {
<div className="view library-playlists-view">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="queue_music" type="material" />
My playlists
<I18n path="library.playlists.title" />
</Header>
{ this.renderView() }
</div>

View File

@ -11,13 +11,17 @@ import * as uiActions from '../../services/ui/actions';
import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { isLoading } from '../../util/helpers';
import { I18n, i18n } from '../../locale';
class LibraryTracks extends React.Component {
componentDidMount() {
this.props.uiActions.setWindowTitle('Tracks');
this.props.uiActions.setWindowTitle(i18n('library.tracks.title'));
if (!this.props.spotify_available) {
this.props.uiActions.createNotification({ level: 'warning', content: 'Enable Spotify to browse tracks' });
this.props.uiActions.createNotification({
level: 'warning',
content: i18n('errors.enable_first', { provider: i18n('services.spotify.title') }),
});
} else if (this.props.library_tracks === undefined) {
this.props.spotifyActions.getLibraryTracks();
}
@ -42,7 +46,7 @@ class LibraryTracks extends React.Component {
if (isLoading(this.props.load_queue, ['spotify_me/tracks?'])) {
return (
<div className="view library-tracks-view">
<Header icon="music" title="My tracks" />
<Header icon="music" title={i18n('library.tracks.title')} />
<Loader body loading />
</div>
);
@ -61,7 +65,7 @@ class LibraryTracks extends React.Component {
const options = (
<a className="button button--no-hover" onClick={(e) => this.playAll(e)}>
<Icon name="play_circle_filled" />
Play all
<I18n path="actions.play_all" />
</a>
);
@ -69,7 +73,7 @@ Play all
<div className="view library-tracks-view">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="music_note" type="material" />
My tracks
<I18n path="library.tracks.title" />
</Header>
<section className="content-wrapper">
<TrackList tracks={tracks} />