Renaming Content to I18n to avoid naming clashes; Artist and Debug views mapped

This commit is contained in:
James Barnsley
2020-07-07 20:52:09 +12:00
parent fca28ada88
commit 6225c6a6b0
18 changed files with 1058 additions and 917 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -100,7 +100,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1594021564";
var build = "1594107635";
var version = "3.50.0";
// Construct the script tag

View File

@ -1,6 +1,6 @@
import { memo } from 'react';
import { content } from '../locale';
import { i18n } from '../locale';
/**
* Format time duration
@ -51,10 +51,10 @@ const durationSentence = (milliseconds = null) => {
const totalMinutes = Math.floor(milliseconds / (1000 * 60));
const totalHours = Math.floor(milliseconds / (1000 * 60 * 60));
if (totalHours > 1) return `${totalHours}+ ${content('time.hours.short')}`;
if (totalMinutes > 1) return `${totalMinutes} ${content('time.minutes.short')}`;
if (totalSeconds) return `${totalSeconds} ${content('time.seconds.short')}`;
return `0 ${content('time.minutes.short')}`;
if (totalHours > 1) return `${totalHours}+ ${i18n('time.hours.short')}`;
if (totalMinutes > 1) return `${totalMinutes} ${i18n('time.minutes.short')}`;
if (totalSeconds) return `${totalSeconds} ${i18n('time.seconds.short')}`;
return `0 ${i18n('time.minutes.short')}`;
};
const dater = (type, data) => {
@ -99,17 +99,17 @@ const dater = (type, data) => {
var years = Math.floor(diff / (1000 * 60 * 60 * 24 * 7 * 52));
if (seconds < 60) {
return `${seconds} ${content(`time.seconds.${seconds > 1 ? 'singular' : 'plural'}`)}`;
return `${seconds} ${i18n(`time.seconds.${seconds > 1 ? 'singular' : 'plural'}`)}`;
} if (minutes < 60) {
return `${minutes} ${content(`time.minutes.${minutes > 1 ? 'singular' : 'plural'}`)}`;
return `${minutes} ${i18n(`time.minutes.${minutes > 1 ? 'singular' : 'plural'}`)}`;
} if (hours < 24) {
return `${hours} ${content(`time.hours.${hours > 1 ? 'singular' : 'plural'}`)}`;
return `${hours} ${i18n(`time.hours.${hours > 1 ? 'singular' : 'plural'}`)}`;
} if (days < 7) {
return `${days} ${content(`time.days.${days > 1 ? 'singular' : 'plural'}`)}`;
return `${days} ${i18n(`time.days.${days > 1 ? 'singular' : 'plural'}`)}`;
} if (weeks < 54) {
return `${weeks} ${content(`time.weeks.${weeks > 1 ? 'singular' : 'plural'}`)}`;
return `${weeks} ${i18n(`time.weeks.${weeks > 1 ? 'singular' : 'plural'}`)}`;
}
return `${years} ${content(`time.years.${years > 1 ? 'singular' : 'plural'}`)}`;
return `${years} ${i18n(`time.years.${years > 1 ? 'singular' : 'plural'}`)}`;
default:
return null;
}

View File

@ -5,19 +5,20 @@ import { bindActionCreators } from 'redux';
import * as uiActions from '../../services/ui/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { isLoading, getFromUri } from '../../util/helpers';
import { i18n } from '../../locale';
class FollowButton extends React.Component {
remove() {
remove = () => {
const { spotifyActions: actions, uri } = this.props;
actions.following(uri, 'DELETE');
}
add() {
add = () => {
const { spotifyActions: actions, uri } = this.props;
actions.following(uri, 'PUT');
}
render() {
render = () => {
const {
uri,
addText,
@ -49,7 +50,7 @@ class FollowButton extends React.Component {
className={`${className} button--disabled`}
onClick={() => actions.createNotification({ content: 'You must authorize Spotify first', level: 'warning' })}
>
{addText}
{addText || i18n('actions.add_to_library')}
</button>
);
} if (is_following === true) {
@ -57,9 +58,9 @@ class FollowButton extends React.Component {
<button
type="button"
className={`${className} button--destructive`}
onClick={() => this.remove()}
onClick={this.remove}
>
{removeText}
{removeText || i18n('actions.remove_from_library')}
</button>
);
}
@ -67,15 +68,15 @@ class FollowButton extends React.Component {
<button
type="button"
className={`${className} button--default`}
onClick={() => this.add()}
onClick={this.add}
>
{addText}
{addText || i18n('actions.add_to_library')}
</button>
);
}
}
const mapStateToProps = (state, ownProps) => ({
const mapStateToProps = (state) => ({
load_queue: state.ui.load_queue,
spotify_authorized: state.spotify.authorization,
});

View File

@ -2,7 +2,7 @@
import React from 'react';
import LinksSentence from './LinksSentence';
import Dater from './Dater';
import { Dater } from './Dater';
import URILink from './URILink';
import ContextMenuTrigger from './ContextMenuTrigger';
import Icon from './Icon';

View File

@ -8,7 +8,7 @@ import ProgressSlider from './Fields/ProgressSlider';
import VolumeControl from './Fields/VolumeControl';
import MuteControl from './Fields/MuteControl';
import OutputControl from './Fields/OutputControl';
import Dater from './Dater';
import { Dater } from './Dater';
import LinksSentence from './LinksSentence';
import Thumbnail from './Thumbnail';
import Icon from './Icon';

View File

@ -8,7 +8,7 @@ import Icon from './Icon';
import Dropzones from './Fields/Dropzones';
import * as uiActions from '../services/ui/actions';
import * as mopidyActions from '../services/mopidy/actions';
import { content, Content } from '../locale';
import { I18n } from '../locale';
class Sidebar extends React.Component {
closeSidebar = () => uiActions.toggleSidebar(false);
@ -47,19 +47,19 @@ class Sidebar extends React.Component {
<Icon name="warning" className="red-text" />
<span className="tooltip__content">
{!mopidy_connected && (
<Content path="sidebar.not_connected" params={{ name: 'Mopidy' }}>
<I18n path="sidebar.not_connected" params={{ name: 'Mopidy' }}>
<br />
</Content>
</I18n>
)}
{!pusher_connected && (
<Content path="sidebar.not_connected" params={{ name: 'Pusher' }}>
<I18n path="sidebar.not_connected" params={{ name: 'Pusher' }}>
<br />
</Content>
</I18n>
)}
{!snapcast_connected && snapcast_enabled && (
<Content path="sidebar.not_connected" params={{ name: 'Snapcast' }}>
<I18n path="sidebar.not_connected" params={{ name: 'Snapcast' }}>
<br />
</Content>
</I18n>
)}
</span>
</span>
@ -84,68 +84,68 @@ class Sidebar extends React.Component {
<section className="sidebar__menu__section">
<Link to="/queue" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="play_arrow" type="material" />
<Content path="sidebar.now_playing" />
<I18n path="sidebar.now_playing" />
</Link>
<Link to="/search" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="search" type="material" />
<Content path="sidebar.search" />
<I18n path="sidebar.search" />
</Link>
</section>
{spotify_available && (
<section className="sidebar__menu__section">
<title className="sidebar__menu__section__title">
<Content path="sidebar.discover" />
<I18n path="sidebar.discover" />
</title>
<Link to="/discover/recommendations" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="explore" type="material" />
<Content path="sidebar.discover" />
<I18n path="sidebar.discover" />
</Link>
<Link to="/discover/categories" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="mood" type="material" />
<Content path="sidebar.genre" />
<I18n path="sidebar.genre" />
</Link>
<Link to="/discover/featured" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="star" type="material" />
<Content path="sidebar.featured_playlists" />
<I18n path="sidebar.featured_playlists" />
</Link>
<Link to="/discover/new-releases" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="new_releases" type="material" />
<Content path="sidebar.new_releases" />
<I18n path="sidebar.new_releases" />
</Link>
</section>
)}
<section className="sidebar__menu__section">
<title className="sidebar__menu__section__title">
<Content path="sidebar.my_music" />
<I18n path="sidebar.my_music" />
</title>
<Link to="/library/playlists" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="queue_music" type="material" />
<Content path="sidebar.playlists" />
<I18n path="sidebar.playlists" />
</Link>
<Link to="/library/artists" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="recent_actors" type="material" />
<Content path="sidebar.artists" />
<I18n path="sidebar.artists" />
</Link>
<Link to="/library/albums" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="album" type="material" />
<Content path="sidebar.albums" />
<I18n path="sidebar.albums" />
</Link>
<Link to="/library/tracks" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="music_note" type="material" />
<Content path="sidebar.tracks" />
<I18n path="sidebar.tracks" />
</Link>
<Link to="/library/browse" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="folder" type="material" />
<Content path="sidebar.browse" />
<I18n path="sidebar.browse" />
</Link>
</section>
<section className="sidebar__menu__section">
<Link to="/settings" history={history} className="sidebar__menu__item" activeClassName="sidebar__menu__item--active">
<Icon name="settings" type="material" />
<Content path="sidebar.settings" />
<I18n path="sidebar.settings" />
{this.renderStatusIcon()}
</Link>
</section>

View File

@ -2,7 +2,7 @@
import React from 'react';
import Icon from './Icon';
import LinksSentence from './LinksSentence';
import Dater from './Dater';
import { Dater } from './Dater';
import URILink from './URILink';
import ContextMenuTrigger from './ContextMenuTrigger';
import ErrorBoundary from './ErrorBoundary';

View File

@ -1,16 +1,22 @@
errors:
uri_not_found: Could not find anything with URI %{uri}
specs:
tracks: %{count} tracks
followers: %{count} followers
listeners: %{count} listeners
plays: %{count} plays
tracks: '%{count} tracks'
followers: '%{count} followers'
listeners: '%{count} listeners'
plays: '%{count} plays'
popularity: '%{percent}% popularity'
actions:
play: Play
pause: Pause
stop: Stop
add_to_library: Add to library
remove_from_library: Remove from library
reset: Reset
back: Back
enable: Enable
disable: Disable
send: Send
time:
seconds:
short: secs
@ -36,6 +42,13 @@ time:
short: yrs
singular: year
plural: years
services:
mopidy:
title: Mopidy
pusher:
title: Pusher
snapcast:
title: Snapcast
sidebar:
now_playing: Now playing
search: Search
@ -50,7 +63,7 @@ sidebar:
tracks: Tracks
browse: Browse
settings: Settings
not_connected: %{name} not connected
not_connected: '%{name} not connected'
now_playing:
title: Now playing
context_actions:
@ -64,25 +77,63 @@ now_playing:
radio: Radio
album:
title: Album
title_window: %{name} by %{artist} (album)
title_window: '%{name} by %{artist} (album)'
wiki:
title: About
published: 'Published: %{date}'
artist:
title: Artist
title_window: '%{name} (artist)'
overview:
title: Overview
top_tracks: Top tracks
related_artists:
title: Related artists
more: All related artists
tracks:
title: Tracks
related_artists:
title: Related artists
about:
title: About
source: '%{source} artist'
wiki:
published: 'Published: %{date}'
origin: 'Origin: '
debug:
title: Debug
ui:
title: User interface
debug:
title: Debug
overlay: Show debug overlay
logging:
title: Logging
mopidy: Log Mopidy
pusher: Log Pusher
actions: Log Actions
snapcast: Log Snapcast
create_notification: Create notification
create_process: Create process notification
run_test: Run test process
call: Call
data: Data
examples: Examples
search:
title: 'Search: %{term}'
context_actions:
sort: Sort
source: Sources (%{count})
source: 'Sources (%{count})'
placeholder: Search
artists:
title: Artists
more: All artists (%{count})
more: 'All artists (%{count})'
albums:
title: Albums
more: All albums (%{count})
more: 'All albums (%{count})'
playlists:
title: Playlists
more: All playlists (%{count})
more: 'All playlists (%{count})'
tracks:
title: Tracks
settings:

View File

@ -1,5 +1,4 @@
import React, { Fragment } from 'react';
import { useSelector } from 'react-redux';
import get from 'lodash/get';
import dictionaries from './dictionaries';
import { titleCase } from '../util/helpers';
@ -7,7 +6,7 @@ import { titleCase } from '../util/helpers';
const PARAMS_REG_EXP = '%{(.*?)}';
const paramsRegExp = new RegExp(PARAMS_REG_EXP, 'g');
const content = (path, params = {}, transform) => {
const i18n = (path, params = {}, transform) => {
const dictionary = dictionaries[window.language || 'en'] || dictionaries.en;
let value = get((dictionary), path, '');
@ -33,19 +32,19 @@ const content = (path, params = {}, transform) => {
return value;
};
const Content = ({ path, params = {}, transform, children }) => (
const I18n = ({ path, transform, children, ...params }) => (
<Fragment>
{content(path, params, transform)}
{i18n(path, params, transform)}
{children}
</Fragment>
);
export default {
Content,
content,
I18n,
i18n,
};
export {
Content,
content,
I18n,
i18n,
};

View File

@ -8,12 +8,12 @@ import Thumbnail from '../components/Thumbnail';
import LinksSentence from '../components/LinksSentence';
import Loader from '../components/Loader';
import FollowButton from '../components/Fields/FollowButton';
import NiceNumber, { nice_number } from '../components/NiceNumber';
import Dater from '../components/Dater';
import { nice_number } from '../components/NiceNumber';
import { Dater } from '../components/Dater';
import LazyLoadListener from '../components/LazyLoadListener';
import ContextMenuTrigger from '../components/ContextMenuTrigger';
import Icon from '../components/Icon';
import { content } from '../locale';
import { i18n, I18n } from '../locale';
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
import * as mopidyActions from '../services/mopidy/actions';
@ -111,9 +111,9 @@ export class Album extends React.Component {
}
}
}
setWindowTitle(content('album.title_window', { name: album.name, artist: artistNames.join() }));
setWindowTitle(i18n('album.title_window', { name: album.name, artist: artistNames.join() }));
} else {
setWindowTitle(content('album.title'));
setWindowTitle(i18n('album.title'));
}
}
@ -174,6 +174,7 @@ export class Album extends React.Component {
tracks,
artists,
load_queue,
slim_mode,
} = this.props;
if (!albumProp) {
@ -187,7 +188,7 @@ export class Album extends React.Component {
return (
<ErrorMessage type="not-found" title="Not found">
<p>
{content('errors.uri_not_found', { uri: encodeURIComponent(uri) })}
{i18n('errors.uri_not_found', { uri: encodeURIComponent(uri) })}
</p>
</ErrorMessage>
);
@ -195,15 +196,11 @@ export class Album extends React.Component {
const album = collate(albumProp, { tracks, artists });
if (
const is_loading_tracks = (
!album.tracks_uris
|| (album.tracks_uris && !album.tracks)
|| album.tracks_uris.length !== album.tracks.length
) {
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
}
);
return (
<div className="view album-view content-wrapper preserve-3d">
@ -215,7 +212,7 @@ export class Album extends React.Component {
<h1>{album.name}</h1>
<ul className="details">
{!this.props.slim_mode ? (
{!slim_mode ? (
<li className="source">
<Icon type="fontawesome" name={sourceIcon(album.uri)} />
</li>
@ -232,28 +229,28 @@ export class Album extends React.Component {
) : null}
{album.tracks ? (
<li>
{content(
{i18n(
'specs.tracks',
{ count: album.tracks_total || album.tracks.length },
)}
</li>
) : null}
{!this.props.slim_mode && album.tracks ? (
{!slim_mode && album.tracks ? (
<li>
<Dater type="total-time" data={album.tracks} />
</li>
) : null}
{!this.props.slim_mode && album.play_count ? (
{!slim_mode && album.play_count ? (
<li>
{content(
{i18n(
'specs.plays',
{ count: nice_number(album.play_count) },
)}
</li>
) : null}
{!this.props.slim_mode && album.listeners ? (
{!slim_mode && album.listeners ? (
<li>
{content(
{i18n(
'specs.plays',
{ count: nice_number(album.listeners) },
)}
@ -263,19 +260,17 @@ export class Album extends React.Component {
</div>
<div className="actions">
<button type="button" className="button button--primary" onClick={(e) => this.play()}>
{content('actions.play')}
<button type="button" className="button button--primary" onClick={this.play}>
<I18n path="actions.play" />
</button>
{uriSource(this.props.uri) == 'spotify' ? (
{uriSource(uri) === 'spotify' && (
<FollowButton
className="secondary"
uri={this.props.uri}
addText={content('actions.add_to_library')}
removeText={content('actions.remove_from_library')}
uri={uri}
is_following={this.inLibrary()}
/>
) : null}
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
)}
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
<section className="list-wrapper">
@ -287,21 +282,18 @@ export class Album extends React.Component {
<LazyLoadListener
loadKey={album.tracks_more}
showLoader={is_loading_tracks}
loadMore={() => this.loadMore()}
loadMore={this.loadMore}
/>
</section>
{album.wiki ? (
<section className="wiki">
<h4 className="wiki__title">{content('album.wiki.title')}</h4>
<h4 className="wiki__title">{i18n('album.wiki.title')}</h4>
<div className="wiki__text">
<p>{album.wiki}</p>
<br />
<div className="mid_grey-text">
{content(
'album.wiki.published',
{ date: album.wiki_publish_date },
)}
<I18n path="album.wiki.published" params={{ date: album.wiki_publish_date }} />
</div>
</div>
</section>

View File

@ -32,6 +32,7 @@ import {
} from '../util/helpers';
import { collate } from '../util/format';
import { sortItems, applyFilter } from '../util/arrays';
import { i18n, I18n } from '../locale';
class Artist extends React.Component {
componentDidMount() {
@ -56,7 +57,7 @@ class Artist extends React.Component {
if (uri !== prevUri) {
loadArtist(uri);
} else if (!prev_mopidy_connected && mopidy_connected) {
if (uriSource(uri) != 'spotify') {
if (uriSource(uri) !== 'spotify') {
loadArtist(uri);
}
}
@ -65,74 +66,127 @@ class Artist extends React.Component {
if (prevUri !== uri && artist) this.setWindowTitle(artist);
}
setWindowTitle(artist = this.props.artist) {
onResetFilters = () => {
this.setFilter(null);
this.setSort(null);
}
onChangeFilter = (value) => {
const { uiActions: { set, hideContextMenu } } = this.props;
set({ artist_albums_filter: value });
hideContextMenu();
}
onChangeSort = (value) => {
const {
sort,
sort_reverse,
uiActions: {
set,
hideContextMenu,
},
} = this.props;
let reverse = false;
if (value !== null && sort == value) {
reverse = !sort_reverse;
}
set({
artist_albums_sort_reverse: reverse,
artist_albums_sort: value,
});
hideContextMenu();
}
onPlay = () => {
const {
artist: {
uri,
tracks_uris,
albums_uris,
},
mopidyActions: { playURIs },
} = this.props;
playURIs(tracks_uris || albums_uris, uri);
}
setWindowTitle = (artist = this.props.artist) => {
const { uiActions: { setWindowTitle } } = this.props;
if (artist) {
this.props.uiActions.setWindowTitle(`${artist.name} (artist)`);
setWindowTitle(i18n('artist.title_window', { name: artist.name }));
} else {
this.props.uiActions.setWindowTitle('Artist');
setWindowTitle(i18n('artist.title'));
}
}
handleContextMenu(e) {
const data = {
handleContextMenu = (e) => {
const {
artist,
uri,
uiActions: { showContextMenu },
} = this.props;
showContextMenu({
e,
context: 'artist',
items: [this.props.artist],
uris: [this.props.uri],
};
this.props.uiActions.showContextMenu(data);
items: [artist],
uris: [uri],
});
}
loadMore() {
this.props.spotifyActions.getMore(
this.props.artist.albums_more,
loadMore = () => {
const {
spotifyActions: { getMore },
artist: { albums_more },
uri,
} = this.props;
getMore(
albums_more,
{
parent_type: 'artist',
parent_key: this.props.uri,
parent_key: uri,
records_type: 'album',
},
);
}
inLibrary() {
const library = `${uriSource(this.props.uri)}_library_artists`;
return (this.props[library] && this.props[library].indexOf(this.props.uri) > -1);
inLibrary = () => {
const { uri } = this.props;
const libraryName = `${uriSource(uri)}_library_artists`;
const { [libraryName]: library = [] } = this.props;
return library.indexOf(uri) > -1;
}
setSort(value) {
let reverse = false;
if (value !== null && this.props.sort == value) {
reverse = !this.props.sort_reverse;
}
const data = {
artist_albums_sort_reverse: reverse,
artist_albums_sort: value,
};
this.props.uiActions.set(data);
}
setFilter(value) {
this.props.uiActions.set({ artist_albums_filter: value });
}
renderOverview() {
const { uiActions } = this.props;
renderOverview = () => {
const {
uri,
uiActions,
artist: artistProp,
artists,
albums,
tracks,
sort,
sort_reverse,
filter,
} = this.props;
const artist = collate(
this.props.artist,
artistProp,
{
artists: this.props.artists,
albums: this.props.albums,
tracks: this.props.tracks,
artists,
albums,
tracks,
},
);
if (this.props.sort && artist.albums) {
artist.albums = sortItems(artist.albums, this.props.sort, this.props.sort_reverse);
if (sort && artist.albums) {
artist.albums = sortItems(artist.albums, sort, sort_reverse);
}
if (this.props.filter && artist.albums) {
artist.albums = applyFilter('type', this.props.filter, artist.albums);
if (filter && artist.albums) {
artist.albums = applyFilter('type', filter, artist.albums);
}
const sort_options = [
@ -173,16 +227,16 @@ class Artist extends React.Component {
},
];
if (!artist.tracks_uris || (artist.tracks_uris && !artist.tracks) || (artist.tracks_uris.length !== artist.tracks.length)) {
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
}
const is_loading_tracks = (
!artist.tracks_uris
|| (artist.tracks_uris && !artist.tracks)
|| (artist.tracks_uris.length !== artist.tracks.length)
);
return (
<div className="body overview">
<div className={`top-tracks col col--w${artist.related_artists && artist.related_artists.length > 0 ? '70' : '100'}`}>
{artist.tracks ? <h4>Top tracks</h4> : null}
{artist.tracks && <h4><I18n path="artist.overview.top_tracks" /></h4>}
<div className="list-wrapper">
<TrackList className="artist-track-list" uri={artist.uri} tracks={artist.tracks ? artist.tracks.splice(0, 10) : []} />
<LazyLoadListener showLoader={is_loading_tracks} />
@ -193,7 +247,7 @@ class Artist extends React.Component {
{artist.related_artists && artist.related_artists.length > 0 && (
<div className="col col--w25 related-artists">
<h4>Related artists</h4>
<h4><I18n path="artist.overview.related_artists.title" /></h4>
<div className="list-wrapper">
<RelatedArtists
artists={artist.related_artists.slice(0, 6)}
@ -201,11 +255,11 @@ class Artist extends React.Component {
/>
</div>
<Link
to={`/artist/${encodeURIComponent(this.props.uri)}/related-artists`}
to={`/artist/${encodeURIComponent(uri)}/related-artists`}
scrollTo="#sub-views-menu"
className="button button--default"
>
All related artists
<I18n path="artist.overview.related_artists.more" />
</Link>
</div>
)}
@ -215,28 +269,28 @@ class Artist extends React.Component {
{artist.albums && (
<div className="albums">
<h4>
<div>Albums</div>
<div><I18n path="artist.overview.albums" /></div>
<DropdownField
icon="swap_vert"
name="Sort"
value={this.props.sort}
value={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(); }}
selected_icon={sort ? (sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
handleChange={this.onChangeSort}
/>
<DropdownField
icon="filter_list"
name="Filter"
value={this.props.filter}
value={filter}
valueAsLabel
options={filter_options}
handleChange={(value) => { this.setFilter(value); this.props.uiActions.hideContextMenu(); }}
handleChange={this.onFilterChange}
/>
{(this.props.sort || this.props.filter) && (
<a className="button button--discrete button--small" onClick={(e) => { this.setFilter(null); this.setSort(null); }}>
{(sort || filter) && (
<a className="button button--discrete button--small" onClick={this.onResetFilters}>
<Icon name="clear" />
Reset
<I18n path="actions.reset" />
</a>
)}
</h4>
@ -246,7 +300,7 @@ class Artist extends React.Component {
<LazyLoadListener
loadKey={artist.albums_more}
showLoader={artist.albums_more}
loadMore={() => this.loadMore()}
loadMore={this.loadMore}
/>
</section>
</div>
@ -255,20 +309,26 @@ class Artist extends React.Component {
);
}
renderTracks() {
renderTracks = () => {
const {
artist: artistProp,
artists,
tracks,
} = this.props;
const artist = collate(
this.props.artist,
artistProp,
{
artists: this.props.artists,
tracks: this.props.tracks,
artists,
tracks,
},
);
if (!artist.tracks_uris || (artist.tracks_uris && !artist.tracks) || (artist.tracks_uris.length !== artist.tracks.length)) {
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
}
const is_loading_tracks = (
!artist.tracks_uris
|| (artist.tracks_uris && !artist.tracks)
|| (artist.tracks_uris.length !== artist.tracks.length)
);
return (
<div className="body related-artists">
@ -280,11 +340,16 @@ class Artist extends React.Component {
);
}
renderRelatedArtists() {
renderRelatedArtists = () => {
const {
artist: artistProp,
artists,
} = this.props;
const artist = collate(
this.props.artist,
artistProp,
{
artists: this.props.artists,
artists,
},
);
@ -297,45 +362,48 @@ class Artist extends React.Component {
);
}
renderAbout() {
renderAbout = () => {
const {
artist: artistProp,
artists,
} = this.props;
const artist = collate(
this.props.artist,
artistProp,
{
artists: this.props.artists,
artists,
},
);
const thumbnails = [];
if (artist.images && artist.images.length > 0) {
for (const images of artist.images) {
// Capture potential 'null' images
// TODO: Prevent these from existing in the first place
if (images.huge) {
thumbnails.push(
<div className="tile thumbnail-wrapper" key={images.huge}>
<Thumbnail size="huge" canZoom fill images={images} />
</div>,
);
}
const thumbnails = artist.images && artist.images.map(
(image) => {
if (!image.huge) return null;
return (
<div className="tile thumbnail-wrapper" key={image.huge}>
<Thumbnail size="huge" canZoom fill images={image} />
</div>
);
}
}
);
return (
<div className="body about">
<div className="col col--w40 tiles artist-stats">
{thumbnails}
<div className="tile">
<span className="content">
<Icon type="fontawesome" name={sourceIcon(artist.uri)} />
{`${titleCase(uriSource(artist.uri))} artist`}
<I18n
path="artist.about.source"
source={titleCase(uriSource(artist.uri))}
/>
</span>
</div>
{artist.followers && (
<div className="tile">
<span className="content">
<Icon type="fontawesome" name="users" />
{`${artist.followers.toLocaleString()} followers`}
<I18n path="specs.followers" count={artist.followers.toLocaleString()} />
</span>
</div>
)}
@ -343,7 +411,7 @@ class Artist extends React.Component {
<div className="tile">
<span className="content">
<Icon type="fontawesome" name="fire" />
{`${artist.popularity} % popularity`}
<I18n path="specs.popularity" percent={artist.popularity} />
</span>
</div>
)}
@ -351,7 +419,7 @@ class Artist extends React.Component {
<div className="tile">
<span className="content">
<Icon type="fontawesome" name="headphones" />
{`${artist.listeners.toLocaleString()} listeners`}
<I18n path="specs.listeners" count={artist.listeners.toLocaleString()} />
</span>
</div>
)}
@ -365,11 +433,17 @@ class Artist extends React.Component {
<p>{artist.biography}</p>
<br />
<div className="mid_grey-text">
{`Published: ${artist.biography_publish_date}`}
<I18n path="artist.about.wiki.published" date={artist.biography_publish_date} />
</div>
<div className="mid_grey-text">
{`Origin: `}
<a href={artist.biography_link} target="_blank">{ artist.biography_link }</a>
<I18n path="artist.about.wiki.origin" />
<a
href={artist.biography_link}
target="_blank"
rel="noopener noreferrer"
>
{artist.biography_link}
</a>
</div>
</div>
)}
@ -379,35 +453,35 @@ class Artist extends React.Component {
);
}
render() {
const scheme = uriSource(this.props.uri);
render = () => {
const {
uri,
load_queue,
artist,
history,
} = this.props;
if (!this.props.artist) {
if (isLoading(this.props.load_queue, [`spotify_artists/${getFromUri('artistid', this.props.uri)}`, 'lastfm_method=artist.getInfo'])) {
if (!artist) {
if (
isLoading(
load_queue,
[`spotify_artists/${getFromUri('artistid', uri)}`, 'lastfm_method=artist.getInfo'],
)
) {
return <Loader body loading />;
}
return (
<ErrorMessage type="not-found" title="Not found">
<p>
{`Could not find artist with URI "${encodeURIComponent(this.props.uri)}"`}
<I18n path="errors.uri_not_found" uri={encodeURIComponent(uri)} />
</p>
</ErrorMessage>
);
}
if (this.props.artist && this.props.artist.images && this.props.artist.images.length > 0) {
var image = this.props.artist.images[0].huge;
} else {
var image = null;
}
const is_spotify = (scheme == 'spotify');
if (this.props.artist.tracks_uris && this.props.artist.tracks_uris.length > 0) {
var uris_to_play = this.props.artist.tracks_uris;
} else {
var uris_to_play = this.props.artist.albums_uris;
}
const scheme = uriSource(uri);
const image = (artist.images && artist.images.length) ? artist.images[0].huge : null;
const is_spotify = (scheme === 'spotify');
return (
<div className="view artist-view preserve-3d">
@ -417,64 +491,69 @@ class Artist extends React.Component {
<div className="liner">
<div className="heading">
<div className="heading__thumbnail">
<Thumbnail size="medium" circle canZoom type="artist" image={image} />
</div>
<div className="heading__content">
<h1>{this.props.artist ? this.props.artist.name : null}</h1>
<div className="actions">
<button className="button button--primary" onClick={(e) => this.props.mopidyActions.playURIs(uris_to_play, this.props.artist.uri)}>Play</button>
{is_spotify ? <FollowButton uri={this.props.uri} removeText="Remove from library" addText="Add to library" is_following={this.inLibrary()} /> : null}
<ContextMenuTrigger className="white" onTrigger={(e) => this.handleContextMenu(e)} />
<button className="button button--primary" onClick={this.onPlay}>
<I18n path="actions.play" />
</button>
{is_spotify && (
<FollowButton
uri={uri}
is_following={this.inLibrary()}
/>
)}
<ContextMenuTrigger className="white" onTrigger={this.handleContextMenu} />
</div>
</div>
</div>
<div className="sub-views" id="sub-views-menu">
<Link
exact
history={this.props.history}
history={history}
activeClassName="sub-views__option--active"
className="sub-views__option"
to={`/artist/${encodeURIComponent(this.props.uri)}`}
to={`/artist/${encodeURIComponent(uri)}`}
scrollTo="#sub-views-menu"
>
<h4>Overview</h4>
<h4><I18n path="artist.overview.title" /></h4>
</Link>
{this.props.artist.tracks_uris && this.props.artist.tracks_uris.length > 10 && (
{artist.tracks_uris && artist.tracks_uris.length > 10 && (
<Link
exact
history={this.props.history}
history={history}
activeClassName="sub-views__option--active"
className="sub-views__option"
to={`/artist/${encodeURIComponent(this.props.uri)}/tracks`}
to={`/artist/${encodeURIComponent(uri)}/tracks`}
scrollTo="#sub-views-menu"
>
<h4>Tracks</h4>
<h4><I18n path="artist.tracks.title" /></h4>
</Link>
)}
{this.props.artist.related_artists_uris && (
{artist.related_artists_uris && (
<Link
exact
history={this.props.history}
history={history}
activeClassName="sub-views__option--active"
className="sub-views__option"
to={`/artist/${encodeURIComponent(this.props.uri)}/related-artists`}
to={`/artist/${encodeURIComponent(uri)}/related-artists`}
scrollTo="#sub-views-menu"
>
<h4>Related artists</h4>
<h4><I18n path="artist.related_artists.title" /></h4>
</Link>
)}
<Link
exact
history={this.props.history}
history={history}
activeClassName="sub-views__option--active"
className="sub-views__option"
to={`/artist/${encodeURIComponent(this.props.uri)}/about`}
to={`/artist/${encodeURIComponent(uri)}/about`}
scrollTo="#sub-views-menu"
>
<h4>About</h4>
<h4><I18n path="artist.about.title" /></h4>
</Link>
</div>
</div>

View File

@ -9,6 +9,7 @@ import * as pusherActions from '../services/pusher/actions';
import * as mopidyActions from '../services/mopidy/actions';
import * as spotifyActions from '../services/spotify/actions';
import * as snapcastActions from '../services/snapcast/actions';
import { I18n, i18n } from '../locale';
class Debug extends React.Component {
constructor(props) {
@ -24,152 +25,196 @@ class Debug extends React.Component {
}
componentDidMount() {
this.props.uiActions.setWindowTitle('Debug');
const { uiActions: { setWindowTitle } } = this.props;
setWindowTitle(i18n('debug.title'));
}
callMopidy(e) {
onBack = () => {
const { history } = this.props;
history.push('/settings');
}
callMopidy = (e) => {
const { mopidyActions: { debug } } = this.props;
const { mopidy_call, mopidy_data } = this.state;
e.preventDefault();
this.props.mopidyActions.debug(this.state.mopidy_call, JSON.parse(this.state.mopidy_data));
debug(mopidy_call, JSON.parse(mopidy_data));
}
callPusher(e) {
callPusher = (e) => {
const { pusherActions: { debug } } = this.props;
const { pusher_data } = this.state;
e.preventDefault();
this.props.pusherActions.debug(JSON.parse(this.state.pusher_data));
debug(JSON.parse(pusher_data));
}
callSnapcast(e) {
callSnapcast = (e) => {
const { snapcastActions: { debug } } = this.props;
const { snapcast_data } = this.state;
e.preventDefault();
this.props.snapcastActions.debug(JSON.parse(this.state.snapcast_data));
debug(JSON.parse(snapcast_data));
}
toggleTestMode(e) {
toggleTestMode = (e) => {
const { uiActions: { set }, test_mode } = this.props;
this.setState({ toggling_test_mode: true });
this.props.uiActions.set({ test_mode: !this.props.test_mode });
set({ test_mode: !test_mode });
// Wait a second to allow state to update, and then refresh
setTimeout(location.reload(), 1000);
}
render() {
render = () => {
const {
uiActions,
spotifyActions,
history,
test_mode,
debug_response,
debug_info,
log_actions,
log_mopidy,
log_pusher,
log_snapcast,
access_token,
} = this.props;
const {
toggling_test_mode,
mopidy_call,
mopidy_data,
pusher_data,
snapcast_data,
} = this.state;
const options = (
<a className="button button--discrete button--no-hover" onClick={(e) => this.props.history.push('/settings')}>
<a className="button button--discrete button--no-hover" onClick={this.onBack}>
<Icon name="keyboard_backspace" />
Back
<I18n path="actions.back" />
</a>
);
return (
<div className="view debugger-view">
<Header options={options} uiActions={this.props.uiActions}>
<Header options={options} uiActions={uiActions}>
<Icon name="settings" type="material" />
Debug
<I18n path="debug.title" />
</Header>
<div className="content-wrapper">
<h4 className="underline">User interface</h4>
<h4 className="underline"><I18n path="debug.ui.title" /></h4>
<form>
<div className="field checkbox">
<div className="name">Test mode</div>
<div className="input">
{this.state.toggling_test_mode ? <span className="button button--working">Applying...</span> : (this.props.test_mode ? <span className="button button--destructive" onClick={(e) => this.toggleTestMode(e)}>Disable</span> : <span className="button button--primary" onClick={(e) => this.toggleTestMode(e)}>Enable</span>)}
{toggling_test_mode && (
<span className="button button--working">Applying...</span>
)}
{!toggling_test_mode && (
<span
className={`button button--${test_mode ? 'destructive' : 'default'}`}
onClick={this.toggleTestMode}
>
<I18n path={`actions.${test_mode ? 'disable' : 'enable'}`} />
</span>
)}
</div>
</div>
<div className="field checkbox">
<div className="name">Debug</div>
<div className="name"><I18n path="debug.debug.title" /></div>
<div className="input">
<label>
<input
type="checkbox"
name="debug_info"
checked={this.props.debug_info}
onChange={(e) => this.props.uiActions.set({ debug_info: !this.props.debug_info })}
checked={debug_info}
onChange={() => uiActions.set({ debug_info: !debug_info })}
/>
<span className="label">Show debug overlay</span>
<span className="label"><I18n path="debug.debug.overlay" /></span>
</label>
</div>
</div>
<div className="field checkbox">
<div className="name">Logging</div>
<div className="name"><I18n path="debug.logging.title" /></div>
<div className="input">
<label>
<input
type="checkbox"
name="log_actions"
checked={this.props.log_actions}
onChange={(e) => this.props.uiActions.set({ log_actions: !this.props.log_actions })}
checked={log_actions}
onChange={() => uiActions.set({ log_actions: !log_actions })}
/>
<span className="label">Log actions</span>
<span className="label"><I18n path="debug.logging.actions" /></span>
</label>
<label>
<input
type="checkbox"
name="log_mopidy"
checked={this.props.log_mopidy}
onChange={(e) => this.props.uiActions.set({ log_mopidy: !this.props.log_mopidy })}
checked={log_mopidy}
onChange={() => uiActions.set({ log_mopidy: !log_mopidy })}
/>
<span className="label">Log Mopidy</span>
<span className="label"><I18n path="debug.logging.mopidy" /></span>
</label>
<label>
<input
type="checkbox"
name="log_pusher"
checked={this.props.log_pusher}
onChange={(e) => this.props.uiActions.set({ log_pusher: !this.props.log_pusher })}
checked={log_pusher}
onChange={() => uiActions.set({ log_pusher: !log_pusher })}
/>
<span className="label">Log Pusher</span>
<span className="label"><I18n path="debug.logging.pusher" /></span>
</label>
<label>
<input
type="checkbox"
name="log_pusher"
checked={this.props.log_snapcast}
onChange={(e) => this.props.uiActions.set({ log_snapcast: !this.props.log_snapcast })}
checked={log_snapcast}
onChange={() => uiActions.set({ log_snapcast: !log_snapcast })}
/>
<span className="label">Log Snapcast</span>
<span className="label"><I18n path="debug.logging.snapcast" /></span>
</label>
</div>
</div>
<div className="field">
<div className="name" />
<div className="input">
<a className="button button--default" onClick={(e) => this.props.uiActions.createNotification({ content: 'Test notification' })}>Create notification</a>
<a className="button button--default" onClick={(e) => this.props.uiActions.startProcess('test_process', "Test process", {remaining: 68, total: 100})}>Create process notification</a>
<a className="button button--default" onClick={(e) => this.props.pusherActions.request('test')}>Run test process</a>
<a className="button button--default" onClick={(e) => this.props.uiActions.createNotification({ content: 'Test notification' })}><I18n path="debug.create_notification" /></a>
<a className="button button--default" onClick={(e) => this.props.uiActions.startProcess('test_process', "Test process", {remaining: 68, total: 100})}><I18n path="debug.create_process" /></a>
<a className="button button--default" onClick={(e) => this.props.pusherActions.request('test')}><I18n path="debug.run_test" /></a>
</div>
</div>
</form>
<h4 className="underline">Spotify</h4>
<label className="field">
<div className="name">Access token</div>
<div className="name"><I18n path="debug.access_token" /></div>
<div className="input">
<input
type="text"
onChange={(e) => this.props.spotifyActions.authorizationGranted({ access_token: e.target.value })}
value={this.state.access_token}
onChange={(e) => spotifyActions.authorizationGranted({ access_token: e.target.value })}
value={access_token}
/>
</div>
</label>
<h4 className="underline">Mopidy</h4>
<form onSubmit={(e) => this.callMopidy(e)}>
<h4 className="underline"><I18n path="services.mopidy.title" /></h4>
<form onSubmit={this.callMopidy}>
<label className="field">
<div className="name">Call</div>
<div className="name"><I18n path="debug.call" /></div>
<div className="input">
<input
type="text"
onChange={(e) => this.setState({ mopidy_call: e.target.value })}
value={this.state.mopidy_call}
value={mopidy_call}
/>
</div>
</label>
<label className="field">
<div className="name">Data</div>
<div className="name"><I18n path="debug.data" /></div>
<div className="input">
<textarea
onChange={(e) => this.setState({ mopidy_data: e.target.value })}
value={this.state.mopidy_data}
value={mopidy_data}
/>
</div>
</label>
@ -181,10 +226,10 @@ Back
</div>
</form>
<h4 className="underline">Pusher</h4>
<form onSubmit={(e) => this.callPusher(e)}>
<h4 className="underline"><I18n path="services.pusher.title" /></h4>
<form onSubmit={this.callPusher}>
<label className="field">
<div className="name">Examples</div>
<div className="name"><I18n path="debug.examples" /></div>
<div className="input">
<select onChange={(e) => this.setState({ pusher_data: e.target.value })}>
<option value='{"method":"get_config"}'>Get config</option>
@ -202,44 +247,44 @@ Back
</div>
</label>
<label className="field">
<div className="name">Data</div>
<div className="name"><I18n path="debug.data" /></div>
<div className="input">
<textarea
onChange={(e) => this.setState({ pusher_data: e.target.value })}
value={this.state.pusher_data}
value={pusher_data}
/>
</div>
</label>
<div className="field">
<div className="name" />
<div className="input">
<button type="submit" className="button button--default">Send</button>
<button type="submit" className="button button--default"><I18n path="actions.send" /></button>
</div>
</div>
</form>
<h4 className="underline">Snapcast</h4>
<h4 className="underline"><I18n path="services.snapcast.title" /></h4>
<form onSubmit={(e) => this.callSnapcast(e)}>
<label className="field">
<div className="name">Data</div>
<div className="name"><I18n path="debug.data" /></div>
<div className="input">
<textarea
onChange={(e) => this.setState({ snapcast_data: e.target.value })}
value={this.state.snapcast_data}
value={snapcast_data}
/>
</div>
</label>
<div className="field">
<div className="name" />
<div className="input">
<button type="submit" className="button button--default">Send</button>
<button type="submit" className="button button--default"><I18n path="actions.send" /></button>
</div>
</div>
</form>
<h4 className="underline">Response</h4>
<h4 className="underline"><I18n path="debug.response" /></h4>
<pre>
{ this.props.debug_response ? JSON.stringify(this.props.debug_response, null, 2) : null }
{debug_response && JSON.stringify(debug_response, null, 2)}
</pre>
</div>
@ -248,7 +293,7 @@ Back
}
}
const mapStateToProps = (state, ownProps) => ({
const mapStateToProps = (state) => ({
connection_id: state.pusher.connection_id,
access_token: (state.spotify.access_token ? state.spotify.access_token : ''),
log_actions: (state.ui.log_actions ? state.ui.log_actions : false),

View File

@ -9,7 +9,7 @@ import TrackList from '../components/TrackList';
import Thumbnail from '../components/Thumbnail';
import Parallax from '../components/Parallax';
import NiceNumber from '../components/NiceNumber';
import Dater from '../components/Dater';
import { Dater } from '../components/Dater';
import LazyLoadListener from '../components/LazyLoadListener';
import FollowButton from '../components/Fields/FollowButton';
import Loader from '../components/Loader';

View File

@ -5,7 +5,7 @@ import Link from '../components/Link';
import Icon from '../components/Icon';
import Parallax from '../components/Parallax';
import TrackList from '../components/TrackList';
import Dater from '../components/Dater';
import { Dater } from '../components/Dater';
import LinksSentence from '../components/LinksSentence';
import Thumbnail from '../components/Thumbnail';
import Header from '../components/Header';
@ -20,7 +20,7 @@ import {
getFromUri,
uriType,
} from '../util/helpers';
import { content, Content } from '../locale';
import { i18n, I18n } from '../locale';
class Queue extends React.Component {
constructor(props) {
@ -40,7 +40,7 @@ class Queue extends React.Component {
if (limit) {
this.setState({ limit });
}
setWindowTitle(content('now_playing.title'));
setWindowTitle(i18n('now_playing.title'));
}
shouldComponentUpdate(nextProps) {
@ -77,7 +77,7 @@ class Queue extends React.Component {
}
}
loadMore() {
loadMore = () => {
const { limit, per_page } = this.state;
const { location, history } = this.props;
const new_limit = limit + per_page;
@ -92,7 +92,7 @@ class Queue extends React.Component {
history.replace({ state });
}
removeTracks(track_indexes) {
removeTracks = (track_indexes) => {
const { queue_tracks, mopidyActions } = this.props;
const tlids = [];
for (let i = 0; i < track_indexes.length; i++) {
@ -107,21 +107,23 @@ class Queue extends React.Component {
}
}
playTrack(track) {
this.props.mopidyActions.changeTrack(track.tlid);
playTrack = (track) => {
const { mopidyActions: { changeTrack } } = this.props;
changeTrack(track.tlid);
}
playTracks(tracks) {
this.props.mopidyActions.changeTrack(tracks[0].tlid);
playTracks = (tracks) => {
const { mopidyActions: { changeTrack } } = this.props;
changeTrack(tracks[0].tlid);
}
reorderTracks(indexes, index) {
this.props.mopidyActions.reorderTracklist(indexes, index);
reorderTracks = (indexes, index) => {
const { mopidyActions: { reorderTracklist } } = this.props;
reorderTracklist(indexes, index);
}
renderArtwork(image) {
const { current_track } = this.props;
const uri = (current_track && current_track.album && current_track.album.uri) ? current_track.album.uri : null;
renderArtwork = (image) => {
const { current_track: { album: { uri } = {} } = {} } = this.props;
if (!image) {
return (
@ -145,7 +147,7 @@ class Queue extends React.Component {
);
}
renderAddedFrom() {
renderAddedFrom = () => {
const { added_from_uri } = this.props;
if (!added_from_uri) return null;
@ -203,7 +205,7 @@ class Queue extends React.Component {
<LinksSentence items={items} />
{uri_type === 'radio' && (
<span className="flag flag--blue">
{content('now_playing.current_track.radio')}
{i18n('now_playing.current_track.radio')}
</span>
)}
</div>
@ -211,7 +213,7 @@ class Queue extends React.Component {
);
}
render() {
render = () => {
const { current_track, queue_tracks, theme } = this.props;
const { limit } = this.state;
const total_queue_tracks = queue_tracks.length;
@ -229,16 +231,16 @@ class Queue extends React.Component {
{this.props.spotify_enabled && (
<Link className="button button--no-hover" to="/queue/radio">
<Icon name="radio" />
{content('now_playing.context_actions.radio')}
<I18n path="now_playing.context_actions.radio" />
</Link>
)}
<Link className="button button--no-hover" to="/queue/history">
<Icon name="history" />
{content('now_playing.context_actions.history')}
<I18n path="now_playing.context_actions.history" />
</Link>
<Link className="button button--no-hover" to="/queue/add-uri">
<Icon name="playlist_add" />
{content('now_playing.context_actions.add_uri')}
<I18n path="now_playing.context_actions.add_uri" />
</Link>
</span>
);
@ -247,7 +249,7 @@ class Queue extends React.Component {
<div className="view queue-view preserve-3d">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="play_arrow" type="material" />
{content('now_playing.title')}
<I18n path="now_playing.title" />
</Header>
{theme === 'dark' && <Parallax image={current_track_image} blur />}
<div className="content-wrapper">
@ -283,7 +285,7 @@ class Queue extends React.Component {
<li>
<a onClick={this.props.mopidyActions.shuffleTracklist}>
<Icon name="shuffle" />
{content('now_playing.current_track.shuffle')}
<I18n path="now_playing.current_track.shuffle" />
</a>
</li>
)}
@ -291,7 +293,7 @@ class Queue extends React.Component {
<li>
<a onClick={this.props.mopidyActions.clearTracklist}>
<Icon name="delete_sweep" />
{content('now_playing.current_track.clear')}
<I18n path="now_playing.current_track.clear" />
</a>
</li>
)}
@ -307,10 +309,10 @@ class Queue extends React.Component {
track_context="queue"
className="queue-track-list"
tracks={tracks}
removeTracks={(track_indexes) => this.removeTracks(track_indexes)}
playTracks={(tracks) => this.playTracks(tracks)}
playTrack={(track) => this.playTrack(track)}
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
removeTracks={this.removeTracks}
playTracks={this.playTracks}
playTrack={this.playTrack}
reorderTracks={this.reorderTracks}
/>
</section>
@ -321,7 +323,7 @@ class Queue extends React.Component {
: total_queue_tracks
}
showLoader={limit < total_queue_tracks}
loadMore={() => this.loadMore()}
loadMore={this.loadMore}
/>
</div>
</div>

View File

@ -20,7 +20,7 @@ import * as mopidyActions from '../services/mopidy/actions';
import * as lastfmActions from '../services/lastfm/actions';
import * as spotifyActions from '../services/spotify/actions';
import { isHosted } from '../util/helpers';
import { content } from '../locale';
import { i18n } from '../locale';
class Settings extends React.Component {
constructor(props) {
@ -37,7 +37,7 @@ class Settings extends React.Component {
componentDidMount() {
const { uiActions: { setWindowTitle } } = this.props;
setWindowTitle(content('settings.title'));
setWindowTitle(i18n('settings.title'));
}
componentDidUpdate = () => {

View File

@ -8,7 +8,7 @@ import Header from '../components/Header';
import Thumbnail from '../components/Thumbnail';
import LinksSentence from '../components/LinksSentence';
import LastfmLoveButton from '../components/Fields/LastfmLoveButton';
import Dater from '../components/Dater';
import { Dater } from '../components/Dater';
import SelectField from '../components/Fields/SelectField';
import ContextMenuTrigger from '../components/ContextMenuTrigger';
import Icon from '../components/Icon';