Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listening now: allow users to vote on and add tags #2475

Merged
merged 49 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
34613a6
Listening now: Refactor util to get recording MBID
MonkeyDo Jul 18, 2022
8451cfe
Listening now: Refactor tag component
MonkeyDo Jul 18, 2022
2d6fee8
Listening now: Refactor tags container
MonkeyDo Jul 18, 2022
46b69f2
Listening now: Small refactor
MonkeyDo Jul 18, 2022
2c92bf4
Listening now: Improve "add tag" component
MonkeyDo Jul 18, 2022
1f0fb7e
Listening now: minor UI tweaks
MonkeyDo Jul 18, 2022
c55bdf3
Listening now: implement tag posting properly
MonkeyDo Jul 19, 2022
d02aafd
Add MusicBrainz auth token to front-end global context
MonkeyDo May 4, 2023
c9c00cf
Use MusicBrainz auth token in tag voting methods
MonkeyDo May 4, 2023
96ad323
Refactor React useCallback in TagComponent
MonkeyDo May 4, 2023
5990a96
Resolve React hooks linting warnings
MonkeyDo May 4, 2023
4c51e82
Improve MB tag auth
MonkeyDo May 5, 2023
1a09937
Clean up types
MonkeyDo May 5, 2023
b3804f5
Add new tags to list when user submits
MonkeyDo May 5, 2023
5bde3b1
Fix RG tag votes
MonkeyDo May 5, 2023
1cc7dec
Merge branch 'master' into listening-now-tags-2
MonkeyDo Jun 27, 2023
add3317
Fetch MusicBrainz genres and add to global context
MonkeyDo Jun 27, 2023
ad29c31
Use multiselect in tag submission component
MonkeyDo Jun 27, 2023
e74ef14
Move genre list fetching to Listening Now page wrapper
MonkeyDo Jun 27, 2023
05c6cf5
Send selected tag value to callback
MonkeyDo Jun 28, 2023
20ff5a0
Reimplement tags vote component
MonkeyDo Jun 30, 2023
da9bd2e
Tweak tags CSS
MonkeyDo Jun 30, 2023
9385a04
Move tag components into separate folder
MonkeyDo Jun 30, 2023
4bf12dd
Fix button padding
MonkeyDo Jul 3, 2023
32f8a99
Tagging: Support missing entity MBID
MonkeyDo Jul 3, 2023
9b2bab2
CSS classname fix
MonkeyDo Jul 3, 2023
d1525b7
Lint LESS files
MonkeyDo Jul 3, 2023
04ff0fe
Tweak wording on tags multiselect
MonkeyDo Jul 5, 2023
4d58a8b
CSS tweaks
MonkeyDo Jul 5, 2023
baea27c
Only display tag component if matching entity exists
MonkeyDo Jul 5, 2023
ea0a316
Avoid stale props in state in tags components
MonkeyDo Jul 5, 2023
89e1964
Merge branch 'master' into listening-now-tags-2
MonkeyDo Jul 5, 2023
7cde6d8
Bigger tags on mobile devices
MonkeyDo Jul 5, 2023
59b3682
Merge branch 'master' into listening-now-tags-2
MonkeyDo Jul 5, 2023
6be7e4c
Use key for TagsComponent to reset state
MonkeyDo Jul 6, 2023
e3ef15b
Fetch user's own tags on MB on entity change
MonkeyDo Jul 6, 2023
899aad9
Fix type issue
MonkeyDo Jul 6, 2023
c68a08d
Lint less file
MonkeyDo Jul 6, 2023
5784dd5
Remove extraneous APIService method
MonkeyDo Jul 6, 2023
ffb82a4
Filter and sort tags
MonkeyDo Jul 7, 2023
0ed322d
Add toast notifications container in page wrapper
MonkeyDo Jul 7, 2023
1277356
CSS tweaks
MonkeyDo Jul 7, 2023
b28fb33
Don't recreate tags on vote count change
MonkeyDo Jul 7, 2023
d895dc2
Small tweaks in React-ness
MonkeyDo Jul 7, 2023
99ab9a3
Fix user tag processing
MonkeyDo Jul 7, 2023
1cbe16d
Retain tag information in children
MonkeyDo Jul 7, 2023
37545c2
Merge branch 'master' into listening-now-tags-2
MonkeyDo Jul 7, 2023
b8f19e1
Merge branch 'master' into listening-now-tags-2
MonkeyDo Jul 25, 2023
d575d61
Update package lockfile
MonkeyDo Jul 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/css/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
@import "search-track.less";
@import "stats.less";
@import "new-navbar.less";
@import "tags.less";

