Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Max Erenberg
2020-09-01 02:10:59 -04:00
100 changed files with 4777 additions and 2374 deletions

View File

@ -26,6 +26,7 @@ import {
sourceIcon,
} from '../util/helpers';
import { collate } from '../util/format';
import Button from '../components/Button';
export class Album extends React.Component {
componentDidMount = () => {
@ -66,12 +67,10 @@ export class Album extends React.Component {
componentDidUpdate = ({
uri: prevUri,
album: prevAlbum,
mopidy_connected: prev_mopidy_connected,
}) => {
const {
uri,
album,
mopidy_connected,
coreActions: {
loadAlbum,
},
@ -82,15 +81,11 @@ export class Album extends React.Component {
if (uri !== prevUri) {
loadAlbum(uri);
} else if (!prev_mopidy_connected && mopidy_connected) {
if (uriSource(uri) !== 'spotify') {
loadAlbum(uri);
}
}
// We have just received our full album or our album artists
if ((!prevAlbum && album) || (!prevAlbum.artists && album.artists)) {
if (album.wiki === undefined && album.artists.length > 0) {
if (album.artists && album.wiki === undefined) {
getAlbum(album.uri, album.artists[0].name, album.name);
}
}
@ -260,12 +255,15 @@ export class Album extends React.Component {
</div>
<div className="actions">
<button type="button" className="button button--primary" onClick={this.play}>
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Album', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
</Button>
{uriSource(uri) === 'spotify' && (
<FollowButton
className="secondary"
uri={uri}
is_following={this.inLibrary()}
/>
@ -320,7 +318,6 @@ const mapStateToProps = (state, ownProps) => {
spotify_library_albums: state.spotify.library_albums,
local_library_albums: state.mopidy.library_albums,
spotify_authorized: state.spotify.authorization,
mopidy_connected: state.mopidy.connected,
};
};

View File

@ -33,6 +33,8 @@ import {
import { collate } from '../util/format';
import { sortItems, applyFilter } from '../util/arrays';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
import { trackEvent } from '../components/Trackable';
class Artist extends React.Component {
componentDidMount() {
@ -42,12 +44,10 @@ class Artist extends React.Component {
componentDidUpdate = ({
uri: prevUri,
mopidy_connected: prev_mopidy_connected,
artist: prevArtist,
}) => {
const {
uri,
mopidy_connected,
artist,
coreActions: {
loadArtist,
@ -56,10 +56,6 @@ class Artist extends React.Component {
if (uri !== prevUri) {
loadArtist(uri);
} else if (!prev_mopidy_connected && mopidy_connected) {
if (uriSource(uri) !== 'spotify') {
loadArtist(uri);
}
}
if (!prevArtist && artist) this.setWindowTitle(artist);
@ -69,12 +65,14 @@ class Artist extends React.Component {
onResetFilters = () => {
this.onChangeFilter(null);
this.onChangeSort(null);
trackEvent({ category: 'Artist', action: 'FilterAlbums', label: 'Reset' });
}
onChangeFilter = (value) => {
const { uiActions: { set, hideContextMenu } } = this.props;
set({ artist_albums_filter: value });
hideContextMenu();
trackEvent({ category: 'Artist', action: 'FilterAlbums', label: value });
}
onChangeSort = (value) => {
@ -88,7 +86,7 @@ class Artist extends React.Component {
} = this.props;
let reverse = false;
if (value !== null && sort == value) {
if (value !== null && sort === value) {
reverse = !sort_reverse;
}
@ -97,6 +95,7 @@ class Artist extends React.Component {
artist_albums_sort: value,
});
hideContextMenu();
trackEvent({ category: 'Artist', action: 'SortAlbums', label: `${value} ${reverse ? 'DESC' : 'ASC'}` });
}
onPlay = () => {
@ -192,38 +191,38 @@ class Artist extends React.Component {
const sort_options = [
{
value: null,
label: 'Default',
label: i18n('artist.albums.sort.default'),
},
{
value: 'name',
label: 'Name',
label: i18n('artist.albums.sort.name'),
},
{
value: 'release_date',
label: 'Date',
label: i18n('artist.albums.sort.release_date'),
},
{
value: 'tracks_uris.length',
label: 'Tracks',
label: i18n('artist.albums.sort.track_count'),
},
];
const filter_options = [
{
value: null,
label: 'All',
label: i18n('artist.albums.filter.all'),
},
{
value: 'album',
label: 'Albums',
label: i18n('artist.albums.filter.albums'),
},
{
value: 'single',
label: 'Singles',
label: i18n('artist.albums.filter.singles'),
},
{
value: 'compilation',
label: 'Compilations',
label: i18n('artist.albums.filter.compilations'),
},
];
@ -254,13 +253,12 @@ class Artist extends React.Component {
uiActions={uiActions}
/>
</div>
<Link
<Button
to={`/artist/${encodeURIComponent(uri)}/related-artists`}
scrollTo="#sub-views-menu"
className="button button--default"
>
<I18n path="artist.overview.related_artists.more" />
</Link>
</Button>
</div>
)}
@ -288,10 +286,15 @@ class Artist extends React.Component {
handleChange={this.onChangeFilter}
/>
{(sort || filter) && (
<a className="button button--discrete button--destructive button--small" onClick={this.onResetFilters}>
<Button
discrete
type="destructive"
size="small"
onClick={this.onResetFilters}
>
<Icon name="clear" />
<I18n path="actions.reset" />
</a>
</Button>
)}
</h4>
@ -497,9 +500,13 @@ class Artist extends React.Component {
<div className="heading__content">
<h1>{this.props.artist ? this.props.artist.name : null}</h1>
<div className="actions">
<button className="button button--primary" onClick={this.onPlay}>
<Button
type="primary"
onClick={this.onPlay}
tracking={{ category: 'Artist', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
</Button>
{is_spotify && (
<FollowButton
uri={uri}
@ -596,7 +603,6 @@ const mapStateToProps = (state, ownProps) => {
sort: (state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null),
sort_reverse: (!!state.ui.artist_albums_sort_reverse),
spotify_authorized: state.spotify.authorization,
mopidy_connected: state.mopidy.connected,
};
};

View File

@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Header from '../components/Header';
import Icon from '../components/Icon';
import Button from '../components/Button';
import * as uiActions from '../services/ui/actions';
import * as pusherActions from '../services/pusher/actions';
import * as mopidyActions from '../services/mopidy/actions';
@ -85,12 +85,16 @@ class Debug extends React.Component {
pusher_data,
snapcast_data,
} = this.state;
const options = (
<a className="button button--discrete button--no-hover" onClick={this.onBack}>
<Button
onClick={this.onBack}
noHover
discrete
>
<Icon name="keyboard_backspace" />
<I18n path="actions.back" />
</a>
</Button>
);
return (
@ -107,17 +111,14 @@ class Debug extends React.Component {
<div className="field checkbox">
<div className="name">Test mode</div>
<div className="input">
{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>
)}
<Button
type={test_mode ? 'destructive' : null}
working={toggling_test_mode}
onClick={this.toggleTestMode}
tracking={{ category: 'Debug', action: 'TestMode', label: test_mode ? 'Disable' : 'Enable' }}
>
<I18n path={`actions.${test_mode ? 'disable' : 'enable'}`} />
</Button>
</div>
</div>
<div className="field checkbox">
@ -178,9 +179,24 @@ class Debug extends React.Component {
<div className="field">
<div className="name" />
<div className="input">
<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>
<Button
onClick={() => this.props.uiActions.createNotification({ content: 'Test notification' })}
tracking={{ category: 'Debug', action: 'Test notification' }}
>
<I18n path="debug.create_notification" />
</Button>
<Button
onClick={() => this.props.uiActions.startProcess('test_process', "Test process", {remaining: 68, total: 100})}
tracking={{ category: 'Debug', action: 'Test process' }}
>
<I18n path="debug.create_process" />
</Button>
<Button
onClick={() => this.props.pusherActions.request('test')}
tracking={{ category: 'Debug', action: 'Run test process' }}
>
<I18n path="debug.run_test" />
</Button>
</div>
</div>
</form>
@ -221,7 +237,12 @@ class Debug extends React.Component {
<div className="field">
<div className="name" />
<div className="input">
<button type="submit" className="button button--default">Send</button>
<Button
submit
tracking={{ category: 'Debug', action: 'Mopidy', label: 'Send' }}
>
<I18n path="actions.send" />
</Button>
</div>
</div>
</form>
@ -258,7 +279,12 @@ class Debug extends React.Component {
<div className="field">
<div className="name" />
<div className="input">
<button type="submit" className="button button--default"><I18n path="actions.send" /></button>
<Button
submit
tracking={{ category: 'Debug', action: 'Pusher', label: 'Send' }}
>
<I18n path="actions.send" />
</Button>
</div>
</div>
</form>
@ -277,7 +303,12 @@ class Debug extends React.Component {
<div className="field">
<div className="name" />
<div className="input">
<button type="submit" className="button button--default"><I18n path="actions.send" /></button>
<Button
submit
tracking={{ category: 'Debug', action: 'Snapcast', label: 'Send' }}
>
<I18n path="actions.send" />
</Button>
</div>
</div>
</form>

View File

@ -2,13 +2,12 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ReactGA from 'react-ga';
import ErrorMessage from '../components/ErrorMessage';
import Link from '../components/Link';
import Button from '../components/Button';
import TrackList from '../components/TrackList';
import Thumbnail from '../components/Thumbnail';
import Parallax from '../components/Parallax';
import NiceNumber, { nice_number } from '../components/NiceNumber';
import PinButton from '../components/Fields/PinButton';
import { nice_number } from '../components/NiceNumber';
import { Dater, dater } from '../components/Dater';
import LazyLoadListener from '../components/LazyLoadListener';
import FollowButton from '../components/Fields/FollowButton';
@ -53,12 +52,10 @@ class Playlist extends React.Component {
componentDidUpdate = ({
uri: prevUri,
playlist: prevPlaylist,
mopidy_connected: prev_mopidy_connected,
}) => {
const {
uri,
playlist,
mopidy_connected,
coreActions: {
loadPlaylist,
},
@ -73,10 +70,6 @@ class Playlist extends React.Component {
if (uri !== prevUri) {
loadPlaylist(uri);
} else if (!prev_mopidy_connected && mopidy_connected) {
if (uriSource(uri) !== 'spotify') {
loadPlaylist(uri);
}
}
if (!prevPlaylist && playlist) this.setWindowTitle(playlist);
@ -183,27 +176,49 @@ class Playlist extends React.Component {
return library.indexOf(uri) > -1;
}
togglePinned = () => {
const {
uri,
coreActions: {
addPinned,
removePinned,
},
} = this.props;
if (this.isPinned()) {
removePinned(uri);
} else {
addPinned(uri);
}
}
renderActions = () => {
const {
uri,
playlist: {
can_edit,
name,
},
} = this.props;
switch (uriSource(this.props.uri)) {
switch (uriSource(uri)) {
case 'm3u':
return (
<div className="actions">
<button className="button button--primary" onClick={this.play}>
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Playlist', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
<Link
className="button button--default"
</Button>
<Button
to={`/playlist/${encodeURIComponent(uri)}/edit`}
tracking={{ category: 'Playlist', action: 'Edit' }}
>
<I18n path="actions.edit" />
</Link>
</Button>
<PinButton item={{ uri, name }} />
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
@ -212,28 +227,38 @@ class Playlist extends React.Component {
if (can_edit) {
return (
<div className="actions">
<button className="button button--primary" onClick={this.play}>
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Playlist', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
<Link
className="button button--default"
</Button>
<Button
to={`/playlist/${encodeURIComponent(uri)}/edit`}
tracking={{ category: 'Playlist', action: 'Edit' }}
>
<I18n path="actions.edit" />
</Link>
</Button>
<PinButton item={{ uri, name }} />
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
}
return (
<div className="actions">
<button className="button button--primary" onClick={this.play}>
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Playlist', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
</Button>
<FollowButton
uri={uri}
is_following={this.inLibrary()}
/>
<PinButton item={{ uri, name }} />
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
@ -241,12 +266,14 @@ class Playlist extends React.Component {
default:
return (
<div className="actions">
<button
className="button button--primary"
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Playlist', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
</Button>
<PinButton item={{ uri, name }} />
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
@ -386,7 +413,6 @@ const mapStateToProps = (state, ownProps) => {
me = {},
} = {},
mopidy: {
connected: mopidy_connected,
library_playlists: local_library_playlists,
} = {},
} = state;
@ -404,7 +430,6 @@ const mapStateToProps = (state, ownProps) => {
playlist: (playlists[uri] !== undefined ? playlists[uri] : false),
spotify_library_playlists,
local_library_playlists,
mopidy_connected,
spotify_authorized,
spotify_userid: (me && me.id) || null,
};

View File

@ -21,6 +21,7 @@ import {
uriType,
} from '../util/helpers';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
const Artwork = ({
image,
@ -236,19 +237,19 @@ class Queue extends React.Component {
const options = (
<span>
{this.props.spotify_enabled && (
<Link className="button button--no-hover" to="/queue/radio">
<Button noHover discrete to="/queue/radio">
<Icon name="radio" />
<I18n path="now_playing.context_actions.radio" />
</Link>
</Button>
)}
<Link className="button button--no-hover" to="/queue/history">
<Button noHover discrete to="/queue/history">
<Icon name="history" />
<I18n path="now_playing.context_actions.history" />
</Link>
<Link className="button button--no-hover" to="/queue/add-uri">
</Button>
<Button noHover discrete to="/queue/add-uri">
<Icon name="playlist_add" />
<I18n path="actions.add" />
</Link>
</Button>
</span>
);

View File

@ -10,6 +10,7 @@ import * as pusherActions from '../services/pusher/actions';
import * as spotifyActions from '../services/spotify/actions';
import * as mopidyActions from '../services/mopidy/actions';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
class QueueHistory extends React.Component {
componentDidMount() {
@ -18,19 +19,18 @@ class QueueHistory extends React.Component {
this.loadHistory();
}
componentDidUpdate = ({ mopidy_connected: prev_mopidy_connected }) => {
const { mopidy_connected } = this.props;
if (!prev_mopidy_connected && mopidy_connected) this.loadHistory();
componentDidUpdate = () => {
this.loadHistory();
}
loadHistory = (props = this.props) => {
loadHistory = () => {
const {
mopidyActions: { getQueueHistory },
mopidyActions: {
getQueueHistory,
},
} = this.props;
if (props.mopidy_connected) {
getQueueHistory();
}
getQueueHistory();
}
onBack = () => {
@ -46,12 +46,16 @@ class QueueHistory extends React.Component {
} = this.props;
const options = (
<a className="button button--no-hover" onClick={this.onBack}>
<Button
onClick={this.onBack}
noHover
tracking={{ category: 'QueueHistory', action: 'Back' }}
>
<I18n path="actions.back">
<Icon name="keyboard_backspace" />
{' '}
</I18n>
</a>
</Button>
);
const tracks = queue_history.map((item) => ({
@ -82,7 +86,6 @@ class QueueHistory extends React.Component {
}
const mapStateToProps = (state) => ({
mopidy_connected: state.mopidy.connected,
tracks: (state.core.tracks ? state.core.tracks : {}),
queue_history: (state.mopidy.queue_history ? state.mopidy.queue_history : []),
});

View File

@ -21,6 +21,7 @@ import * as lastfmActions from '../services/lastfm/actions';
import * as spotifyActions from '../services/spotify/actions';
import { isHosted } from '../util/helpers';
import { i18n, I18n, languagesAvailable } from '../locale';
import Button from '../components/Button';
class Settings extends React.Component {
constructor(props) {
@ -127,13 +128,13 @@ class Settings extends React.Component {
const loading = processes.local_scan && processes.local_scan.status === 'running';
return (
<button
className={`button button--default ${loading ? 'button--working' : ''}`}
<Button
working={loading}
onClick={this.doLocalScan}
type="button"
tracking={{ category: 'System', action: 'LocalScan' }}
>
<I18n path="settings.advanced.start_local_scan" />
</button>
</Button>
);
}
@ -159,24 +160,26 @@ class Settings extends React.Component {
const options = (
<span>
<Link
className="button button--discrete button--no-hover"
<Button
discrete
noHover
to="/settings/debug"
>
<I18n path="debug.title">
<Icon name="code" />
</I18n>
</Link>
<a
className="button button--discrete button--no-hover"
</Button>
<Button
href="https://github.com/jaedb/Iris/wiki"
target="_blank"
rel="noreferrer noopener"
discrete
noHover
>
<I18n path="settings.help">
<Icon name="help" />
</I18n>
</a>
</Button>
</span>
);
@ -403,9 +406,9 @@ class Settings extends React.Component {
runCommand={(id, notify) => runCommand(id, notify)}
onChange={(commands) => setCommands(commands)}
/>
<Link to="/edit-command" className="button button--default">
<Button to="/edit-command">
<I18n path="actions.add" />
</Link>
</Button>
</div>
</div>
@ -504,43 +507,40 @@ class Settings extends React.Component {
<div className="field">
{this.renderLocalScanButton()}
<Link
className="button button--default"
to="/share-configuration"
>
<Button to="/share-configuration">
<I18n path="settings.advanced.share_configuration" />
</Link>
</Button>
</div>
<div className="field">
{pusher.version.upgrade_available && (
<button
className="button button--secondary"
<Button
type="secondary"
onClick={this.doUpgrade}
type="button"
tracking={{ category: 'System', action: 'Upgrade', label: pusher.version.latest }}
>
<I18n path="settings.advanced.version.upgrade" version={pusher.version.latest} />
</button>
</Button>
)}
<button
className={`button button--destructive${mopidy.restarting ? ' button--working' : ''}`}
<Button
type="destructive"
working={mopidy.restarting}
onClick={this.doRestart}
type="button"
tracking={{ category: 'System', action: 'Restart' }}
>
<I18n path="settings.advanced.restart" />
</button>
</Button>
<ConfirmationButton
className="button--destructive"
content={i18n('settings.advanced.reset')}
onConfirm={this.resetAllSettings}
/>
<button
className="button button--destructive"
<Button
type="destructive"
onClick={this.resetServiceWorkerAndCache}
type="button"
tracking={{ category: 'System', action: 'ResetCache' }}
>
<I18n path="settings.advanced.reset_cache" />
</button>
</Button>
</div>
<h4 className="underline">
@ -563,18 +563,26 @@ class Settings extends React.Component {
<br />
<br />
<div>
<a className="button button--default" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=james%40barnsley%2enz&lc=NZ&item_name=James%20Barnsley&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted" target="_blank">
<Button
href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=james%40barnsley%2enz&lc=NZ&item_name=James%20Barnsley&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted"
target="_blank"
tracking={{ category: 'About', action: 'Paypal' }}
>
<I18n path="settings.about.donate">
<Icon type="fontawesome" name="paypal" />
{' '}
</I18n>
</a>
<a className="button button--default" href="https://github.com/jaedb/Iris" target="_blank">
</Button>
<Button
href="https://github.com/jaedb/Iris"
target="_blank"
tracking={{ category: 'About', action: 'GitHub' }}
>
<I18n path="settings.about.github">
<Icon type="fontawesome" name="github" />
{' '}
</I18n>
</a>
</Button>
</div>
</section>

View File

@ -27,6 +27,7 @@ import {
uriType,
} from '../util/helpers';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
class Track extends React.Component {
componentDidMount() {
@ -52,14 +53,12 @@ class Track extends React.Component {
componentDidUpdate = ({
uri: prevUri,
track: prevTrack,
mopidy_connected: prev_mopidy_connected,
}) => {
const {
uri,
track,
genius_authorized,
lastfm_authorized,
mopidy_connected,
coreActions: {
loadTrack,
},
@ -70,14 +69,13 @@ class Track extends React.Component {
getTrack,
},
} = this.props;
// if our URI has changed, fetch new track
if (prevUri !== uri) {
loadTrack(uri);
if (genius_authorized && track.artists) findTrackLyrics(track);
// if mopidy has just connected AND we're not a Spotify track, go get
} else if (!prev_mopidy_connected && mopidy_connected) {
if (uriSource(uri) !== 'spotify') loadTrack(uri);
if (genius_authorized && track.artists) {
findTrackLyrics(track);
}
}
// We have just received our full track or our track artists
@ -299,9 +297,13 @@ class Track extends React.Component {
</div>
<div className="actions">
<button className="button button--primary" onClick={this.play} type="button">
<Button
type="primary"
onClick={this.play}
tracking={{ category: 'Track', action: 'Play' }}
>
<I18n path="actions.play" />
</button>
</Button>
<LastfmLoveButton
uri={uri}
artist={(track.artists ? track.artists[0].name : null)}
@ -370,7 +372,6 @@ const mapStateToProps = (state, ownProps) => {
lastfm_authorized: state.lastfm.authorization,
spotify_authorized: state.spotify.authorization,
genius_authorized: state.genius.authorization,
mopidy_connected: state.mopidy.connected,
};
};

View File

@ -11,6 +11,7 @@ import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { isLoading } from '../../util/helpers';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class DiscoverFeatured extends React.Component {
componentDidMount() {
@ -89,10 +90,14 @@ class DiscoverFeatured extends React.Component {
}
const options = (
<a className="button button--no-hover" onClick={this.onRefresh}>
<Button
noHover
onClick={this.onRefresh}
tracking={{ category: 'DiscoverFeatured', action: 'Refresh' }}
>
<Icon name="refresh" />
<I18n path="actions.refresh" />
</a>
</Button>
);
return (

View File

@ -13,6 +13,7 @@ import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { isLoading } from '../../util/helpers';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class DiscoverNewReleases extends React.Component {
componentDidMount() {
@ -119,10 +120,14 @@ class DiscoverNewReleases extends React.Component {
}
const options = (
<a className="button button--no-hover" onClick={this.refresh}>
<Button
noHover
onClick={this.refresh}
tracking={{ category: 'DiscoverFeatured', action: 'Refresh' }}
>
<Icon name="refresh" />
<I18n path="actions.refresh" />
</a>
</Button>
);
return (

View File

@ -24,6 +24,7 @@ import {
} from '../../util/helpers';
import { arrayOf, indexToArray } from '../../util/arrays';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class Discover extends React.Component {
constructor(props) {
@ -526,9 +527,13 @@ class Discover extends React.Component {
<I18n path="discover.recommendations.tracks" />
<div className="pull-right">
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
<button className="button button--primary" onClick={this.playTracks} type="button">
<Button
type="primary"
onClick={this.playTracks}
tracking={{ category: 'DiscoverRecommendations', action: 'Play' }}
>
<I18n path="actions.play_all" />
</button>
</Button>
</div>
</h4>
<TrackList className="discover-track-list" uri={this.uri()} tracks={tracks} />
@ -608,15 +613,19 @@ class Discover extends React.Component {
handleChange={this.toggleTunability}
/>
<div className="intro__actions__separator" />
<span
className={`submit button button--primary button--large${is_loading ? ' button--working' : ''}`}
<Button
className="submit"
type="primary"
size="large"
working={is_loading}
onClick={this.getRecommendations}
tracking={{ category: 'DiscoverRecommendations', action: 'Submit' }}
>
<Icon name="explore" />
<I18n path="discover.recommendations.find_recommendations" afterContent>
{' '}
</I18n>
</span>
</Button>
</div>
</div>

View File

@ -40,16 +40,6 @@ class LibraryAlbums extends React.Component {
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
@ -60,59 +50,71 @@ class LibraryAlbums extends React.Component {
}
setWindowTitle(i18n('library.albums.title'));
this.getMopidyLibrary();
this.getGoogleLibrary();
this.getSpotifyLibrary();
}
if (mopidy_connected && mopidy_library_albums_status !== 'finished' && mopidy_library_albums_status !== 'started' && (source === 'all' || source === 'local')) {
mopidyActions.getLibraryAlbums();
}
componentDidUpdate = ({ source: prevSource }) => {
const { source } = this.props;
if (google_available && google_library_albums_status !== 'finished' && google_library_albums_status !== 'started' && (source === 'all' || source === 'google')) {
googleActions.getLibraryAlbums();
}
if (spotify_available && spotify_library_albums_status !== 'finished' && spotify_library_albums_status !== 'started' && (source == 'all' || source === 'spotify')) {
spotifyActions.getLibraryAlbums();
if (source !== prevSource) {
this.getMopidyLibrary();
this.getGoogleLibrary();
this.getSpotifyLibrary();
}
}
componentDidUpdate = ({
mopidy_connected: prev_mopidy_connected,
}) => {
getMopidyLibrary = () => {
const {
mopidy_connected,
google_available,
spotify_available,
source,
mopidyActions,
googleActions,
spotifyActions,
mopidy_library_albums_status,
google_library_albums_status,
spotify_library_albums_status,
mopidy_library_playlists,
mopidyActions: {
getLibraryAlbums,
},
} = this.props;
if (mopidy_connected && (source === 'all' || source === 'local')) {
if (!prev_mopidy_connected) mopidyActions.getLibraryAlbums();
if (source !== 'local' && source !== 'all') return;
if (mopidy_library_playlists) return;
// Filter changed, but we haven't got this provider's library yet
if (source !== 'all' && source !== 'local' && mopidy_library_albums_status !== 'finished' && mopidy_library_albums_status !== 'started') {
mopidyActions.getLibraryAlbums();
}
}
getLibraryAlbums();
};
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();
}
}
getGoogleLibrary = () => {
const {
source,
google_available,
google_library_albums_status,
googleActions: {
getLibraryAlbums,
},
} = this.props;
if (spotify_available && (source === 'all' || source === 'spotify')) {
// Filter changed, but we haven't got this provider's library yet
if (spotify_library_albums_status !== 'finished' && spotify_library_albums_status !== 'started') {
spotifyActions.getLibraryAlbums();
}
}
}
if (!google_available) return;
if (source !== 'google' && source !== 'all') return;
if (google_library_albums_status === 'finished') return;
if (google_library_albums_status === 'started') return;
getLibraryAlbums();
};
getSpotifyLibrary = () => {
const {
source,
spotify_available,
spotify_library_albums_status,
spotifyActions: {
getLibraryAlbums,
},
} = this.props;
if (!spotify_available) return;
if (source !== 'spotify' && source !== 'all') return;
if (spotify_library_albums_status === 'finished') return;
if (spotify_library_albums_status === 'started') return;
getLibraryAlbums();
};
handleContextMenu = (e, item) => {
const {
@ -398,7 +400,6 @@ class LibraryAlbums extends React.Component {
}
const mapStateToProps = (state, ownProps) => ({
mopidy_connected: state.mopidy.connected,
mopidy_uri_schemes: state.mopidy.uri_schemes,
load_queue: state.ui.load_queue,
artists: state.core.artists,

View File

@ -31,6 +31,12 @@ class LibraryArtists extends React.Component {
}
componentDidMount() {
const {
uiActions: {
setWindowTitle,
},
} = this.props;
// Restore any limit defined in our location state
const state = (this.props.location.state ? this.props.location.state : {});
if (state.limit) {
@ -39,57 +45,72 @@ class LibraryArtists extends React.Component {
});
}
this.props.uiActions.setWindowTitle(i18n('library.artists.title'));
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();
}
this.getMopidyLibrary();
this.getGoogleLibrary();
this.getSpotifyLibrary();
}
if (this.props.google_available && !this.props.google_library_artists && this.props.mopidy_connected && (this.props.source == 'all' || this.props.source == 'google')) {
this.props.googleActions.getLibraryArtists();
}
componentDidUpdate = ({ source: prevSource }) => {
const { source } = this.props;
if (this.props.spotify_available && this.props.spotify_library_artists_status != 'finished' && (this.props.source == 'all' || this.props.source == 'spotify')) {
this.props.spotifyActions.getLibraryArtists();
if (source !== prevSource) {
this.getMopidyLibrary();
this.getGoogleLibrary();
this.getSpotifyLibrary();
}
}
componentDidUpdate = ({
mopidy_connected: prev_mopidy_connected,
}) => {
getMopidyLibrary = () => {
const {
source,
mopidy_connected,
google_available,
spotify_available,
mopidyActions,
googleActions,
spotifyActions,
mopidy_library_artists,
google_library_artists,
spotify_library_artists_status,
mopidyActions: {
getLibraryArtists,
},
} = this.props;
if (mopidy_connected && (source === 'all' || source === 'local')) {
if (!prev_mopidy_connected) mopidyActions.getLibraryArtists();
if (source !== 'local' && source !== 'all') return;
if (mopidy_library_artists) return;
if (source !== 'all' && source !== 'local' && !mopidy_library_artists) {
mopidyActions.getLibraryArtists();
}
}
getLibraryArtists();
};
if (mopidy_connected && google_available && (source === 'all' || source === 'google')) {
if (source !== 'all' && source !== 'google' && !google_library_artists) {
googleActions.getLibraryArtists();
}
}
getGoogleLibrary = () => {
const {
source,
google_available,
google_library_artists,
googleActions: {
getLibraryArtists,
},
} = this.props;
if (spotify_available && (source === 'all' || source === 'spotify')) {
if (spotify_library_artists_status !== 'finished' && spotify_library_artists_status !== 'started') {
spotifyActions.getLibraryArtists();
}
}
}
if (!google_available) return;
if (source !== 'google' && source !== 'all') return;
if (google_library_artists) return;
getLibraryArtists();
};
getSpotifyLibrary = () => {
const {
source,
spotify_available,
spotify_library_artists_status,
spotifyActions: {
getLibraryArtists,
},
} = this.props;
if (!spotify_available) return;
if (source !== 'spotify' && source !== 'all') return;
if (spotify_library_artists_status === 'finished') return;
if (spotify_library_artists_status === 'started') return;
getLibraryArtists();
};
handleContextMenu(e, item) {
const data = {
@ -324,8 +345,7 @@ class LibraryArtists extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
mopidy_connected: state.mopidy.connected,
const mapStateToProps = (state) => ({
mopidy_uri_schemes: state.mopidy.uri_schemes,
mopidy_library_artists: state.mopidy.library_artists,
google_available: (state.mopidy.uri_schemes && state.mopidy.uri_schemes.includes('gmusic:')),

View File

@ -17,15 +17,14 @@ class LibraryBrowse extends React.Component {
this.props.uiActions.setWindowTitle(i18n('library.browse.title'));
}
componentDidUpdate = ({ mopidy_connected: prev_mopidy_connected}) => {
const { mopidy_connected } = this.props;
if (!prev_mopidy_connected && mopidy_connected) this.loadDirectory();
}
loadDirectory = () => {
const {
mopidyActions: {
getDirectory,
},
} = this.props;
loadDirectory(props = this.props) {
if (props.mopidy_connected) {
this.props.mopidyActions.getDirectory(null);
}
getDirectory(null);
}
render() {
@ -64,6 +63,10 @@ class LibraryBrowse extends React.Component {
subdirectory.icons = ['/iris/assets/backgrounds/browse-soundcloud.jpg'];
break;
case 'Mixcloud':
subdirectory.icons = ['/iris/assets/backgrounds/browse-mixcloud.jpg'];
break;
case 'iTunes Store: Podcasts':
subdirectory.icons = ['/iris/assets/backgrounds/browse-itunes.jpg'];
break;
@ -124,9 +127,8 @@ class LibraryBrowse extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
const mapStateToProps = (state) => ({
load_queue: state.ui.load_queue,
mopidy_connected: state.mopidy.connected,
directory: state.mopidy.directory,
view: state.ui.library_directory_view,
});

View File

@ -20,6 +20,7 @@ import {
} from '../../util/helpers';
import { arrayOf, sortItems } from '../../util/arrays';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class LibraryBrowseDirectory extends React.Component {
constructor(props) {
@ -45,27 +46,23 @@ class LibraryBrowseDirectory extends React.Component {
this.loadDirectory();
}
componentDidUpdate = ({
mopidy_connected: prev_mopidy_connected,
uri: prevUri,
}) => {
const {
uri,
mopidy_connected,
} = this.props;
componentDidUpdate = ({ uri: prevUri }) => {
const { uri } = this.props;
if (!prev_mopidy_connected && mopidy_connected) this.loadDirectory();
if (uri && uri !== prevUri) this.loadDirectory();
if (uri && uri !== prevUri) {
this.loadDirectory();
}
}
loadDirectory(props = this.props) {
if (props.mopidy_connected) {
let uri = null;
if (props.uri !== undefined) {
uri = props.uri;
}
this.props.mopidyActions.getDirectory(uri);
}
loadDirectory = () => {
const {
uri,
mopidyActions: {
getDirectory,
},
} = this.props;
getDirectory(uri);
}
loadMore = () => {
@ -221,15 +218,24 @@ class LibraryBrowseDirectory extends React.Component {
handleChange={(value) => { uiActions.set({ library_directory_view: value }); uiActions.hideContextMenu(); }}
/>
{tracks && (
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.playAll(e, all_tracks); }}>
<Button
onClick={(e) => { uiActions.hideContextMenu(); this.playAll(e, all_tracks); }}
noHover
discrete
tracking={{ category: 'Directory', action: 'Play' }}
>
<Icon name="play_circle_filled" />
<I18n path="actions.play_all" />
</a>
</Button>
)}
<a className="button button--no-hover" onClick={(e) => { uiActions.hideContextMenu(); this.goBack(e); }}>
<Button
onClick={(e) => { uiActions.hideContextMenu(); this.goBack(e); }}
noHover
tracking={{ category: 'Directory', action: 'Back' }}
>
<Icon name="keyboard_backspace" />
<I18n path="actions.back" />
</a>
</Button>
</span>
);
@ -283,7 +289,6 @@ const mapStateToProps = (state, ownProps) => {
return {
uri,
load_queue: state.ui.load_queue,
mopidy_connected: state.mopidy.connected,
directory: state.mopidy.directory,
view: state.ui.library_directory_view,
};

View File

@ -2,7 +2,7 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Link from '../../components/Link';
import Button from '../../components/Button';
import PlaylistGrid from '../../components/PlaylistGrid';
import List from '../../components/List';
import DropdownField from '../../components/Fields/DropdownField';
@ -39,40 +39,49 @@ class LibraryPlaylists extends React.Component {
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();
}
if (this.props.spotify_available && this.props.spotify_library_playlists_status !== 'finished' && (this.props.source == 'all' || this.props.source == 'spotify')) {
this.props.spotifyActions.getLibraryPlaylists();
}
this.getMopidyLibrary();
this.getSpotifyLibrary();
}
componentDidUpdate = ({
mopidy_connected: prev_mopidy_connected,
}) => {
getMopidyLibrary = () => {
const {
source,
mopidy_connected,
mopidy_library_playlists,
spotify_available,
spotify_library_playlists_status,
mopidyActions,
spotifyActions,
mopidyActions: {
getLibraryPlaylists,
},
} = this.props;
if (mopidy_connected && (source == 'all' || source == 'local')) {
if (!prev_mopidy_connected) mopidyActions.getLibraryPlaylists();
if (source !== 'local' && source !== 'all') return;
if (mopidy_library_playlists) return;
if (source !== 'all' && source !== 'local' && !mopidy_library_playlists) {
this.props.mopidyActions.getLibraryPlaylists();
}
}
getLibraryPlaylists();
};
if (spotify_available && (source === 'all' || source === 'spotify')) {
if (spotify_library_playlists_status !== 'finished' && spotify_library_playlists_status !== 'started') {
spotifyActions.getLibraryPlaylists();
}
getSpotifyLibrary = () => {
const {
source,
spotify_available,
spotify_library_playlists_status,
spotifyActions: {
getLibraryPlaylists,
},
} = this.props;
if (!spotify_available) return;
if (source !== 'spotify' && source !== 'all') return;
if (spotify_library_playlists_status === 'finished') return;
if (spotify_library_playlists_status === 'started') return;
getLibraryPlaylists();
};
componentDidUpdate = ({ source: prevSource }) => {
const { source } = this.props;
if (source !== prevSource) {
this.getMopidyLibrary();
this.getSpotifyLibrary();
}
}
@ -272,10 +281,15 @@ class LibraryPlaylists extends React.Component {
options={source_options}
handleChange={(value) => { this.props.uiActions.set({ library_playlists_source: value }); this.props.uiActions.hideContextMenu(); }}
/>
<Link className="button button--no-hover" to="/playlist/create">
<Button
to="/playlist/create"
noHover
discrete
tracking={{ category: 'Playlist', action: 'Create' }}
>
<Icon name="add_box" />
<I18n path="actions.add" />
</Link>
</Button>
</span>
);
@ -291,9 +305,8 @@ class LibraryPlaylists extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
const mapStateToProps = (state) => ({
slim_mode: state.ui.slim_mode,
mopidy_connected: state.mopidy.connected,
mopidy_uri_schemes: state.mopidy.uri_schemes,
mopidy_library_playlists: state.mopidy.library_playlists,
mopidy_library_playlists_status: (state.ui.processes.MOPIDY_LIBRARY_PLAYLISTS_PROCESSOR !== undefined ? state.ui.processes.MOPIDY_LIBRARY_PLAYLISTS_PROCESSOR.status : null),

View File

@ -12,6 +12,7 @@ import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { isLoading } from '../../util/helpers';
import { I18n, i18n } from '../../locale';
import Button from '../../components/Button';
class LibraryTracks extends React.Component {
componentDidMount() {
@ -63,10 +64,17 @@ class LibraryTracks extends React.Component {
}
const options = (
<a className="button button--no-hover" onClick={(e) => this.playAll(e)}>
<Icon name="play_circle_filled" />
<I18n path="actions.play_all" />
</a>
<div className="header__options__wrapper">
<Button
onClick={(e) => this.playAll(e)}
tracking={{ category: 'LibraryTracks', action: 'Play' }}
noHover
discrete
>
<Icon name="play_circle_filled" />
<I18n path="actions.play_all" />
</Button>
</div>
);
return (

View File

@ -19,7 +19,6 @@ class AddToPlaylist extends React.Component {
const {
spotify_library_playlists_status,
mopidy_library_playlists_status,
mopidy_connected,
spotify_available,
spotifyActions,
mopidyActions,
@ -32,7 +31,7 @@ class AddToPlaylist extends React.Component {
spotifyActions.getLibraryPlaylists();
}
if ((!mopidy_library_playlists_status || mopidy_library_playlists_status !== 'finished') && mopidy_connected) {
if ((!mopidy_library_playlists_status || mopidy_library_playlists_status !== 'finished')) {
mopidyActions.getLibraryPlaylists();
}
@ -114,7 +113,6 @@ class AddToPlaylist extends React.Component {
const mapStateToProps = (state, ownProps) => ({
uris: (ownProps.match.params.uris ? decodeURIComponent(ownProps.match.params.uris).split(',') : []),
mopidy_connected: state.mopidy.connected,
mopidy_uri_schemes: state.mopidy.uri_schemes,
mopidy_library_playlists: state.mopidy.library_playlists,
mopidy_library_playlists_status: (state.ui.processes.MOPIDY_LIBRARY_PLAYLISTS_PROCESSOR !== undefined ? state.ui.processes.MOPIDY_LIBRARY_PLAYLISTS_PROCESSOR.status : null),

View File

@ -12,6 +12,7 @@ import { uriType } from '../../util/helpers';
import Icon from '../../components/Icon';
import TextField from '../../components/Fields/TextField';
import LinksSentence from '../../components/LinksSentence';
import Button from '../../components/Button';
const UriListItem = ({
uri,
@ -44,14 +45,17 @@ const UriListItem = ({
</I18n>
)}
<span className="mid_grey-text">{` (${type})`}</span>
<button
className="button button--discrete button--destructive button--tiny pull-right"
type="button"
<Button
type="destructive"
discrete
size="tiny"
className="pull-right"
onClick={() => remove(uri)}
tracking={{ category: 'AddToQueue', action: 'RemoveItem' }}
>
<Icon name="delete" />
<I18n path="actions.remove" />
</button>
</Button>
</div>
);
};
@ -257,20 +261,23 @@ class AddToQueue extends React.Component {
</div>
<div className="actions centered-text">
<button
type="button"
className="button button--grey button--large"
<Button
colour="grey"
size="large"
onClick={this.addRandom}
tracking={{ category: 'AddToQueue', action: 'AddRandom' }}
>
<I18n path="modal.add_to_queue.add_random" />
</button>
<button
type="submit"
className="button button--primary button--large"
</Button>
<Button
type="primary"
size="large"
disabled={!uris.length}
submit
tracking={{ category: 'AddToQueue', action: 'Submit' }}
>
<I18n path="actions.add" />
</button>
</Button>
</div>
</form>
</Modal>
@ -279,7 +286,6 @@ class AddToQueue extends React.Component {
}
const mapStateToProps = (state) => ({
mopidy_connected: state.mopidy.connected,
albums: state.core.albums,
tracks: state.core.tracks,
view: state.core.view ? state.core.view : {},

View File

@ -6,6 +6,7 @@ import Modal from './Modal';
import * as coreActions from '../../services/core/actions';
import * as uiActions from '../../services/ui/actions';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class CreatePlaylist extends React.Component {
constructor(props) {
@ -173,9 +174,14 @@ class CreatePlaylist extends React.Component {
{this.renderFields()}
<div className="actions centered-text">
<button type="submit" className="button button--primary button--large">
<Button
type="primary"
size="large"
submit
tracking={{ category: 'CreatePlaylist', action: 'Submit' }}
>
<I18n path="modal.edit_playlist.create_playlist" />
</button>
</Button>
</div>
</form>

View File

@ -10,6 +10,7 @@ import * as pusherActions from '../../services/pusher/actions';
import * as uiActions from '../../services/ui/actions';
import { scrollTo, generateGuid } from '../../util/helpers';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class EditCommand extends React.Component {
constructor(props) {
@ -239,17 +240,23 @@ class EditCommand extends React.Component {
<div className="actions centered-text">
{command && (
<button
type="button"
className="button button--destructive button--large"
<Button
type="destructive"
size="large"
onClick={(e) => this.handleDelete(e)}
tracking={{ category: 'EditCommand', action: 'Delete' }}
>
<I18n path="actions.delete" />
</button>
</Button>
)}
<button type="submit" className="button button--primary button--large">
<Button
type="primary"
size="large"
submit
tracking={{ category: 'EditCommand', action: 'Submit' }}
>
<I18n path="actions.save" />
</button>
</Button>
</div>
</form>

View File

@ -9,6 +9,7 @@ import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { uriSource } from '../../util/helpers';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class EditPlaylist extends React.Component {
constructor(props) {
@ -43,27 +44,24 @@ class EditPlaylist extends React.Component {
break;
default:
if (this.props.mopidy_connected) {
this.props.mopidyActions.getPlaylist(this.props.uri);
}
this.props.mopidyActions.getPlaylist(this.props.uri);
break;
}
}
}
componentDidUpdate = ({
mopidy_connected: prev_mopidy_connected,
playlist: prevPlaylist,
}) => {
const {
uri,
playlist,
mopidy_connected,
mopidyActions: {
getPlaylist,
},
} = this.props;
if (!prev_mopidy_connected && mopidy_connected && !playlist) {
if (playlist !== prevPlaylist) {
getPlaylist(uri);
}
}
@ -225,9 +223,14 @@ class EditPlaylist extends React.Component {
{this.renderFields()}
<div className="actions centered-text">
<button type="submit" className="button button--primary button--large">
<Button
type="primary"
size="large"
tracking={{ category: 'EditPlaylist', action: 'Submit' }}
submit
>
<I18n path="actions.save" />
</button>
</Button>
</div>
</form>
</Modal>
@ -249,7 +252,6 @@ const mapStateToProps = (state, ownProps) => {
return {
uri,
mopidy_connected: state.mopidy.connected,
playlist: (state.core.playlists[uri] !== undefined ? state.core.playlists[uri] : null),
playlists: state.core.playlists,
};

View File

@ -15,6 +15,7 @@ import {
} from '../../util/helpers';
import { i18n, I18n } from '../../locale';
import TextField from '../../components/Fields/TextField';
import Button from '../../components/Button';
const SeedListItem = ({ seed, remove }) => (
<div className="list__item list__item--no-interaction" key={seed.uri}>
@ -28,14 +29,17 @@ const SeedListItem = ({ seed, remove }) => (
{` (${uriType(seed.uri)})`}
</span>
)}
<button
type="button"
className="button button--discrete button--destructive button--tiny pull-right"
<Button
type="destructive"
size="tiny"
className="pull-right"
discrete
onClick={() => remove(seed.uri)}
tracking={{ category: 'Directory', action: 'Play' }}
>
<Icon name="delete" />
<I18n path="actions.remove" />
</button>
</Button>
</div>
);
@ -277,28 +281,35 @@ class EditRadio extends React.Component {
</div>
<div className="actions centered-text">
{this.state.enabled ? (
<span className="button button--destructive button--large" onClick={(e) => this.handleStop(e)}>
{this.state.enabled && (
<Button
type="destructive"
size="large"
onClick={(e) => this.handleStop(e)}
tracking={{ category: 'Radio', action: 'Stop' }}
>
<I18n path="actions.add" />
</span>
) : null}
</Button>
)}
{this.state.enabled ? (
<button
className="button button--primary button--large"
<Button
type="primary"
size="large"
onClick={(e) => this.handleUpdate(e)}
type="button"
tracking={{ category: 'Radio', action: 'Update' }}
>
<I18n path="actions.save" />
</button>
</Button>
) : (
<button
className="button button--primary button--large"
<Button
type="primary"
size="large"
onClick={(e) => this.handleStart(e)}
type="button"
tracking={{ category: 'Radio', action: 'Start' }}
>
<I18n path="actions.start" />
</button>
</Button>
)}
</div>
</form>
@ -308,7 +319,6 @@ class EditRadio extends React.Component {
}
const mapStateToProps = (state, ownProps) => ({
mopidy_connected: state.mopidy.connected,
radio: state.core.radio,
artists: state.core.artists,
tracks: state.core.tracks,

View File

@ -9,6 +9,7 @@ import * as pusherActions from '../../services/pusher/actions';
import * as mopidyActions from '../../services/mopidy/actions';
import { queryString, isHosted } from '../../util/helpers';
import { I18n, i18n } from '../../locale';
import Button from '../../components/Button';
class InitialSetup extends React.Component {
constructor(props) {
@ -163,9 +164,15 @@ class InitialSetup extends React.Component {
)}
<div className="actions centered-text">
<button className={`button button--primary button--large${this.state.saving ? ' button--working' : ''}`} onClick={(e) => this.handleSubmit(e)}>
<Button
type="primary"
size="large"
working={this.state.saving}
onClick={(e) => this.handleSubmit(e)}
tracking={{ category: 'InitialSetup', action: 'Submit' }}
>
<I18n path="actions.save" />
</button>
</Button>
</div>
</form>

View File

@ -6,6 +6,7 @@ import Modal from './Modal';
import * as uiActions from '../../services/ui/actions';
import * as pusherActions from '../../services/pusher/actions';
import { i18n, I18n } from '../../locale';
import Button from '../../components/Button';
class ShareConfiguration extends React.Component {
constructor(props) {
@ -280,15 +281,15 @@ class ShareConfiguration extends React.Component {
</div>
<div className="actions centered-text">
{this.state.recipients.length > 0 ? (
<button className="button button--primary button--large" onClick={(e) => this.handleSubmit(e)}>
<I18n path="actions.send" />
</button>
) : (
<button className="button button--primary button--large" disabled="disabled" onClick={(e) => this.handleSubmit(e)}>
<I18n path="actions.send" />
</button>
)}
<Button
type="primary"
size="large"
disabled={this.state.recipients.length <= 0}
onClick={(e) => this.handleSubmit(e)}
tracking={{ category: 'ShareConfiguration', action: 'Send' }}
>
<I18n path="actions.send" />
</Button>
</div>
</form>
</Modal>