mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-15 01:23:53 -07:00
New: Add Star Rating to Album table (#365)
This commit is contained in:
parent
73157534e0
commit
18b29f8208
6 changed files with 102 additions and 2 deletions
|
@ -9,6 +9,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
import AlbumSearchCellConnector from 'Album/AlbumSearchCellConnector';
|
import AlbumSearchCellConnector from 'Album/AlbumSearchCellConnector';
|
||||||
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||||
|
import StarRating from 'Components/StarRating';
|
||||||
import styles from './AlbumRow.css';
|
import styles from './AlbumRow.css';
|
||||||
|
|
||||||
function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
||||||
|
@ -74,6 +75,7 @@ class AlbumRow extends Component {
|
||||||
mediumCount,
|
mediumCount,
|
||||||
secondaryTypes,
|
secondaryTypes,
|
||||||
title,
|
title,
|
||||||
|
ratings,
|
||||||
isSaving,
|
isSaving,
|
||||||
artistMonitored,
|
artistMonitored,
|
||||||
foreignAlbumId,
|
foreignAlbumId,
|
||||||
|
@ -169,6 +171,19 @@ class AlbumRow extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name === 'rating') {
|
||||||
|
return (
|
||||||
|
<TableRowCell key={name}>
|
||||||
|
{
|
||||||
|
<StarRating
|
||||||
|
rating={ratings.value}
|
||||||
|
votes={ratings.votes}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (name === 'releaseDate') {
|
if (name === 'releaseDate') {
|
||||||
return (
|
return (
|
||||||
<RelativeDateCellConnector
|
<RelativeDateCellConnector
|
||||||
|
@ -223,6 +238,7 @@ AlbumRow.propTypes = {
|
||||||
mediumCount: PropTypes.number.isRequired,
|
mediumCount: PropTypes.number.isRequired,
|
||||||
duration: PropTypes.number.isRequired,
|
duration: PropTypes.number.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
ratings: PropTypes.object.isRequired,
|
||||||
secondaryTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
|
secondaryTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
foreignAlbumId: PropTypes.string.isRequired,
|
foreignAlbumId: PropTypes.string.isRequired,
|
||||||
isSaving: PropTypes.bool,
|
isSaving: PropTypes.bool,
|
||||||
|
|
|
@ -4,10 +4,12 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import connectSection from 'Store/connectSection';
|
||||||
import { findCommand } from 'Utilities/Command';
|
import { findCommand } from 'Utilities/Command';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import { toggleAlbumsMonitored, setAlbumsTableOption, setAlbumsSort } from 'Store/Actions/albumActions';
|
import { toggleAlbumsMonitored, setAlbumsTableOption, setAlbumsSort } from 'Store/Actions/albumActions';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
|
@ -16,7 +18,7 @@ import ArtistDetailsSeason from './ArtistDetailsSeason';
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { label }) => label,
|
(state, { label }) => label,
|
||||||
(state) => state.albums,
|
createClientSideCollectionSelector(),
|
||||||
createArtistSelector(),
|
createArtistSelector(),
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
|
@ -94,4 +96,10 @@ ArtistDetailsSeasonConnector.propTypes = {
|
||||||
executeCommand: PropTypes.func.isRequired
|
executeCommand: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistDetailsSeasonConnector);
|
export default connectSection(
|
||||||
|
createMapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ section: 'albums' }
|
||||||
|
)(ArtistDetailsSeasonConnector);
|
||||||
|
|
19
frontend/src/Components/StarRating.css
Normal file
19
frontend/src/Components/StarRating.css
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.starRating {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backStar {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
color: #515253;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frontStar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #ffbc0b;
|
||||||
|
}
|
44
frontend/src/Components/StarRating.js
Normal file
44
frontend/src/Components/StarRating.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import styles from './StarRating.css';
|
||||||
|
|
||||||
|
function StarRating({ rating, votes, iconSize }) {
|
||||||
|
const starWidth = {
|
||||||
|
width: `${rating * 10}%`
|
||||||
|
};
|
||||||
|
|
||||||
|
const helpText = `${rating/2} (${votes} Votes)`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={styles.starRating} title={helpText}>
|
||||||
|
<div className={styles.backStar}>
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<div className={styles.frontStar} style={starWidth}>
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
<Icon name={icons.STAR_FULL} size={iconSize} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
StarRating.propTypes = {
|
||||||
|
rating: PropTypes.number.isRequired,
|
||||||
|
votes: PropTypes.number.isRequired,
|
||||||
|
iconSize: PropTypes.number.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
StarRating.defaultProps = {
|
||||||
|
iconSize: 14
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StarRating;
|
|
@ -79,6 +79,7 @@ import fasSpinner from '@fortawesome/fontawesome-free-solid/faSpinner';
|
||||||
import fasSort from '@fortawesome/fontawesome-free-solid/faSort';
|
import fasSort from '@fortawesome/fontawesome-free-solid/faSort';
|
||||||
import fasSortDown from '@fortawesome/fontawesome-free-solid/faSortDown';
|
import fasSortDown from '@fortawesome/fontawesome-free-solid/faSortDown';
|
||||||
import fasSortUp from '@fortawesome/fontawesome-free-solid/faSortUp';
|
import fasSortUp from '@fortawesome/fontawesome-free-solid/faSortUp';
|
||||||
|
import fasStar from '@fortawesome/fontawesome-free-solid/faStar';
|
||||||
import fasStop from '@fortawesome/fontawesome-free-solid/faStop';
|
import fasStop from '@fortawesome/fontawesome-free-solid/faStop';
|
||||||
import fasSync from '@fortawesome/fontawesome-free-solid/faSync';
|
import fasSync from '@fortawesome/fontawesome-free-solid/faSync';
|
||||||
import fasTags from '@fortawesome/fontawesome-free-solid/faTags';
|
import fasTags from '@fortawesome/fontawesome-free-solid/faTags';
|
||||||
|
@ -178,6 +179,7 @@ export const SORT = fasSort;
|
||||||
export const SORT_ASCENDING = fasSortUp;
|
export const SORT_ASCENDING = fasSortUp;
|
||||||
export const SORT_DESCENDING = fasSortDown;
|
export const SORT_DESCENDING = fasSortDown;
|
||||||
export const SPINNER = fasSpinner;
|
export const SPINNER = fasSpinner;
|
||||||
|
export const STAR_FULL = fasStar;
|
||||||
export const SUBTRACT = fasMinus;
|
export const SUBTRACT = fasMinus;
|
||||||
export const SYSTEM = fasLaptop;
|
export const SYSTEM = fasLaptop;
|
||||||
export const TAGS = fasTags;
|
export const TAGS = fasTags;
|
||||||
|
|
|
@ -31,6 +31,11 @@ export const defaultState = {
|
||||||
sortDirection: sortDirections.DESCENDING,
|
sortDirection: sortDirections.DESCENDING,
|
||||||
items: [],
|
items: [],
|
||||||
pendingChanges: {},
|
pendingChanges: {},
|
||||||
|
sortPredicates: {
|
||||||
|
rating: function(item) {
|
||||||
|
return item.ratings.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
@ -73,6 +78,12 @@ export const defaultState = {
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: false
|
isVisible: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'rating',
|
||||||
|
label: 'Rating',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
label: 'Status',
|
label: 'Status',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue