diff --git a/frontend/src/AlbumStudio/AlbumStudio.css b/frontend/src/AlbumStudio/AlbumStudio.css deleted file mode 100644 index 033279591..000000000 --- a/frontend/src/AlbumStudio/AlbumStudio.css +++ /dev/null @@ -1,36 +0,0 @@ -.pageContentBodyWrapper { - display: flex; - flex: 1 0 1px; - overflow: hidden; -} - -.contentBody { - composes: contentBody from '~Components/Page/PageContentBody.css'; - - display: flex; - flex-direction: column; -} - -.tableInnerContentBody { - composes: innerContentBody from '~Components/Page/PageContentBody.css'; - - display: flex; - flex-direction: column; - flex-grow: 1; -} - -.contentBodyContainer { - display: flex; - flex-direction: column; - flex-grow: 1; -} - -@media only screen and (max-width: $breakpointSmall) { - .pageContentBodyWrapper { - flex-basis: auto; - } - - .contentBody { - flex-basis: 1px; - } -} diff --git a/frontend/src/AlbumStudio/AlbumStudio.css.d.ts b/frontend/src/AlbumStudio/AlbumStudio.css.d.ts deleted file mode 100644 index 9937ea245..000000000 --- a/frontend/src/AlbumStudio/AlbumStudio.css.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'contentBody': string; - 'contentBodyContainer': string; - 'pageContentBodyWrapper': string; - 'tableInnerContentBody': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/AlbumStudio/AlbumStudio.js b/frontend/src/AlbumStudio/AlbumStudio.js deleted file mode 100644 index 200b7ffe4..000000000 --- a/frontend/src/AlbumStudio/AlbumStudio.js +++ /dev/null @@ -1,440 +0,0 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { CellMeasurer, CellMeasurerCache } from 'react-virtualized'; -import NoArtist from 'Artist/NoArtist'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import FilterMenu from 'Components/Menu/FilterMenu'; -import PageContent from 'Components/Page/PageContent'; -import PageContentBody from 'Components/Page/PageContentBody'; -import PageJumpBar from 'Components/Page/PageJumpBar'; -import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; -import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; -import VirtualTable from 'Components/Table/VirtualTable'; -import VirtualTableRow from 'Components/Table/VirtualTableRow'; -import { align, sortDirections } from 'Helpers/Props'; -import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter'; -import getErrorMessage from 'Utilities/Object/getErrorMessage'; -import translate from 'Utilities/String/translate'; -import getSelectedIds from 'Utilities/Table/getSelectedIds'; -import selectAll from 'Utilities/Table/selectAll'; -import toggleSelected from 'Utilities/Table/toggleSelected'; -import AlbumStudioFilterModalConnector from './AlbumStudioFilterModalConnector'; -import AlbumStudioFooter from './AlbumStudioFooter'; -import AlbumStudioRowConnector from './AlbumStudioRowConnector'; -import AlbumStudioTableHeader from './AlbumStudioTableHeader'; -import styles from './AlbumStudio.css'; - -const columns = [ - { - name: 'status', - isVisible: true - }, - { - name: 'sortName', - label: () => translate('Name'), - isSortable: true, - isVisible: true - }, - { - name: 'albumCount', - label: () => translate('Albums'), - isSortable: false, - isVisible: true - } -]; - -class AlbumStudio extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.scrollerRef = React.createRef(); - - this.state = { - estimatedRowSize: 100, - jumpBarItems: { order: [] }, - scrollIndex: null, - jumpCount: 0, - allSelected: false, - allUnselected: false, - lastToggled: null, - selectedState: {} - }; - - this.cache = new CellMeasurerCache({ - defaultHeight: 100, - fixedWidth: true - }); - } - - componentDidMount() { - this.setSelectedState(); - } - - componentDidUpdate(prevProps) { - const { - isSaving, - saveError - } = this.props; - - const { - scrollIndex, - jumpCount - } = this.state; - - if (prevProps.isSaving && !isSaving && !saveError) { - this.onSelectAllChange({ value: false }); - } - - // nasty hack to fix react-virtualized jumping incorrectly - // due to variable row heights - if (scrollIndex != null && scrollIndex > 0) { - if (jumpCount === 0) { - this.setState({ - scrollIndex: scrollIndex - 1, - jumpCount: 1 - }); - } else if (jumpCount === 1) { - this.setState({ - scrollIndex: scrollIndex + 1, - jumpCount: 2 - }); - } else { - this.setState({ - scrollIndex: null, - jumpCount: 0 - }); - } - } - } - - setJumpBarItems() { - const { - items, - sortKey, - sortDirection - } = this.props; - - // Reset if not sorting by sortName - if (sortKey !== 'sortName') { - this.setState({ jumpBarItems: { order: [] } }); - return; - } - - const characters = _.reduce(items, (acc, item) => { - let char = item.sortName.charAt(0); - - if (!isNaN(char)) { - char = '#'; - } - - if (char in acc) { - acc[char] = acc[char] + 1; - } else { - acc[char] = 1; - } - - return acc; - }, {}); - - const order = Object.keys(characters).sort(); - - // Reverse if sorting descending - if (sortDirection === sortDirections.DESCENDING) { - order.reverse(); - } - - const jumpBarItems = { - characters, - order - }; - - this.setState({ jumpBarItems }); - } - - getSelectedIds = () => { - if (this.state.allUnselected) { - return []; - } - return getSelectedIds(this.state.selectedState); - }; - - setSelectedState = () => { - const { - items - } = this.props; - - const { - selectedState - } = this.state; - - const newSelectedState = {}; - - items.forEach((artist) => { - const isItemSelected = selectedState[artist.id]; - - if (isItemSelected) { - newSelectedState[artist.id] = isItemSelected; - } else { - newSelectedState[artist.id] = false; - } - }); - - const selectedCount = getSelectedIds(newSelectedState).length; - const newStateCount = Object.keys(newSelectedState).length; - let isAllSelected = false; - let isAllUnselected = false; - - if (selectedCount === 0) { - isAllUnselected = true; - } else if (selectedCount === newStateCount) { - isAllSelected = true; - } - - this.setState({ selectedState: newSelectedState, allSelected: isAllSelected, allUnselected: isAllUnselected }); - }; - - estimateRowHeight = (width) => { - const { - albumCount, - items - } = this.props; - - if (albumCount === undefined || albumCount === 0 || items.length === 0) { - return 100; - } - - // guess 250px per album entry - // available width is total width less 186px for select, status etc - const cols = Math.max(Math.floor((width - 186) / 250), 1); - const albumsPerArtist = albumCount / items.length; - const albumRowsPerArtist = albumsPerArtist / cols; - - // each row is 23px per album row plus 16px padding - return albumRowsPerArtist * 23 + 16; - }; - - rowRenderer = ({ key, rowIndex, parent, style }) => { - const { - items - } = this.props; - - const { - selectedState - } = this.state; - - const item = items[rowIndex]; - - return ( - - {({ registerChild }) => ( - - - - )} - - ); - }; - - // - // Listeners - - onSelectAllChange = ({ value }) => { - this.setState(selectAll(this.state.selectedState, value)); - }; - - onSelectedChange = ({ id, value, shiftKey = false }) => { - this.setState((state) => { - return toggleSelected(state, this.props.items, id, value, shiftKey); - }); - }; - - onSelectAllPress = () => { - this.onSelectAllChange({ value: !this.state.allSelected }); - }; - - onUpdateSelectedPress = (changes) => { - this.props.onUpdateSelectedPress({ - artistIds: this.getSelectedIds(), - ...changes - }); - }; - - onJumpBarItemPress = (jumpToCharacter) => { - const scrollIndex = getIndexOfFirstCharacter(this.props.items, jumpToCharacter); - - if (scrollIndex != null) { - this.setState({ scrollIndex }); - } - }; - - onGridRecompute = (width) => { - this.setJumpBarItems(); - this.setSelectedState(); - this.setState({ estimatedRowSize: this.estimateRowHeight(width) }); - this.cache.clearAll(); - }; - - // - // Render - - render() { - const { - isFetching, - isPopulated, - error, - totalItems, - items, - selectedFilterKey, - filters, - customFilters, - sortKey, - sortDirection, - isSaving, - saveError, - isSmallScreen, - onSortPress, - onFilterSelect - } = this.props; - - const { - allSelected, - allUnselected, - estimatedRowSize, - jumpBarItems, - scrollIndex - } = this.state; - - return ( - - - - - - - - -
- - { - isFetching && !isPopulated && - - } - - { - !isFetching && !!error && -
{getErrorMessage(error, 'Failed to load artist from API')}
- } - - { - !error && - isPopulated && - !!items.length && - this.scrollerRef.current ? -
- - } - sortKey={sortKey} - sortDirection={sortDirection} - deferredMeasurementCache={this.cache} - rowHeight={this.cache.rowHeight} - estimatedRowSize={estimatedRowSize} - onRecompute={this.onGridRecompute} - /> -
: - null - } - - { - !error && isPopulated && !items.length && - - } -
- - { - isPopulated && !!jumpBarItems.order.length && - - } -
- - -
- ); - } -} - -AlbumStudio.propTypes = { - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - error: PropTypes.object, - totalItems: PropTypes.number.isRequired, - items: PropTypes.arrayOf(PropTypes.object).isRequired, - albumCount: PropTypes.number.isRequired, - sortKey: PropTypes.string, - sortDirection: PropTypes.oneOf(sortDirections.all), - selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - filters: PropTypes.arrayOf(PropTypes.object).isRequired, - customFilters: PropTypes.arrayOf(PropTypes.object).isRequired, - isSaving: PropTypes.bool.isRequired, - saveError: PropTypes.object, - isSmallScreen: PropTypes.bool.isRequired, - onSortPress: PropTypes.func.isRequired, - onFilterSelect: PropTypes.func.isRequired, - onUpdateSelectedPress: PropTypes.func.isRequired -}; - -export default AlbumStudio; diff --git a/frontend/src/AlbumStudio/AlbumStudioAlbum.css b/frontend/src/AlbumStudio/AlbumStudioAlbum.css deleted file mode 100644 index c568a2489..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioAlbum.css +++ /dev/null @@ -1,39 +0,0 @@ -.album { - display: flex; - align-items: stretch; - overflow: hidden; - margin: 2px 4px; - border: 1px solid var(--borderColor); - border-radius: 4px; - background-color: var(--albumBackgroundColor); - cursor: default; -} - -.info { - padding: 0 4px; -} - -.albumType { - padding: 0 4px; - border-width: 0 1px; - border-style: solid; - border-color: var(--borderColor); - background-color: var(--albumBackgroundColor); - color: var(--defaultColor); -} - -.tracks { - padding: 0 4px; - background-color: var(--trackBackgroundColor); - color: var(--defaultColor); -} - -.allTracks { - background-color: color(#27c24c saturation(-25%)); - color: var(--white); -} - -.missingWanted { - background-color: color(#f05050 saturation(-20%)); - color: var(--white); -} diff --git a/frontend/src/AlbumStudio/AlbumStudioAlbum.css.d.ts b/frontend/src/AlbumStudio/AlbumStudioAlbum.css.d.ts deleted file mode 100644 index 31142374e..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioAlbum.css.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'album': string; - 'albumType': string; - 'allTracks': string; - 'info': string; - 'missingWanted': string; - 'tracks': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/AlbumStudio/AlbumStudioAlbum.js b/frontend/src/AlbumStudio/AlbumStudioAlbum.js deleted file mode 100644 index 5dc9dc233..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioAlbum.js +++ /dev/null @@ -1,102 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import MonitorToggleButton from 'Components/MonitorToggleButton'; -import translate from 'Utilities/String/translate'; -import styles from './AlbumStudioAlbum.css'; - -class AlbumStudioAlbum extends Component { - - // - // Listeners - - onAlbumMonitoredPress = () => { - const { - id, - monitored - } = this.props; - - this.props.onAlbumMonitoredPress(id, !monitored); - }; - - // - // Render - - render() { - const { - title, - disambiguation, - albumType, - monitored, - statistics, - isSaving - } = this.props; - - const { - trackFileCount, - totalTrackCount, - percentOfTracks - } = statistics; - - return ( -
-
- - - - { - disambiguation ? `${title} (${disambiguation})` : `${title}` - } - -
- -
- - { - `${albumType}` - } - -
- -
- { - totalTrackCount === 0 ? '0/0' : `${trackFileCount}/${totalTrackCount}` - } -
-
- ); - } -} - -AlbumStudioAlbum.propTypes = { - id: PropTypes.number.isRequired, - title: PropTypes.string.isRequired, - disambiguation: PropTypes.string, - albumType: PropTypes.string.isRequired, - monitored: PropTypes.bool.isRequired, - statistics: PropTypes.object.isRequired, - isSaving: PropTypes.bool.isRequired, - onAlbumMonitoredPress: PropTypes.func.isRequired -}; - -AlbumStudioAlbum.defaultProps = { - isSaving: false, - statistics: { - trackFileCount: 0, - totalTrackCount: 0, - percentOfTracks: 0 - } -}; - -export default AlbumStudioAlbum; diff --git a/frontend/src/AlbumStudio/AlbumStudioConnector.js b/frontend/src/AlbumStudio/AlbumStudioConnector.js deleted file mode 100644 index 25fedafa4..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioConnector.js +++ /dev/null @@ -1,116 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { clearAlbums, fetchAlbums } from 'Store/Actions/albumActions'; -import { saveAlbumStudio, setAlbumStudioFilter, setAlbumStudioSort } from 'Store/Actions/albumStudioActions'; -import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector'; -import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; -import AlbumStudio from './AlbumStudio'; - -function createAlbumFetchStateSelector() { - return createSelector( - (state) => state.albums.items.length, - (state) => state.albums.isFetching, - (state) => state.albums.isPopulated, - (length, isFetching, isPopulated) => { - const albumCount = (!isFetching && isPopulated) ? length : 0; - return { - albumCount, - isFetching, - isPopulated - }; - } - ); -} - -function createMapStateToProps() { - return createSelector( - createAlbumFetchStateSelector(), - createArtistClientSideCollectionItemsSelector('albumStudio'), - createDimensionsSelector(), - (albums, artist, dimensionsState) => { - const isPopulated = albums.isPopulated && artist.isPopulated; - const isFetching = artist.isFetching || albums.isFetching; - return { - ...artist, - isPopulated, - isFetching, - albumCount: albums.albumCount, - isSmallScreen: dimensionsState.isSmallScreen - }; - } - ); -} - -const mapDispatchToProps = { - fetchAlbums, - clearAlbums, - setAlbumStudioSort, - setAlbumStudioFilter, - saveAlbumStudio -}; - -class AlbumStudioConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.populate(); - } - - componentWillUnmount() { - this.unpopulate(); - } - - // - // Control - - populate = () => { - this.props.fetchAlbums(); - }; - - unpopulate = () => { - this.props.clearAlbums(); - }; - - // - // Listeners - - onSortPress = (sortKey) => { - this.props.setAlbumStudioSort({ sortKey }); - }; - - onFilterSelect = (selectedFilterKey) => { - this.props.setAlbumStudioFilter({ selectedFilterKey }); - }; - - onUpdateSelectedPress = (payload) => { - this.props.saveAlbumStudio(payload); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -AlbumStudioConnector.propTypes = { - setAlbumStudioSort: PropTypes.func.isRequired, - setAlbumStudioFilter: PropTypes.func.isRequired, - fetchAlbums: PropTypes.func.isRequired, - clearAlbums: PropTypes.func.isRequired, - saveAlbumStudio: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(AlbumStudioConnector); diff --git a/frontend/src/AlbumStudio/AlbumStudioFilterModalConnector.js b/frontend/src/AlbumStudio/AlbumStudioFilterModalConnector.js deleted file mode 100644 index db378a7f2..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioFilterModalConnector.js +++ /dev/null @@ -1,24 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import FilterModal from 'Components/Filter/FilterModal'; -import { setAlbumStudioFilter } from 'Store/Actions/albumStudioActions'; - -function createMapStateToProps() { - return createSelector( - (state) => state.artist.items, - (state) => state.albumStudio.filterBuilderProps, - (sectionItems, filterBuilderProps) => { - return { - sectionItems, - filterBuilderProps, - customFilterType: 'albumStudio' - }; - } - ); -} - -const mapDispatchToProps = { - dispatchSetFilter: setAlbumStudioFilter -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(FilterModal); diff --git a/frontend/src/AlbumStudio/AlbumStudioFooter.css b/frontend/src/AlbumStudio/AlbumStudioFooter.css deleted file mode 100644 index 11ea5496a..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioFooter.css +++ /dev/null @@ -1,14 +0,0 @@ -.inputContainer { - margin-right: 20px; -} - -.label { - margin-bottom: 3px; - font-weight: bold; -} - -.updateSelectedButton { - composes: button from '~Components/Link/SpinnerButton.css'; - - height: 35px; -} diff --git a/frontend/src/AlbumStudio/AlbumStudioFooter.css.d.ts b/frontend/src/AlbumStudio/AlbumStudioFooter.css.d.ts deleted file mode 100644 index 83e60938b..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioFooter.css.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'inputContainer': string; - 'label': string; - 'updateSelectedButton': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/AlbumStudio/AlbumStudioFooter.js b/frontend/src/AlbumStudio/AlbumStudioFooter.js deleted file mode 100644 index f579d0dd0..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioFooter.js +++ /dev/null @@ -1,174 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import MonitorAlbumsSelectInput from 'Components/Form/MonitorAlbumsSelectInput'; -import MonitorNewItemsSelectInput from 'Components/Form/MonitorNewItemsSelectInput'; -import SpinnerButton from 'Components/Link/SpinnerButton'; -import PageContentFooter from 'Components/Page/PageContentFooter'; -import { inputTypes, kinds } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; -import styles from './AlbumStudioFooter.css'; - -const NO_CHANGE = 'noChange'; - -class AlbumStudioFooter extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - monitored: NO_CHANGE, - monitor: NO_CHANGE, - monitorNewItems: NO_CHANGE - }; - } - - componentDidUpdate(prevProps) { - const { - isSaving, - saveError - } = this.props; - - if (prevProps.isSaving && !isSaving && !saveError) { - this.setState({ - monitored: NO_CHANGE, - monitor: NO_CHANGE, - monitorNewItems: NO_CHANGE - }); - } - } - - // - // Listeners - - onInputChange = ({ name, value }) => { - this.setState({ [name]: value }); - }; - - onUpdateSelectedPress = () => { - const { - monitor, - monitored, - monitorNewItems - } = this.state; - - const changes = {}; - - if (monitored !== NO_CHANGE) { - changes.monitored = monitored === 'monitored'; - } - - if (monitor !== NO_CHANGE) { - changes.monitor = monitor; - } - - if (monitorNewItems !== NO_CHANGE) { - changes.monitorNewItems = monitorNewItems; - } - - this.props.onUpdateSelectedPress(changes); - }; - - // - // Render - - render() { - const { - selectedCount, - isSaving - } = this.props; - - const { - monitored, - monitor, - monitorNewItems - } = this.state; - - const monitoredOptions = [ - { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, - { key: 'monitored', value: translate('Monitored') }, - { key: 'unmonitored', value: translate('Unmonitored') } - ]; - - const noChanges = monitored === NO_CHANGE && - monitor === NO_CHANGE && - monitorNewItems === NO_CHANGE; - - return ( - -
-
- {translate('MonitorArtist')} -
- - -
- -
-
- {translate('MonitorExistingAlbums')} -
- - -
- -
-
- {translate('MonitorNewAlbums')} -
- - -
- -
-
- {translate('CountArtistsSelected', { count: selectedCount })} -
- - - {translate('UpdateSelected')} - -
-
- ); - } -} - -AlbumStudioFooter.propTypes = { - selectedCount: PropTypes.number.isRequired, - isSaving: PropTypes.bool.isRequired, - saveError: PropTypes.object, - onUpdateSelectedPress: PropTypes.func.isRequired -}; - -export default AlbumStudioFooter; diff --git a/frontend/src/AlbumStudio/AlbumStudioRow.css b/frontend/src/AlbumStudio/AlbumStudioRow.css deleted file mode 100644 index d8def1d50..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioRow.css +++ /dev/null @@ -1,41 +0,0 @@ -.cell { - composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css'; - - display: flex; - align-items: center; -} - -.selectCell { - composes: cell from '~Components/Table/Cells/TableRowCell.css'; - - display: flex; - align-items: center; -} - -.status { - composes: cell from '~Components/Table/Cells/TableRowCell.css'; - - display: flex; - align-items: center; - padding: 0; - min-width: 60px; -} - -.title { - composes: cell from '~Components/Table/Cells/TableRowCell.css'; - - display: flex; - align-items: center; - - flex-shrink: 0; - min-width: 110px; -} - -.albums { - composes: cell; - - display: flex; - flex-grow: 4; - flex-wrap: wrap; - min-width: 400px; -} diff --git a/frontend/src/AlbumStudio/AlbumStudioRow.css.d.ts b/frontend/src/AlbumStudio/AlbumStudioRow.css.d.ts deleted file mode 100644 index 9f9ac8eac..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioRow.css.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'albums': string; - 'cell': string; - 'selectCell': string; - 'status': string; - 'title': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/AlbumStudio/AlbumStudioRow.js b/frontend/src/AlbumStudio/AlbumStudioRow.js deleted file mode 100644 index 5a13f442d..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioRow.js +++ /dev/null @@ -1,95 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import ArtistNameLink from 'Artist/ArtistNameLink'; -import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell'; -import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell'; -import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell'; -import AlbumStudioAlbum from './AlbumStudioAlbum'; -import styles from './AlbumStudioRow.css'; - -class AlbumStudioRow extends Component { - - // - // Render - - render() { - const { - artistId, - status, - foreignArtistId, - artistName, - artistType, - monitored, - albums, - isSaving, - isSelected, - onSelectedChange, - onArtistMonitoredPress, - onAlbumMonitoredPress - } = this.props; - - return ( - <> - - - - - - - - - - { - albums.map((album) => { - return ( - - ); - }) - } - - - ); - } -} - -AlbumStudioRow.propTypes = { - artistId: PropTypes.number.isRequired, - status: PropTypes.string.isRequired, - foreignArtistId: PropTypes.string.isRequired, - artistName: PropTypes.string.isRequired, - artistType: PropTypes.string, - monitored: PropTypes.bool.isRequired, - albums: PropTypes.arrayOf(PropTypes.object).isRequired, - isSaving: PropTypes.bool.isRequired, - isSelected: PropTypes.bool, - onSelectedChange: PropTypes.func.isRequired, - onArtistMonitoredPress: PropTypes.func.isRequired, - onAlbumMonitoredPress: PropTypes.func.isRequired -}; - -AlbumStudioRow.defaultProps = { - isSaving: false -}; - -export default AlbumStudioRow; diff --git a/frontend/src/AlbumStudio/AlbumStudioRowConnector.js b/frontend/src/AlbumStudio/AlbumStudioRowConnector.js deleted file mode 100644 index fd0bd21dc..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioRowConnector.js +++ /dev/null @@ -1,94 +0,0 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { toggleAlbumsMonitored } from 'Store/Actions/albumActions'; -import { toggleArtistMonitored } from 'Store/Actions/artistActions'; -import createArtistSelector from 'Store/Selectors/createArtistSelector'; -import AlbumStudioRow from './AlbumStudioRow'; - -// Use a const to share the reselect cache between instances -const getAlbumMap = createSelector( - (state) => state.albums.items, - (albums) => { - return albums.reduce((acc, curr) => { - (acc[curr.artistId] = acc[curr.artistId] || []).push(curr); - return acc; - }, {}); - } -); - -function createMapStateToProps() { - return createSelector( - createArtistSelector(), - getAlbumMap, - (artist, albumMap) => { - const albumsInArtist = albumMap.hasOwnProperty(artist.id) ? albumMap[artist.id] : []; - const sortedAlbums = _.orderBy(albumsInArtist, 'releaseDate', 'desc'); - - return { - ...artist, - artistId: artist.id, - artistName: artist.artistName, - monitored: artist.monitored, - status: artist.status, - isSaving: artist.isSaving, - albums: sortedAlbums - }; - } - ); -} - -const mapDispatchToProps = { - toggleArtistMonitored, - toggleAlbumsMonitored -}; - -class AlbumStudioRowConnector extends Component { - - // - // Listeners - - onArtistMonitoredPress = () => { - const { - artistId, - monitored - } = this.props; - - this.props.toggleArtistMonitored({ - artistId, - monitored: !monitored - }); - }; - - onAlbumMonitoredPress = (albumId, monitored) => { - const albumIds = [albumId]; - this.props.toggleAlbumsMonitored({ - albumIds, - monitored - }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -AlbumStudioRowConnector.propTypes = { - artistId: PropTypes.number.isRequired, - monitored: PropTypes.bool.isRequired, - toggleArtistMonitored: PropTypes.func.isRequired, - toggleAlbumsMonitored: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(AlbumStudioRowConnector); diff --git a/frontend/src/AlbumStudio/AlbumStudioTableHeader.css b/frontend/src/AlbumStudio/AlbumStudioTableHeader.css deleted file mode 100644 index da21f0553..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioTableHeader.css +++ /dev/null @@ -1,18 +0,0 @@ -.status { - composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; - - flex: 0 0 60px; - padding: 0; -} - -.sortName { - composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; - - flex: 0 0 110px; -} - -.albumCount { - composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; - - padding: 12px; -} diff --git a/frontend/src/AlbumStudio/AlbumStudioTableHeader.css.d.ts b/frontend/src/AlbumStudio/AlbumStudioTableHeader.css.d.ts deleted file mode 100644 index 3978a6c39..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioTableHeader.css.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'albumCount': string; - 'sortName': string; - 'status': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/AlbumStudio/AlbumStudioTableHeader.js b/frontend/src/AlbumStudio/AlbumStudioTableHeader.js deleted file mode 100644 index 8d18babbe..000000000 --- a/frontend/src/AlbumStudio/AlbumStudioTableHeader.js +++ /dev/null @@ -1,61 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import VirtualTableHeader from 'Components/Table/VirtualTableHeader'; -import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell'; -import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell'; -import styles from './AlbumStudioTableHeader.css'; - -function AlbumStudioTableHeader(props) { - const { - columns, - allSelected, - allUnselected, - onSelectAllChange, - ...otherProps - } = props; - - return ( - - - { - columns.map((column) => { - const { - name, - label, - isSortable, - isVisible - } = column; - - if (!isVisible) { - return null; - } - - return ( - - {typeof label === 'function' ? label() : label} - - ); - }) - } - - ); -} - -AlbumStudioTableHeader.propTypes = { - columns: PropTypes.arrayOf(PropTypes.object).isRequired, - allSelected: PropTypes.bool.isRequired, - allUnselected: PropTypes.bool.isRequired, - onSelectAllChange: PropTypes.func.isRequired -}; - -export default AlbumStudioTableHeader; diff --git a/frontend/src/App/AppRoutes.js b/frontend/src/App/AppRoutes.js index af938fd83..0af990f43 100644 --- a/frontend/src/App/AppRoutes.js +++ b/frontend/src/App/AppRoutes.js @@ -5,7 +5,6 @@ import BlocklistConnector from 'Activity/Blocklist/BlocklistConnector'; import HistoryConnector from 'Activity/History/HistoryConnector'; import QueueConnector from 'Activity/Queue/QueueConnector'; import AlbumDetailsPageConnector from 'Album/Details/AlbumDetailsPageConnector'; -import AlbumStudioConnector from 'AlbumStudio/AlbumStudioConnector'; import ArtistDetailsPageConnector from 'Artist/Details/ArtistDetailsPageConnector'; import ArtistIndex from 'Artist/Index/ArtistIndex'; import CalendarPageConnector from 'Calendar/CalendarPageConnector'; @@ -90,7 +89,15 @@ function AppRoutes(props) { { + return ( + + ); + }} /> translate('AddNew'), to: '/add/search' }, - { - title: () => translate('AlbumStudio'), - to: '/albumstudio' - }, { title: () => translate('UnmappedFiles'), to: '/unmapped' diff --git a/frontend/src/Store/Actions/albumStudioActions.js b/frontend/src/Store/Actions/albumStudioActions.js deleted file mode 100644 index 39fbe8d20..000000000 --- a/frontend/src/Store/Actions/albumStudioActions.js +++ /dev/null @@ -1,167 +0,0 @@ -import { createAction } from 'redux-actions'; -import { filterBuilderTypes, filterBuilderValueTypes, sortDirections } from 'Helpers/Props'; -import { createThunk, handleThunks } from 'Store/thunks'; -import createAjaxRequest from 'Utilities/createAjaxRequest'; -import translate from 'Utilities/String/translate'; -import { fetchAlbums } from './albumActions'; -import { filterPredicates, filters } from './artistActions'; -import { set } from './baseActions'; -import createHandleActions from './Creators/createHandleActions'; -import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer'; -import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer'; - -// -// Variables - -export const section = 'albumStudio'; - -// -// State - -export const defaultState = { - isSaving: false, - saveError: null, - sortKey: 'sortName', - sortDirection: sortDirections.ASCENDING, - secondarySortKey: 'sortName', - secondarySortDirection: sortDirections.ASCENDING, - selectedFilterKey: 'all', - filters, - filterPredicates, - - filterBuilderProps: [ - { - name: 'monitored', - label: () => translate('Monitored'), - type: filterBuilderTypes.EXACT, - valueType: filterBuilderValueTypes.BOOL - }, - { - name: 'status', - label: () => translate('Status'), - type: filterBuilderTypes.EXACT, - valueType: filterBuilderValueTypes.ARTIST_STATUS - }, - { - name: 'artistType', - label: () => translate('ArtistType'), - type: filterBuilderTypes.EXACT - }, - { - name: 'qualityProfileId', - label: () => translate('QualityProfile'), - type: filterBuilderTypes.EXACT, - valueType: filterBuilderValueTypes.QUALITY_PROFILE - }, - { - name: 'metadataProfileId', - label: () => translate('MetadataProfile'), - type: filterBuilderTypes.EXACT, - valueType: filterBuilderValueTypes.METADATA_PROFILE - }, - { - name: 'rootFolderPath', - label: () => translate('RootFolderPath'), - type: filterBuilderTypes.EXACT - }, - { - name: 'tags', - label: () => translate('Tags'), - type: filterBuilderTypes.ARRAY, - valueType: filterBuilderValueTypes.TAG - } - ] -}; - -export const persistState = [ - 'albumStudio.sortKey', - 'albumStudio.sortDirection', - 'albumStudio.selectedFilterKey', - 'albumStudio.customFilters' -]; - -// -// Actions Types - -export const SET_ALBUM_STUDIO_SORT = 'albumStudio/setAlbumStudioSort'; -export const SET_ALBUM_STUDIO_FILTER = 'albumStudio/setAlbumStudioFilter'; -export const SAVE_ALBUM_STUDIO = 'albumStudio/saveAlbumStudio'; - -// -// Action Creators - -export const setAlbumStudioSort = createAction(SET_ALBUM_STUDIO_SORT); -export const setAlbumStudioFilter = createAction(SET_ALBUM_STUDIO_FILTER); -export const saveAlbumStudio = createThunk(SAVE_ALBUM_STUDIO); - -// -// Action Handlers - -export const actionHandlers = handleThunks({ - - [SAVE_ALBUM_STUDIO]: function(getState, payload, dispatch) { - const { - artistIds, - monitor, - monitored, - monitorNewItems - } = payload; - - const artists = []; - - artistIds.forEach((id) => { - const artistsToUpdate = { id }; - - if (payload.hasOwnProperty('monitored')) { - artistsToUpdate.monitored = monitored; - } - - artists.push(artistsToUpdate); - }); - - dispatch(set({ - section, - isSaving: true - })); - - const promise = createAjaxRequest({ - url: '/albumStudio', - method: 'POST', - data: JSON.stringify({ - artist: artists, - monitoringOptions: { monitor }, - monitorNewItems - }), - dataType: 'json' - }).request; - - promise.done((data) => { - dispatch(fetchAlbums()); - - dispatch(set({ - section, - isSaving: false, - saveError: null - })); - }); - - promise.fail((xhr) => { - dispatch(set({ - section, - isSaving: false, - saveError: xhr - })); - }); - } -}); - -// -// Reducers - -export const reducers = createHandleActions({ - - [SET_ALBUM_STUDIO_SORT]: createSetClientSideCollectionSortReducer(section), - [SET_ALBUM_STUDIO_FILTER]: createSetClientSideCollectionFilterReducer(section) - -}, defaultState, section); - diff --git a/frontend/src/Store/Actions/index.js b/frontend/src/Store/Actions/index.js index d82b9790f..95b02d089 100644 --- a/frontend/src/Store/Actions/index.js +++ b/frontend/src/Store/Actions/index.js @@ -1,6 +1,5 @@ import * as albums from './albumActions'; import * as albumHistory from './albumHistoryActions'; -import * as albumStudio from './albumStudioActions'; import * as app from './appActions'; import * as artist from './artistActions'; import * as artistHistory from './artistHistoryActions'; @@ -46,7 +45,6 @@ export default [ providerOptions, queue, releases, - albumStudio, artist, artistHistory, artistIndex,