Skip to content

Commit

Permalink
Merge pull request #2985 from metabrainz/ansh/sort-rg-artist-page
Browse files Browse the repository at this point in the history
LB-1626: Sort Release Groups on Artist Page
  • Loading branch information
MonkeyDo authored Oct 10, 2024
2 parents 3ec6bbd + 923579c commit 39282e4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 30 deletions.
3 changes: 3 additions & 0 deletions frontend/css/entity-pages.less
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@
justify-items: center;
grid-template-columns: repeat(auto-fill, minmax(190px, max-content));
grid-template-rows: repeat(2, 1fr);
&.single-row {
grid-template-rows: repeat(1, 1fr);
}
.cover-art {
aspect-ratio: 1;
background: lightgrey;
Expand Down
101 changes: 84 additions & 17 deletions frontend/js/src/artist/ArtistPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
faPlayCircle,
faUserAstronaut,
} from "@fortawesome/free-solid-svg-icons";
import { chain, isEmpty, isUndefined, partition, sortBy } from "lodash";
import { chain, isEmpty, isUndefined, orderBy, groupBy, sortBy } from "lodash";
import { sanitize } from "dompurify";
import {
Link,
Expand All @@ -19,7 +19,7 @@ import {
import { Helmet } from "react-helmet";
import { useQuery } from "@tanstack/react-query";
import NiceModal from "@ebay/nice-modal-react";
import GlobalAppContext from "../utils/GlobalAppContext";
import { faCalendar } from "@fortawesome/free-regular-svg-icons";
import { getReviewEventContent } from "../utils/utils";
import TagsComponent from "../tags/TagsComponent";
import ListenCard from "../common/listens/ListenCard";
Expand All @@ -39,11 +39,43 @@ import { RouteQuery } from "../utils/Loader";
import { useBrainzPlayerDispatch } from "../common/brainzplayer/BrainzPlayerContext";
import SimilarArtistComponent from "../explore/music-neighborhood/components/SimilarArtist";
import CBReviewModal from "../cb-review/CBReviewModal";
import Pill from "../components/Pill";

function SortingButtons({
sort,
setSort,
}: {
sort: "release_date" | "total_listen_count";
setSort: (sort: "release_date" | "total_listen_count") => void;
}): JSX.Element {
return (
<div className="flex" role="group" aria-label="Sort by">
<Pill
type="secondary"
active={sort === "release_date"}
onClick={() => setSort("release_date")}
>
<FontAwesomeIcon icon={faCalendar} />
</Pill>
<Pill
type="secondary"
active={sort === "total_listen_count"}
onClick={() => setSort("total_listen_count")}
>
<FontAwesomeIcon icon={faHeadphones} />
</Pill>
</div>
);
}

interface ReleaseGroupWithSecondaryTypes extends ReleaseGroup {
secondary_types: string[];
}

export type ArtistPageProps = {
popularRecordings: PopularRecording[];
artist: MusicBrainzArtist;
releaseGroups: ReleaseGroup[];
releaseGroups: ReleaseGroupWithSecondaryTypes[];
similarArtists: {
artists: SimilarArtist[];
topReleaseGroupColor: ReleaseColor | undefined;
Expand All @@ -53,9 +85,10 @@ export type ArtistPageProps = {
coverArt?: string;
};

const COVER_ART_SINGLE_ROW_COUNT = 8;

export default function ArtistPage(): JSX.Element {
const _ = useLoaderData();
const { APIService } = React.useContext(GlobalAppContext);
const location = useLocation();
const params = useParams() as { artistMBID: string };
const { artistMBID } = params;
Expand Down Expand Up @@ -84,11 +117,44 @@ export default function ArtistPage(): JSX.Element {
WikipediaExtract
>();

const [albumsByThisArtist, alsoAppearsOn] = partition(
const [sort, setSort] = React.useState<"release_date" | "total_listen_count">(
"release_date"
);

const rgGroups = groupBy(
releaseGroups,
(rg) => rg.artists[0].artist_mbid === artist?.artist_mbid
(rg) =>
(rg.type ?? "Other") +
(rg.secondary_types?.[0] ? ` + ${rg.secondary_types?.[0]}` : "")
);

const sortReleaseGroups = (
releaseGroupsInput: ReleaseGroupWithSecondaryTypes[]
) =>
orderBy(
releaseGroupsInput,
[
sort === "release_date" ? (rg) => rg.date || "" : "total_listen_count",
sort === "release_date" ? "total_listen_count" : (rg) => rg.date || "",
"name",
],
["desc", "desc", "asc"]
);

const typeOrder = ["Album", "Single", "EP", "Broadcast", "Other"];
const sortedRgGroupsKeys = sortBy(Object.keys(rgGroups), [
(type) => typeOrder.indexOf(type.split(" + ")[0]),
(type) => type.split(" + ")[1] ?? "",
]);

const groupedReleaseGroups: Record<
string,
ReleaseGroupWithSecondaryTypes[]
> = {};
sortedRgGroupsKeys.forEach((type) => {
groupedReleaseGroups[type] = sortReleaseGroups(rgGroups[type]);
});

React.useEffect(() => {
async function fetchReviews() {
try {
Expand Down Expand Up @@ -407,20 +473,21 @@ export default function ArtistPage(): JSX.Element {
</div>
)}
</div>
<div className="albums full-width scroll-start">
<h3 className="header-with-line">Albums</h3>
<div className="cover-art-container dragscroll">
{albumsByThisArtist.map(getReleaseCard)}
</div>
</div>
{Boolean(alsoAppearsOn?.length) && (
{Object.entries(groupedReleaseGroups).map(([type, rgGroup]) => (
<div className="albums full-width scroll-start">
<h3 className="header-with-line">Also appears on</h3>
<div className="cover-art-container dragscroll">
{alsoAppearsOn.map(getReleaseCard)}
<div className="listen-header">
<h3 className="header-with-line">{type}</h3>
<SortingButtons sort={sort} setSort={setSort} />
</div>
<div
className={`cover-art-container dragscroll ${
rgGroup.length <= COVER_ART_SINGLE_ROW_COUNT ? "single-row" : ""
}`}
>
{rgGroup.map(getReleaseCard)}
</div>
</div>
)}
))}
</div>

{similarArtists && similarArtists.artists.length > 0 ? (
Expand Down
13 changes: 0 additions & 13 deletions listenbrainz/webserver/views/entity_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,6 @@
release_group_bp = Blueprint("release-group", __name__)


def get_release_group_sort_key(release_group):
""" Return a tuple that sorts release group by total_listen_count and then by date """
release_date = release_group.get("date")
if release_date is None:
release_date = datetime.min
else:
release_date = datetime.strptime(release_date, "%Y-%m-%d")

return release_group["total_listen_count"] or 0, release_date


def get_cover_art_for_artist(release_groups):
""" Get the cover art for an artist using a list of their release groups """
covers = []
Expand Down Expand Up @@ -170,8 +159,6 @@ def artist_entity(artist_mbid):
release_group["total_user_count"] = pop["total_user_count"]
release_groups.append(release_group)

release_groups.sort(key=get_release_group_sort_key, reverse=True)

listening_stats = get_entity_listener(db_conn, "artists", artist_mbid, "all_time")
if listening_stats is None:
listening_stats = {
Expand Down

0 comments on commit 39282e4

Please sign in to comment.