@icon-font-path: "/static/fonts/";

Expand Down
43 changes: 2 additions & 41 deletions frontend/css/metadata-viewer.less
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
border-radius: @border-radius;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.1) inset;
background-color: @boxes-background-color;
min-height: 2.8em; // adapt to add-tag component height
align-items: center;

&.white {
background-color: white;
Expand Down Expand Up @@ -216,47 +218,6 @@
}
}

.tags-wrapper {
display: flex;
position: relative;
.tags {
flex: 1;
align-items: center;
// horizontal scroll overflow with hidden scrollbar and faded overlay
display: flex;
overflow-x: scroll;
&::-webkit-scrollbar {
// hidden scrollbar
display: none;
}
.badge {
flex: none;
color: currentcolor;
background-color: lightgrey;
}
.badge + .badge {
margin-left: 0.3em;
}
}
.add-tag {
flex: none;
// Faded overlay to hide extra tags in .tags
&:before {
content: "";
position: absolute;
width: 60px;
height: 80%;
right: 75px;
top: 10%;
background: linear-gradient(
to right,
transparent,
@boxes-background-color
);
pointer-events: none;
}
}
}
}
}
}
Expand Down
59 changes: 59 additions & 0 deletions frontend/css/tags.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@tagBorderRadius: 6px;
.tag {
flex: none;
color: currentcolor;
> * {
background-color: rgb(236, 235, 228);
display: inline-block;
padding: 3px 7px;
vertical-align: middle;
&:first-child {
border-top-left-radius: @tagBorderRadius;
border-bottom-left-radius: @tagBorderRadius;
}
&:last-child {
border-top-right-radius: @tagBorderRadius;
border-bottom-right-radius: @tagBorderRadius;
}
}
.tag-vote-button {
margin: 0;
border-left: 1px dashed #f7f7f7;
opacity: 0;
line-height: 1.5em;
vertical-align: baseline;
padding: 3px 0;
width: 0px;
transition-duration: 0.3s;
transition-property: padding, width, opacity;
transition-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1.28);
// Cancelling some .btn styling; we want the .btn class applied
// for interactivity reasons, but not all the CSS styling
margin: 0;
font-size: inherit;
line-height: inherit;
vertical-align: initial;

&.upvote:hover,
&.upvote.selected {
color: @btn-success-color;
background-color: lighten(@btn-success-bg, 20%);
}
&.downvote:hover,
&.downvote.selected,
&.delete:hover {
color: @btn-danger-color;
background-color: lighten(@btn-danger-bg, 20%);
}
}
&:hover > .tag-vote-button {
width: 2em;
opacity: 1;
}
&:not(:hover) > :first-child {
border-radius: @tagBorderRadius;
}
& + .tag {
margin-left: 0.3em;
}
}
110 changes: 74 additions & 36 deletions frontend/js/src/metadata-viewer/MetadataViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { faHeart, faHeartBroken } from "@fortawesome/free-solid-svg-icons";
import { faPauseCircle } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as tinycolor from "tinycolor2";
import { first, get, isEmpty, isNumber, isPlainObject, pick } from "lodash";
import TagsComponent from "./TagsComponent";
import { first, isEmpty, isNumber, isPlainObject, pick } from "lodash";
import TagsComponent from "../tags/TagsComponent";
import {
getArtistName,
getAverageRGBOfImage,
getRecordingMBID,
getTrackName,
} from "../utils/utils";
import GlobalAppContext from "../utils/GlobalAppContext";
Expand All @@ -23,6 +24,10 @@ const supportLinkTypes = [
"official homepage",
"purchase for download",
"purchase for mail-order",
"social network",
"patronage",
"crowdfunding",
"blog",
];

