mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-18 21:34:28 -04:00
New: Optionally display authors as LastName, FirstName in index
Fixes #1062
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user