From 796eba82ac021556c751cf4dac890fb3ecc7cd90 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 13 Dec 2020 21:49:54 -0500 Subject: [PATCH] Mass Editor size and options Co-Authored-By: Mark McDowall --- frontend/src/Artist/Editor/ArtistEditor.js | 78 +++------ .../Artist/Editor/ArtistEditorConnector.js | 13 +- .../src/Artist/Editor/ArtistEditorFooter.js | 164 +++++++++++------- frontend/src/Artist/Editor/ArtistEditorRow.js | 143 ++++++++++----- .../src/Store/Actions/artistEditorActions.js | 62 +++++++ 5 files changed, 297 insertions(+), 163 deletions(-) diff --git a/frontend/src/Artist/Editor/ArtistEditor.js b/frontend/src/Artist/Editor/ArtistEditor.js index 8bbe48ac1..8db9a0474 100644 --- a/frontend/src/Artist/Editor/ArtistEditor.js +++ b/frontend/src/Artist/Editor/ArtistEditor.js @@ -6,10 +6,13 @@ import FilterMenu from 'Components/Menu/FilterMenu'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; +import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; +import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import Table from 'Components/Table/Table'; import TableBody from 'Components/Table/TableBody'; -import { align, sortDirections } from 'Helpers/Props'; +import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; +import { align, icons, sortDirections } from 'Helpers/Props'; import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getSelectedIds from 'Utilities/Table/getSelectedIds'; import selectAll from 'Utilities/Table/selectAll'; @@ -20,52 +23,6 @@ import ArtistEditorRowConnector from './ArtistEditorRowConnector'; import RetagArtistModal from './AudioTags/RetagArtistModal'; import OrganizeArtistModal from './Organize/OrganizeArtistModal'; -function getColumns(showMetadataProfile) { - return [ - { - name: 'status', - isSortable: true, - isVisible: true - }, - { - name: 'sortName', - label: 'Name', - isSortable: true, - isVisible: true - }, - { - name: 'qualityProfileId', - label: 'Quality Profile', - isSortable: true, - isVisible: true - }, - { - name: 'metadataProfileId', - label: 'Metadata Profile', - isSortable: true, - isVisible: showMetadataProfile - }, - { - name: 'albumFolder', - label: 'Album Folder', - isSortable: true, - isVisible: true - }, - { - name: 'path', - label: 'Path', - isSortable: true, - isVisible: true - }, - { - name: 'tags', - label: 'Tags', - isSortable: false, - isVisible: true - } - ]; -} - class ArtistEditor extends Component { // @@ -80,8 +37,7 @@ class ArtistEditor extends Component { lastToggled: null, selectedState: {}, isOrganizingArtistModalOpen: false, - isRetaggingArtistModalOpen: false, - columns: getColumns(props.showMetadataProfile) + isRetaggingArtistModalOpen: false }; } @@ -161,6 +117,7 @@ class ArtistEditor extends Component { error, totalItems, items, + columns, selectedFilterKey, filters, customFilters, @@ -172,7 +129,7 @@ class ArtistEditor extends Component { deleteError, isOrganizingArtist, isRetaggingArtist, - showMetadataProfile, + onTableOptionChange, onSortPress, onFilterSelect } = this.props; @@ -180,8 +137,7 @@ class ArtistEditor extends Component { const { allSelected, allUnselected, - selectedState, - columns + selectedState } = this.state; const selectedArtistIds = this.getSelectedIds(); @@ -191,6 +147,18 @@ class ArtistEditor extends Component { + + + + + + column.name === 'metadataProfileId').isVisible} onSaveSelected={this.onSaveSelected} onOrganizeArtistPress={this.onOrganizeArtistPress} onRetagArtistPress={this.onRetagArtistPress} @@ -290,6 +259,7 @@ ArtistEditor.propTypes = { error: PropTypes.object, totalItems: PropTypes.number.isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired, + columns: PropTypes.arrayOf(PropTypes.object).isRequired, sortKey: PropTypes.string, sortDirection: PropTypes.oneOf(sortDirections.all), selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, @@ -301,7 +271,7 @@ ArtistEditor.propTypes = { deleteError: PropTypes.object, isOrganizingArtist: PropTypes.bool.isRequired, isRetaggingArtist: PropTypes.bool.isRequired, - showMetadataProfile: PropTypes.bool.isRequired, + onTableOptionChange: PropTypes.func.isRequired, onSortPress: PropTypes.func.isRequired, onFilterSelect: PropTypes.func.isRequired, onSaveSelected: PropTypes.func.isRequired diff --git a/frontend/src/Artist/Editor/ArtistEditorConnector.js b/frontend/src/Artist/Editor/ArtistEditorConnector.js index 1fda0f4d9..427aab9b6 100644 --- a/frontend/src/Artist/Editor/ArtistEditorConnector.js +++ b/frontend/src/Artist/Editor/ArtistEditorConnector.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import * as commandNames from 'Commands/commandNames'; -import { saveArtistEditor, setArtistEditorFilter, setArtistEditorSort } from 'Store/Actions/artistEditorActions'; +import { saveArtistEditor, setArtistEditorFilter, setArtistEditorSort, setArtistEditorTableOption } from 'Store/Actions/artistEditorActions'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchRootFolders } from 'Store/Actions/settingsActions'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; @@ -12,15 +12,13 @@ import ArtistEditor from './ArtistEditor'; function createMapStateToProps() { return createSelector( - (state) => state.settings.metadataProfiles, createClientSideCollectionSelector('artist', 'artistEditor'), createCommandExecutingSelector(commandNames.RENAME_ARTIST), createCommandExecutingSelector(commandNames.RETAG_ARTIST), - (metadataProfiles, artist, isOrganizingArtist, isRetaggingArtist) => { + (artist, isOrganizingArtist, isRetaggingArtist) => { return { isOrganizingArtist, isRetaggingArtist, - showMetadataProfile: metadataProfiles.items.length > 1, ...artist }; } @@ -30,6 +28,7 @@ function createMapStateToProps() { const mapDispatchToProps = { dispatchSetArtistEditorSort: setArtistEditorSort, dispatchSetArtistEditorFilter: setArtistEditorFilter, + dispatchSetArtistEditorTableOption: setArtistEditorTableOption, dispatchSaveArtistEditor: saveArtistEditor, dispatchFetchRootFolders: fetchRootFolders, dispatchExecuteCommand: executeCommand @@ -55,6 +54,10 @@ class ArtistEditorConnector extends Component { this.props.dispatchSetArtistEditorFilter({ selectedFilterKey }); } + onTableOptionChange = (payload) => { + this.props.dispatchSetArtistEditorTableOption(payload); + } + onSaveSelected = (payload) => { this.props.dispatchSaveArtistEditor(payload); } @@ -76,6 +79,7 @@ class ArtistEditorConnector extends Component { onSortPress={this.onSortPress} onFilterSelect={this.onFilterSelect} onSaveSelected={this.onSaveSelected} + onTableOptionChange={this.onTableOptionChange} /> ); } @@ -84,6 +88,7 @@ class ArtistEditorConnector extends Component { ArtistEditorConnector.propTypes = { dispatchSetArtistEditorSort: PropTypes.func.isRequired, dispatchSetArtistEditorFilter: PropTypes.func.isRequired, + dispatchSetArtistEditorTableOption: PropTypes.func.isRequired, dispatchSaveArtistEditor: PropTypes.func.isRequired, dispatchFetchRootFolders: PropTypes.func.isRequired, dispatchExecuteCommand: PropTypes.func.isRequired diff --git a/frontend/src/Artist/Editor/ArtistEditorFooter.js b/frontend/src/Artist/Editor/ArtistEditorFooter.js index 83cd41872..b7ee90170 100644 --- a/frontend/src/Artist/Editor/ArtistEditorFooter.js +++ b/frontend/src/Artist/Editor/ArtistEditorFooter.js @@ -143,7 +143,7 @@ class ArtistEditorFooter extends Component { isDeleting, isOrganizingArtist, isRetaggingArtist, - showMetadataProfile, + columns, onOrganizeArtistPress, onRetagArtistPress } = this.props; @@ -190,71 +190,110 @@ class ArtistEditorFooter extends Component { /> -
- - - -
- { - showMetadataProfile && -
- + columns.map((column) => { + const { + name, + isVisible + } = column; - -
+ if (!isVisible) { + return null; + } + + if (name === 'qualityProfileId') { + return ( +
+ + + +
+ ); + } + + if (name === 'metadataProfileId') { + return ( +
+ + + +
+ ); + } + + if (name === 'albumFolder') { + return ( +
+ + + +
+ ); + } + + if (name === 'path') { + return ( +
+ + + +
+ ); + } + + return null; + }) } -
- - - -
- -
- - - -
-
- - - - - - - - {qualityProfile.name} - - { - _.find(columns, { name: 'metadataProfileId' }).isVisible && - - {metadataProfile.name} - + columns.map((column) => { + const { + name, + isVisible + } = column; + + if (!isVisible) { + return null; + } + + if (name === 'status') { + return ( + + ); + } + + if (name === 'sortName') { + return ( + + + + ); + } + + if (name === 'qualityProfileId') { + return ( + + {qualityProfile.name} + + ); + } + + if (name === 'metadataProfileId') { + return ( + + {metadataProfile.name} + + ); + } + + if (name === 'albumFolder') { + return ( + + + + ); + } + + if (name === 'path') { + return ( + + {path} + + ); + } + + if (name === 'sizeOnDisk') { + return ( + + {formatBytes(statistics.sizeOnDisk)} + + ); + } + + if (name === 'tags') { + return ( + + + + ); + } + + return null; + }) } - - - - - - - {path} - - - - - ); } @@ -111,6 +167,7 @@ ArtistEditorRow.propTypes = { qualityProfile: PropTypes.object.isRequired, albumFolder: PropTypes.bool.isRequired, path: PropTypes.string.isRequired, + statistics: PropTypes.object.isRequired, tags: PropTypes.arrayOf(PropTypes.number).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired, isSaving: PropTypes.bool.isRequired, diff --git a/frontend/src/Store/Actions/artistEditorActions.js b/frontend/src/Store/Actions/artistEditorActions.js index 1563742de..844cf971c 100644 --- a/frontend/src/Store/Actions/artistEditorActions.js +++ b/frontend/src/Store/Actions/artistEditorActions.js @@ -8,6 +8,7 @@ import { set, updateItem } from './baseActions'; import createHandleActions from './Creators/createHandleActions'; import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer'; import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer'; +import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer'; // // Variables @@ -30,6 +31,58 @@ export const defaultState = { filters, filterPredicates, + columns: [ + { + name: 'status', + columnLabel: 'Status', + isSortable: true, + isVisible: true, + isModifiable: false + }, + { + name: 'sortName', + label: 'Name', + isSortable: true, + isVisible: true + }, + { + name: 'qualityProfileId', + label: 'Quality Profile', + isSortable: true, + isVisible: true + }, + { + name: 'metadataProfileId', + label: 'Metadata Profile', + isSortable: true, + isVisible: false + }, + { + name: 'albumFolder', + label: 'Album Folder', + isSortable: true, + isVisible: true + }, + { + name: 'path', + label: 'Path', + isSortable: true, + isVisible: true + }, + { + name: 'sizeOnDisk', + label: 'Size on Disk', + isSortable: true, + isVisible: false + }, + { + name: 'tags', + label: 'Tags', + isSortable: false, + isVisible: true + } + ], + filterBuilderProps: [ { name: 'monitored', @@ -65,6 +118,12 @@ export const defaultState = { label: 'Root Folder Path', type: filterBuilderTypes.EXACT }, + { + name: 'sizeOnDisk', + label: 'Size on Disk', + type: filterBuilderTypes.NUMBER, + valueType: filterBuilderValueTypes.BYTES + }, { name: 'tags', label: 'Tags', @@ -90,6 +149,7 @@ export const SET_ARTIST_EDITOR_SORT = 'artistEditor/setArtistEditorSort'; export const SET_ARTIST_EDITOR_FILTER = 'artistEditor/setArtistEditorFilter'; export const SAVE_ARTIST_EDITOR = 'artistEditor/saveArtistEditor'; export const BULK_DELETE_ARTIST = 'artistEditor/bulkDeleteArtist'; +export const SET_ARTIST_EDITOR_TABLE_OPTION = 'artistEditor/setArtistEditorTableOption'; // // Action Creators @@ -98,6 +158,7 @@ export const setArtistEditorSort = createAction(SET_ARTIST_EDITOR_SORT); export const setArtistEditorFilter = createAction(SET_ARTIST_EDITOR_FILTER); export const saveArtistEditor = createThunk(SAVE_ARTIST_EDITOR); export const bulkDeleteArtist = createThunk(BULK_DELETE_ARTIST); +export const setArtistEditorTableOption = createAction(SET_ARTIST_EDITOR_TABLE_OPTION); // // Action Handlers @@ -181,6 +242,7 @@ export const actionHandlers = handleThunks({ export const reducers = createHandleActions({ + [SET_ARTIST_EDITOR_TABLE_OPTION]: createSetTableOptionReducer(section), [SET_ARTIST_EDITOR_SORT]: createSetClientSideCollectionSortReducer(section), [SET_ARTIST_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section)