function OpenInMusicBrainzButton(props: {
Expand Down Expand Up @@ -54,9 +59,23 @@ function OpenInMusicBrainzButton(props: {
);
}

function getNowPlayingRecordingMBID(
recordingData?: MetadataLookup,
playingNow?: Listen
) {
if (!recordingData && !playingNow) {
return undefined;
}
return (
recordingData?.recording_mbid ?? getRecordingMBID(playingNow as Listen)
);
}

export default function MetadataViewer(props: MetadataViewerProps) {
const { recordingData, playingNow } = props;
const { APIService, currentUser } = React.useContext(GlobalAppContext);
const { getFeedbackForUserForRecordings, submitFeedback } = APIService;
const { auth_token, name: username } = currentUser;

const [currentListenFeedback, setCurrentListenFeedback] = React.useState(0);
const [expandedAccordion, setExpandedAccordion] = React.useState(1);
Expand All @@ -72,29 +91,30 @@ export default function MetadataViewer(props: MetadataViewerProps) {
const averageColor = getAverageRGBOfImage(albumArtRef?.current);
setAlbumArtColor(averageColor);
};
if (albumArtRef?.current) {
albumArtRef.current.addEventListener("load", setAverageColor);
const currentAlbumArtRef = albumArtRef.current;
if (currentAlbumArtRef) {
currentAlbumArtRef.addEventListener("load", setAverageColor);
}
return () => {
if (albumArtRef?.current) {
albumArtRef.current.removeEventListener("load", setAverageColor);
if (currentAlbumArtRef) {
currentAlbumArtRef.removeEventListener("load", setAverageColor);
}
};
}, [albumArtRef?.current, setAlbumArtColor]);
}, [setAlbumArtColor]);

React.useEffect(() => {
const getFeedbackPromise = async () => {
const recordingMBID =
recordingData?.recording_mbid ||
get(playingNow, "track_metadata.additional_info.recording_mbid");
const recordingMBID = getNowPlayingRecordingMBID(
recordingData,
playingNow
);
if (!recordingMBID) {
return;
}
try {
const feedbackObject = await APIService.getFeedbackForUserForRecordings(
currentUser.name,
[recordingMBID]
);
const feedbackObject = await getFeedbackForUserForRecordings(username, [
recordingMBID,
]);
if (feedbackObject?.feedback?.length) {
const feedback: any = first(feedbackObject.feedback);
setCurrentListenFeedback(feedback.score);
Expand All @@ -109,25 +129,21 @@ export default function MetadataViewer(props: MetadataViewerProps) {
}
};
getFeedbackPromise();
}, [recordingData, playingNow]);
}, [recordingData, playingNow, getFeedbackForUserForRecordings, username]);

const submitFeedback = React.useCallback(
const submitFeedbackCallback = React.useCallback(
async (score: ListenFeedBack) => {
if (currentUser?.auth_token) {
const recordingMBID =
recordingData?.recording_mbid ||
get(playingNow, "track_metadata.additional_info.recording_mbid");
if (auth_token) {
const recordingMBID = getNowPlayingRecordingMBID(
recordingData,
playingNow
);
if (!recordingMBID) {
return;
}
try {
setCurrentListenFeedback(score);
await APIService.submitFeedback(
currentUser.auth_token,
score,
undefined,
recordingMBID
);
await submitFeedback(auth_token, score, undefined, recordingMBID);
} catch (error) {
// Revert the feedback UI in case of failure
setCurrentListenFeedback(0);
Expand All @@ -136,7 +152,13 @@ export default function MetadataViewer(props: MetadataViewerProps) {
}
}
},
[recordingData, playingNow, setCurrentListenFeedback]
[
recordingData,
playingNow,
setCurrentListenFeedback,
submitFeedback,
auth_token,
]
);

const adjustedAlbumColor = tinycolor.fromRatio(albumArtColor);
Expand All @@ -153,12 +175,13 @@ export default function MetadataViewer(props: MetadataViewerProps) {

// Default to empty object
const { metadata } = recordingData ?? {};

const recordingMBID = getNowPlayingRecordingMBID(recordingData, playingNow);
const artistMBID = first(recordingData?.artist_mbids);
const userSubmittedReleaseMBID =
playingNow?.track_metadata?.additional_info?.release_mbid;
const CAAReleaseMBID = metadata?.release?.caa_release_mbid;
const CAAID = metadata?.release?.caa_id;

let coverArtSrc = "/static/img/cover-art-placeholder.jpg";

// try fetching cover art using user submitted release mbid first
Expand Down Expand Up @@ -270,6 +293,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
<h4 className="panel-title">
<div className="recordingheader">
<div className="name strong">{trackName}</div>
&nbsp;<small>Track</small>
<div className="date">
{isNumber(duration) && millisecondsToStr(duration)}
</div>
Expand All @@ -286,7 +310,11 @@ export default function MetadataViewer(props: MetadataViewerProps) {
aria-labelledby="headingOne"
>
<div className="panel-body">
<TagsComponent tags={metadata?.tag?.recording} />
<TagsComponent
tags={metadata?.tag?.recording}
entityType="recording"
entityMBID={recordingMBID}
/>
{/* <div className="ratings content-box" /> */}
{Boolean(flattenedRecRels?.length) && (
<div className="white content-box">
Expand Down Expand Up @@ -331,7 +359,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
)}
<OpenInMusicBrainzButton
entityType="recording"
entityMBID={recordingData?.recording_mbid}
entityMBID={recordingMBID}
/>
</div>
</div>
Expand Down Expand Up @@ -359,6 +387,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
<div className="name strong">
{recordingData?.release_name}
</div>
&nbsp;<small>Album</small>
<div className="date">{metadata?.release?.year}</div>
<div className="caret" />
</div>
Expand All @@ -373,7 +402,11 @@ export default function MetadataViewer(props: MetadataViewerProps) {
aria-labelledby="headingTwo"
>
<div className="panel-body">
<TagsComponent tags={metadata?.tag?.release_group} />
<TagsComponent
MonkeyDo marked this conversation as resolved.
Show resolved Hide resolved
tags={metadata?.tag?.release_group}
entityType="release-group"
entityMBID={metadata?.release?.release_group_mbid}
/>
<OpenInMusicBrainzButton
entityType="release"
entityMBID={releaseMBID}
Expand Down Expand Up @@ -401,6 +434,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
<h4 className="panel-title">
<div className="artistheader">
<div className="name strong">{artistName}</div>
&nbsp;<small>Artist</small>
<div className="date">{artist?.begin_year}</div>
<div className="caret" />
</div>
Expand All @@ -415,7 +449,11 @@ export default function MetadataViewer(props: MetadataViewerProps) {
aria-labelledby="headingThree"
>
<div className="panel-body">
<TagsComponent tags={metadata?.tag?.artist} />
<TagsComponent
tags={metadata?.tag?.artist}
entityType="artist"
entityMBID={artistMBID}
/>
{/* <div className="ratings content-box" /> */}
{(artist?.begin_year || artist?.area) && (
<div>
Expand Down Expand Up @@ -452,8 +490,8 @@ export default function MetadataViewer(props: MetadataViewerProps) {
>
<a
href={
recordingData?.recording_mbid
? `${musicBrainzURLRoot}recording/${recordingData?.recording_mbid}`
recordingMBID
? `${musicBrainzURLRoot}recording/${recordingMBID}`
: undefined
}
target="_blank"
Expand All @@ -480,7 +518,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
<button
className="btn-transparent"
onClick={() =>
submitFeedback(currentListenFeedback === 1 ? 0 : 1)
submitFeedbackCallback(currentListenFeedback === 1 ? 0 : 1)
}
type="button"
>
Expand All @@ -494,7 +532,7 @@ export default function MetadataViewer(props: MetadataViewerProps) {
<button
className="btn-transparent"
onClick={() =>
submitFeedback(currentListenFeedback === -1 ? 0 : -1)
submitFeedbackCallback(currentListenFeedback === -1 ? 0 : -1)
}
type="button"
>
Expand Down
Loading