Restructuring and functional component-ifying

This commit is contained in:
James Barnsley
2021-11-19 17:24:30 +13:00
parent 699e809568
commit 2777851889
20 changed files with 89 additions and 286 deletions

View File

@ -28,18 +28,18 @@ import Debug from './views/Debug';
import Search from './views/Search';
import Settings from './views/Settings';
import DiscoverRecommendations from './views/discover/DiscoverRecommendations';
import DiscoverFeatured from './views/discover/DiscoverFeatured';
import DiscoverCategories from './views/discover/DiscoverCategories';
import DiscoverCategory from './views/discover/DiscoverCategory';
import DiscoverNewReleases from './views/discover/DiscoverNewReleases';
import DiscoverRecommendations from './views/Discover/DiscoverRecommendations';
import DiscoverFeatured from './views/Discover/DiscoverFeatured';
import DiscoverCategories from './views/Discover/DiscoverCategories';
import DiscoverCategory from './views/Discover/DiscoverCategory';
import DiscoverNewReleases from './views/Discover/DiscoverNewReleases';
import LibraryArtists from './views/library/LibraryArtists';
import LibraryAlbums from './views/library/LibraryAlbums';
import LibraryTracks from './views/library/LibraryTracks';
import LibraryPlaylists from './views/library/LibraryPlaylists';
import LibraryBrowse from './views/library/LibraryBrowse';
import LibraryBrowseDirectory from './views/library/LibraryBrowseDirectory';
import LibraryArtists from './views/Library/LibraryArtists';
import LibraryAlbums from './views/Library/LibraryAlbums';
import LibraryTracks from './views/Library/LibraryTracks';
import LibraryPlaylists from './views/Library/LibraryPlaylists';
import LibraryBrowse from './views/Library/LibraryBrowse';
import LibraryBrowseDirectory from './views/Library/LibraryBrowseDirectory';
import EditPlaylist from './views/modals/EditPlaylist';
import CreatePlaylist from './views/modals/CreatePlaylist';

View File

