New: Optionally display authors as LastName, FirstName in index

Fixes #1062
This commit is contained in:
ta264
2021-07-21 21:50:17 +01:00
parent 332997aefe
commit 7f8dc3d2b4
28 changed files with 193 additions and 54 deletions
+2 -2
View File
@@ -94,13 +94,13 @@ class AuthorIndex extends Component {
} = this.props;
// Reset if not sorting by sortName
if (sortKey !== 'sortName') {
if (sortKey !== 'sortName' && sortKey !== 'sortNameLastFirst') {
this.setState({ jumpBarItems: { order: [] } });
return;
}
const characters = _.reduce(items, (acc, item) => {
let char = item.sortName.charAt(0);
let char = item[sortKey].charAt(0);
if (!isNaN(char)) {
char = '#';
@@ -34,16 +34,16 @@ function AuthorIndexSortMenu(props) {
sortDirection={sortDirection}
onPress={onSortSelect}
>
Name
First Name
</SortMenuItem>
<SortMenuItem
name="authorType"
name="sortNameLastFirst"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
Type
Last Name
</SortMenuItem>
<SortMenuItem
@@ -74,6 +74,7 @@ class AuthorIndexOverview extends Component {
const {
id,
authorName,
authorNameLastFirst,
overview,
monitored,
status,
@@ -167,7 +168,7 @@ class AuthorIndexOverview extends Component {
className={styles.title}
to={link}
>
{authorName}
{overviewOptions.showTitle === 'firstLast' ? authorName : authorNameLastFirst}
</Link>
<div className={styles.actions}>
@@ -247,7 +248,8 @@ class AuthorIndexOverview extends Component {
AuthorIndexOverview.propTypes = {
id: PropTypes.number.isRequired,
authorName: PropTypes.string.isRequired,
overview: PropTypes.string.isRequired,
authorNameLastFirst: PropTypes.string.isRequired,
overview: PropTypes.string,
monitored: PropTypes.bool.isRequired,
status: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired,
@@ -90,7 +90,8 @@ class AuthorIndexOverviews extends Component {
if (this._grid &&
(prevState.width !== width ||
prevState.rowHeight !== rowHeight ||
hasDifferentItemsOrOrder(prevProps.items, items))) {
hasDifferentItemsOrOrder(prevProps.items, items) ||
prevProps.overviewOptions.showTitle !== overviewOptions.showTitle)) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
@@ -101,7 +102,7 @@ class AuthorIndexOverviews extends Component {
}
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const index = getIndexOfFirstCharacter(items, jumpToCharacter);
const index = getIndexOfFirstCharacter(items, sortKey, jumpToCharacter);
if (this._grid && index != null) {
@@ -13,6 +13,11 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const nameOptions = [
{ key: 'firstLast', value: translate('NameFirstLast') },
{ key: 'lastFirst', value: translate('NameLastFirst') }
];
const posterSizeOptions = [
{ key: 'small', value: 'Small' },
{ key: 'medium', value: 'Medium' },
@@ -28,6 +33,7 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
super(props, context);
this.state = {
showTitle: props.showTitle,
detailedProgressBar: props.detailedProgressBar,
size: props.size,
showMonitored: props.showMonitored,
@@ -43,6 +49,7 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
componentDidUpdate(prevProps) {
const {
showTitle,
detailedProgressBar,
size,
showMonitored,
@@ -57,6 +64,10 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
const state = {};
if (showTitle !== prevProps.showTitle) {
state.showTitle = showTitle;
}
if (detailedProgressBar !== prevProps.detailedProgressBar) {
state.detailedProgressBar = detailedProgressBar;
}
@@ -122,6 +133,7 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
} = this.props;
const {
showTitle,
detailedProgressBar,
size,
showMonitored,
@@ -142,6 +154,20 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
<ModalBody>
<Form>
<FormGroup>
<FormLabel>
{translate('NameStyle')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="showTitle"
value={showTitle}
values={nameOptions}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>
{translate('PosterSize')}
@@ -291,6 +317,7 @@ class AuthorIndexOverviewOptionsModalContent extends Component {
}
AuthorIndexOverviewOptionsModalContent.propTypes = {
showTitle: PropTypes.string.isRequired,
size: PropTypes.string.isRequired,
detailedProgressBar: PropTypes.bool.isRequired,
showMonitored: PropTypes.bool.isRequired,
@@ -70,6 +70,7 @@ class AuthorIndexPoster extends Component {
const {
id,
authorName,
authorNameLastFirst,
monitored,
titleSlug,
status,
@@ -193,9 +194,9 @@ class AuthorIndexPoster extends Component {
/>
{
showTitle &&
showTitle !== 'no' &&
<div className={styles.title}>
{authorName}
{showTitle === 'firstLast' ? authorName : authorNameLastFirst}
</div>
}
@@ -260,6 +261,7 @@ class AuthorIndexPoster extends Component {
AuthorIndexPoster.propTypes = {
id: PropTypes.number.isRequired,
authorName: PropTypes.string.isRequired,
authorNameLastFirst: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
status: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired,
@@ -269,7 +271,7 @@ AuthorIndexPoster.propTypes = {
posterWidth: PropTypes.number.isRequired,
posterHeight: PropTypes.number.isRequired,
detailedProgressBar: PropTypes.bool.isRequired,
showTitle: PropTypes.bool.isRequired,
showTitle: PropTypes.string.isRequired,
showMonitored: PropTypes.bool.isRequired,
showQualityProfile: PropTypes.bool.isRequired,
qualityProfile: PropTypes.object.isRequired,
@@ -50,7 +50,7 @@ function calculateRowHeight(posterHeight, sortKey, isSmallScreen, posterOptions)
isSmallScreen ? columnPaddingSmallScreen : columnPadding
];
if (showTitle) {
if (showTitle !== 'no') {
heights.push(19);
}
@@ -137,7 +137,8 @@ class AuthorIndexPosters extends Component {
prevState.columnWidth !== columnWidth ||
prevState.columnCount !== columnCount ||
prevState.rowHeight !== rowHeight ||
hasDifferentItemsOrOrder(prevProps.items, items))) {
hasDifferentItemsOrOrder(prevProps.items, items)) ||
prevProps.posterOptions.showTitle !== posterOptions.showTitle) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
@@ -148,7 +149,7 @@ class AuthorIndexPosters extends Component {
}
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const index = getIndexOfFirstCharacter(items, jumpToCharacter);
const index = getIndexOfFirstCharacter(items, sortKey, jumpToCharacter);
if (this._grid && index != null) {
const row = Math.floor(index / columnCount);
@@ -19,6 +19,12 @@ const posterSizeOptions = [
{ key: 'large', value: 'Large' }
];
const nameOptions = [
{ key: 'no', value: translate('NoName') },
{ key: 'firstLast', value: translate('NameFirstLast') },
{ key: 'lastFirst', value: translate('NameLastFirst') }
];
class AuthorIndexPosterOptionsModalContent extends Component {
//
@@ -148,9 +154,10 @@ class AuthorIndexPosterOptionsModalContent extends Component {
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
type={inputTypes.SELECT}
name="showTitle"
value={showTitle}
values={nameOptions}
helpText={translate('ShowTitleHelpText')}
onChange={this.onChangePosterOption}
/>
@@ -214,7 +221,7 @@ class AuthorIndexPosterOptionsModalContent extends Component {
AuthorIndexPosterOptionsModalContent.propTypes = {
size: PropTypes.string.isRequired,
showTitle: PropTypes.bool.isRequired,
showTitle: PropTypes.string.isRequired,
showMonitored: PropTypes.bool.isRequired,
showQualityProfile: PropTypes.bool.isRequired,
detailedProgressBar: PropTypes.bool.isRequired,
@@ -82,6 +82,7 @@ class AuthorIndexRow extends Component {
monitored,
status,
authorName,
authorNameLastFirst,
titleSlug,
qualityProfile,
metadataProfile,
@@ -95,6 +96,7 @@ class AuthorIndexRow extends Component {
tags,
images,
showBanners,
showTitle,
showSearchAction,
columns,
isRefreshingAuthor,
@@ -169,14 +171,14 @@ class AuthorIndexRow extends Component {
{
hasBannerError &&
<div className={styles.overlayTitle}>
{authorName}
{showTitle === 'firstLast' ? authorName : authorNameLastFirst}
</div>
}
</Link> :
<AuthorNameLink
titleSlug={titleSlug}
authorName={authorName}
authorName={showTitle === 'firstLast' ? authorName : authorNameLastFirst}
/>
}
</VirtualTableRowCell>
@@ -408,6 +410,7 @@ AuthorIndexRow.propTypes = {
monitored: PropTypes.bool.isRequired,
status: PropTypes.string.isRequired,
authorName: PropTypes.string.isRequired,
authorNameLastFirst: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired,
qualityProfile: PropTypes.object.isRequired,
metadataProfile: PropTypes.object.isRequired,
@@ -422,6 +425,7 @@ AuthorIndexRow.propTypes = {
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
showBanners: PropTypes.bool.isRequired,
showTitle: PropTypes.string.isRequired,
showSearchAction: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
isRefreshingAuthor: PropTypes.bool.isRequired,
@@ -25,12 +25,13 @@ class AuthorIndexTable extends Component {
componentDidUpdate(prevProps) {
const {
items,
sortKey,
jumpToCharacter
} = this.props;
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
const scrollIndex = getIndexOfFirstCharacter(items, jumpToCharacter);
const scrollIndex = getIndexOfFirstCharacter(items, sortKey, jumpToCharacter);
if (scrollIndex != null) {
this.setState({ scrollIndex });
@@ -47,7 +48,8 @@ class AuthorIndexTable extends Component {
const {
items,
columns,
showBanners
showBanners,
showTitle
} = this.props;
const author = items[rowIndex];
@@ -66,6 +68,7 @@ class AuthorIndexTable extends Component {
qualityProfileId={author.qualityProfileId}
metadataProfileId={author.metadataProfileId}
showBanners={showBanners}
showTitle={showTitle}
/>
</VirtualTableRow>
);
@@ -118,9 +121,10 @@ class AuthorIndexTable extends Component {
AuthorIndexTable.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortKey: PropTypes.string.isRequired,
sortDirection: PropTypes.oneOf(sortDirections.all),
showBanners: PropTypes.bool.isRequired,
showTitle: PropTypes.string.isRequired,
jumpToCharacter: PropTypes.string,
scrollTop: PropTypes.number,
scroller: PropTypes.instanceOf(Element).isRequired,
@@ -12,6 +12,7 @@ function createMapStateToProps() {
return {
isSmallScreen: dimensions.isSmallScreen,
showBanners: tableOptions.showBanners,
showTitle: tableOptions.showTitle,
columns
};
}
@@ -6,6 +6,11 @@ import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const nameOptions = [
{ key: 'firstLast', value: translate('NameFirstLast') },
{ key: 'lastFirst', value: translate('NameLastFirst') }
];
class AuthorIndexTableOptions extends Component {
//
@@ -16,23 +21,27 @@ class AuthorIndexTableOptions extends Component {
this.state = {
showBanners: props.showBanners,
showSearchAction: props.showSearchAction
showSearchAction: props.showSearchAction,
showTitle: props.showTitle
};
}
componentDidUpdate(prevProps) {
const {
showBanners,
showSearchAction
showSearchAction,
showTitle
} = this.props;
if (
showBanners !== prevProps.showBanners ||
showSearchAction !== prevProps.showSearchAction
showSearchAction !== prevProps.showSearchAction ||
showTitle !== prevProps.showTitle
) {
this.setState({
showBanners,
showSearchAction
showSearchAction,
showTitle
});
}
}
@@ -59,11 +68,26 @@ class AuthorIndexTableOptions extends Component {
render() {
const {
showBanners,
showSearchAction
showSearchAction,
showTitle
} = this.state;
return (
<Fragment>
<FormGroup>
<FormLabel>
{translate('NameStyle')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="showTitle"
value={showTitle}
values={nameOptions}
onChange={this.onTableOptionChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>
{translate('ShowBanners')}
@@ -97,6 +121,7 @@ class AuthorIndexTableOptions extends Component {
}
AuthorIndexTableOptions.propTypes = {
showTitle: PropTypes.string.isRequired,
showBanners: PropTypes.bool.isRequired,
showSearchAction: PropTypes.bool.isRequired,
onTableOptionChange: PropTypes.func.isRequired
@@ -16,31 +16,23 @@ export const section = 'authorIndex';
// State
export const defaultState = {
sortKey: 'sortName',
sortKey: 'sortNameLastFirst',
sortDirection: sortDirections.ASCENDING,
secondarySortKey: 'sortName',
secondarySortKey: 'sortNameLastFirst',
secondarySortDirection: sortDirections.ASCENDING,
view: 'posters',
posterOptions: {
detailedProgressBar: false,
size: 'large',
showTitle: true,
showMonitored: true,
showQualityProfile: true,
showSearchAction: false
},
bannerOptions: {
detailedProgressBar: false,
size: 'large',
showTitle: false,
showTitle: 'lastFirst',
showMonitored: true,
showQualityProfile: true,
showSearchAction: false
},
overviewOptions: {
showTitle: 'lastFirst',
detailedProgressBar: false,
size: 'medium',
showMonitored: true,
@@ -54,6 +46,7 @@ export const defaultState = {
},
tableOptions: {
showTitle: 'lastFirst',
showBanners: false,
showSearchAction: false
},
@@ -9,12 +9,14 @@ function createUnoptimizedSelector(uiSection) {
const items = authors.items.map((s) => {
const {
id,
sortName
sortName,
sortNameLastFirst
} = s;
return {
id,
sortName
sortName,
sortNameLastFirst
};
});
@@ -1,8 +1,8 @@
import _ from 'lodash';
export default function getIndexOfFirstCharacter(items, character) {
export default function getIndexOfFirstCharacter(items, sortKey, character) {
return _.findIndex(items, (item) => {
const firstCharacter = item.sortName.charAt(0);
const firstCharacter = item[sortKey].charAt(0);
if (character === '#') {
return !isNaN(firstCharacter);