@ -1,65 +1,43 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as uiActions from '../../services/ui/actions';
import React, { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { uriType } from '../../util/helpers';
import Icon from '../Icon';
import { i18n } from '../../locale';
import { encodeUri } from '../../util/format';
class SearchForm extends React.Component {
constructor(props) {
super(props);
const SearchForm = ({
term: termProp,
onBlur: doBlur,
onReset: doReset,
onSubmit: doSubmit,
}) => {
const history = useHistory();
const [term, setTerm] = useState(termProp);
const [dirty, setDirty] = useState();
this.state = {
term: props.term,
pristine: true,
};
}
useEffect(
() => {
if (termProp && !dirty) setTerm(termProp);
},
[termProp],
);
static getDerivedStateFromProps(props, state) {
const { pristine, term } = state;
if (pristine && term === '' && term !== props.term) {
return {
term,
pristine: false,
};
}
return null;
}
const onChange = (e) => {
setTerm(e.target.value);
setDirty(true);
};
shouldComponentUpdate = (nextProps, nextState) => {
const { term: termProp } = this.props;
const { term: termState } = this.state;
if (nextProps.term !== termProp) return true;
if (nextState.term !== termState) return true;
return false;
}
onChange = (e) => {
this.setState({
term: e.target.value,
pristine: false,
});
}
onBlur = () => {
const { onBlur } = this.props;
const { term } = this.state;
this.setState({ pristine: false });
if (onBlur) {
onBlur(term);
const onBlur = () => {
setDirty(true);
if (doBlur) {
doBlur(term);
}
}
onFocus = () => {
this.setState({ pristine: false });
}
const onFocus = () => setDirty(true);
onSubmit = (e) => {
const { term } = this.state;
const { history } = this.props;
const onSubmit = (e) => {
e.preventDefault();
// check for uri type matching
@ -81,52 +59,36 @@ class SearchForm extends React.Component {
break;
default:
this.props.onSubmit(term);
doSubmit(term);
break;
}
return false;
};
const onReset = () => {
setDirty(false);
setTerm('');
if (doReset) doReset();
}
onReset = () => {
const { onReset: doReset } = this.props;
return (
<form className="search-form" onSubmit={onSubmit}>
<label>
<input
type="text"
placeholder={i18n('fields.search')}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
value={term}
/>
</label>
{term && (
<Icon name="close" className="search-form__reset" onClick={onReset} />
)}
</form>
);
};
this.setState(
{
term: '',
pristine: true,
},
() => {
doReset();
},
);
}
render = () => {
const { term } = this.state;
return (
<form className="search-form" onSubmit={this.onSubmit}>
<label>
<input
type="text"
placeholder={i18n('fields.search')}
onChange={this.onChange}
onBlur={this.onBlur}
onFocus={this.onFocus}
value={term}
/>
</label>
{term && (
<Icon name="close" className="search-form__reset" onClick={this.onReset} />
)}
</form>
);
}
}
const mapDispatchToProps = (dispatch) => ({
uiActions: bindActionCreators(uiActions, dispatch),
});
export default connect(mapDispatchToProps)(SearchForm);
export default SearchForm;

View File

@ -1,14 +1,16 @@
import React, { memo } from 'react';
import { useDispatch } from 'react-redux';
import ContextMenuTrigger from './ContextMenuTrigger';
import { showContextMenu } from '../services/ui/actions';
export default memo(({
handleContextMenuTrigger,
options,
title,
uiActions,
className,
children,
}) => {
const dispatch = useDispatch();
const onTrigger = (e) => {
if (handleContextMenuTrigger) return handleContextMenuTrigger(e);
@ -19,7 +21,7 @@ export default memo(({
title,
options,
};
uiActions.showContextMenu(data);
dispatch(showContextMenu(data));
return true;
};

View File

@ -130,10 +130,15 @@ export default function reducer(ui = {}, action) {
* Loading and processes
* */
case 'START_LOADING':
var load_queue = { ...(ui.load_queue ? ui.load_queue : {}) };
load_queue[action.key] = action.source || action.key;
return { ...ui, load_queue };
case 'START_LOADING': {
return {
...ui,
load_queue: {
...ui.load_queue || {},
[action.key]: action.source || action.key,
},
};
}
case 'STOP_LOADING':
return {

View File

@ -8,10 +8,10 @@ import Thumbnail from '../components/Thumbnail';
import Parallax from '../components/Parallax';
import FollowButton from '../components/Fields/FollowButton';
import ContextMenuTrigger from '../components/ContextMenuTrigger';
import ArtistAbout from './subviews/ArtistAbout';
import ArtistOverview from './subviews/ArtistOverview';
import ArtistTracks from './subviews/ArtistTracks';
import ArtistRelated from './subviews/ArtistRelated';
import About from './Artist/About';
import Overview from './Artist/Overview';
import Tracks from './Artist/Tracks';
import Related from './Artist/Related';
import Loader from '../components/Loader';
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
@ -23,7 +23,6 @@ import { encodeUri, decodeUri } from '../util/format';
import { arrayOf } from '../util/arrays';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
import { trackEvent } from '../components/Trackable';
import {
makeItemSelector,
makeLoadingSelector,
@ -186,16 +185,16 @@ const Artist = ({
<div className="content-wrapper">
<Switch>
<Route exact path="/artist/:id/related-artists">
<ArtistRelated artist={artist} />
<Related artist={artist} />
</Route>
<Route exact path="/artist/:id/tracks">
<ArtistTracks artist={artist} />
<Tracks artist={artist} />
</Route>
<Route exact path="/artist/:id/about">
<ArtistAbout artist={artist} />
<About artist={artist} />
</Route>
<Route exact path="/artist/:id/:name?">
<ArtistOverview artist={artist} albums={albums} />
<Overview artist={artist} albums={albums} />
</Route>
</Switch>
</div>

View File

@ -9,7 +9,7 @@ import {
import { i18n, I18n } from '../../locale';
import { nice_number } from '../../components/NiceNumber';
const ArtistAbout = ({
export default ({
artist,
}) => {
const thumbnails = artist.images && Array.isArray(artist.images) && artist.images.map(
@ -88,6 +88,4 @@ const ArtistAbout = ({
</div>
</div>
);
}
export default ArtistAbout;
};

View File

@ -11,11 +11,8 @@ import * as uiActions from '../../services/ui/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { encodeUri } from '../../util/format';
import { i18n, I18n } from '../../locale';
import { indexToArray } from '../../util/arrays';
import { indexToArray, applyFilter } from '../../util/arrays';
import { makeLoadingSelector } from '../../util/selectors';
import { applyFilter } from '../../util/arrays';
const SORT_KEY = 'discover_categories';
const DiscoverCategories = ({
loading,

View File

@ -1,160 +0,0 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import TrackList from '../../components/TrackList';
import FilterField from '../../components/Fields/FilterField';
import DropdownField from '../../components/Fields/DropdownField';
import { Grid } from '../../components/Grid';
import Button from '../../components/Button';
import RelatedArtists from '../../components/RelatedArtists';
import { i18n, I18n } from '../../locale';
import { sortItems, applyFilter } from '../../util/arrays';
import { encodeUri } from '../../util/format';
import { getSortSelector } from '../../util/selectors';
import {
hideContextMenu,
setSort,
} from '../../services/ui/actions';
const SORT_KEY = 'artist_albums';
export default ({
artist,
albums: albumsProp,
}) => {
const dispatch = useDispatch();
const [search, setSearch] = useState('');
const [type, setType] = useState('');
const [sortField, sortReverse] = useSelector(
(state) => getSortSelector(state, SORT_KEY, null),
);
const {
uri,
tracks,
related_artists,
} = artist;
let albums = albumsProp;
const onSortChange = (field) => {
let reverse = false;
if (field !== null && sortField === field) {
reverse = !sortReverse;
}
dispatch(setSort(SORT_KEY, field, reverse));
dispatch(hideContextMenu());
}
const onSubmit = () => {
dispatch(hideContextMenu());
};
if (sortField && albums) {
albums = sortItems(albums, sortField, sortReverse);
}
if (type && albums) {
albums = applyFilter('type', type, albums);
}
if (search && search !== '') {
albums = applyFilter('name', search, albums);
}
const sort_options = [
{
value: null,
label: i18n('artist.albums.sort.default'),
},
{
value: 'name',
label: i18n('artist.albums.sort.name'),
},
{
value: 'release_date',
label: i18n('artist.albums.sort.release_date'),
},
{
value: 'tracks',
label: i18n('artist.albums.sort.track_count'),
},
];
const filter_type_options = [
{
value: null,
label: i18n('artist.albums.filter.all'),
},
{
value: 'album',
label: i18n('artist.albums.filter.albums'),
},
{
value: 'single',
label: i18n('artist.albums.filter.singles'),
},
];
return (
<div className="body overview">
<div className={`top-tracks col col--w${related_artists && related_artists.length > 0 ? '70' : '100'}`}>
{tracks && <h4><I18n path="artist.overview.top_tracks" /></h4>}
<div className="list-wrapper">
<TrackList className="artist-track-list" uri={uri} tracks={tracks ? tracks.slice(0, 10) : []} />
</div>
</div>
<div className="col col--w5" />
{related_artists && related_artists.length > 0 && (
<div className="col col--w25 related-artists">
<h4><I18n path="artist.overview.related_artists.title" /></h4>
<div className="list-wrapper">
<RelatedArtists artists={related_artists.slice(0, 6)} />
</div>
<Button
to={`/artist/${encodeUri(uri)}/related-artists`}
scrollTo="#sub-views-menu"
>
<I18n path="artist.overview.related_artists.more" />
</Button>
</div>
)}
<div className="cf" />
<div className="albums">
<h4>
<I18n path="artist.overview.albums" count={albums ? albums.length : 0} />
<div className="actions-wrapper">
<FilterField
initialValue={search}
handleChange={setSearch}
onSubmit={onSubmit}
/>
<DropdownField
icon="swap_vert"
name="Sort"
value={sortField}
valueAsLabel
options={sort_options}
selected_icon={sortField ? (sortReverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
handleChange={onSortChange}
/>
<DropdownField
icon="filter_list"
name="Filter"
value={type}
valueAsLabel
options={filter_type_options}
handleChange={setType}
/>
</div>
</h4>
<section className="grid-wrapper no-top-padding">
<Grid items={albums} />
</section>
</div>
</div>
);
}