Skip to content

Commit

Permalink
Merge pull request #3079 from metabrainz/fix-unlinked-listens-modal
Browse files Browse the repository at this point in the history
Fix multi-track matching modal
  • Loading branch information
MonkeyDo authored Dec 16, 2024
2 parents 2aa41e6 + d7402ba commit d594c6c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 50 deletions.
74 changes: 36 additions & 38 deletions frontend/js/src/settings/link-listens/LinkListens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,40 @@ export default function LinkListensPage() {
}
};

const openMultiTrackMappingModal = React.useCallback(
async (group: UnlinkedListens[], releaseName: string | null) => {
const matchedTracks: MatchingTracksResults = await NiceModal.show(
MultiTrackMBIDMappingModal,
{
unlinkedListens: group,
releaseName,
}
);
// Remove successfully matched items from the page
setUnlinkedListens((prevValue) =>
prevValue.filter((md) => !matchedTracks[md.recording_msid])
);
Object.entries(matchedTracks).forEach(([recordingMsid, track]) => {
// For deleting items from the BrainzPlayer queue, we need to use
// the metadata it was created from rather than the matched track metadata
const itemBeforeMatching = group.find(
({ recording_msid }) => recordingMsid === recording_msid
);
if (itemBeforeMatching) {
// Remove the listen from the BrainzPlayer queue
dispatch({
type: "REMOVE_TRACK_FROM_AMBIENT_QUEUE",
data: {
track: unlinkedListenDataToListen(itemBeforeMatching, user),
index: -1,
},
});
}
});
},
[dispatch, user]
);

// Effects
React.useEffect(() => {
// Set the ?page search param in URL on startup if not set, as well as
Expand Down Expand Up @@ -257,44 +291,8 @@ export default function LinkListensPage() {
className="btn btn-link btn-icon color-orange"
style={{ padding: "0", height: "initial" }}
type="button"
onClick={(e) => {
NiceModal.show<MatchingTracksResults, any>(
MultiTrackMBIDMappingModal,
{
unlinkedListens: group,
releaseName,
}
).then((matchedTracks) => {
Object.entries(matchedTracks).forEach(
([recordingMsid, track]) => {
// For deleting items from the BrainzPlayer queue, we need to use
// the metadata it was created from rather than the matched track metadata
const itemBeforeMatching = group.find(
({ recording_msid }) =>
recordingMsid === recording_msid
);
if (itemBeforeMatching) {
// Remove the listen from the BrainzPlayer queue
dispatch({
type: "REMOVE_TRACK_FROM_AMBIENT_QUEUE",
data: {
track: unlinkedListenDataToListen(
itemBeforeMatching,
user
),
index: -1,
},
});
}
}
);
// Remove successfully matched items from the page
setUnlinkedListens((prevValue) =>
prevValue.filter(
(md) => !matchedTracks[md.recording_msid]
)
);
});
onClick={() => {
openMultiTrackMappingModal(group, releaseName);
}}
data-toggle="modal"
data-target="#MultiTrackMBIDMappingModal"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ export type MatchingTracksResults = {

export type MultiTrackMBIDMappingModalProps = {
releaseName: string | null;
missingData: Array<UnlinkedListens>;
unlinkedListens: Array<UnlinkedListens>;
};

// https://lucene.apache.org/core/7_7_2/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters
const lucineSpecialCharRegex = /[+\-!(){}[\]^"~*?:\\/]|(?:&{2})|(?:\|{2})/gm;

export default NiceModal.create(
({ missingData, releaseName }: MultiTrackMBIDMappingModalProps) => {
({ unlinkedListens, releaseName }: MultiTrackMBIDMappingModalProps) => {
const modal = useModal();
const { APIService, currentUser } = React.useContext(GlobalAppContext);
const { lookupMBRelease, submitMBIDMapping } = APIService;
Expand Down Expand Up @@ -267,15 +267,15 @@ export default NiceModal.create(
keys: ["title", "artist-credit.name"],
});
const newMatchingTracks: MatchingTracksResults = {};
missingData.forEach((missingDataItem) => {
let stringToSearch = missingDataItem.recording_name;
unlinkedListens.forEach((unlinkedListensItem) => {
let stringToSearch = unlinkedListensItem.recording_name;
if (includeArtistNameMatch) {
stringToSearch += ` ${missingDataItem.artist_name}`;
stringToSearch += ` ${unlinkedListensItem.artist_name}`;
}
const matches = fuzzysearch.search(stringToSearch);
if (matches[0]) {
// We have a match
newMatchingTracks[missingDataItem.recording_msid] = {
newMatchingTracks[unlinkedListensItem.recording_msid] = {
...matches[0].item,
searchString: stringToSearch,
};
Expand All @@ -285,22 +285,22 @@ export default NiceModal.create(
}
});
setMatchingTracks(newMatchingTracks);
}, [includeArtistNameMatch, missingData, potentialTracks]);
}, [includeArtistNameMatch, unlinkedListens, potentialTracks]);

const removeItemFromMatches = (recordingMSID: string) => {
setMatchingTracks((currentMatchingTracks) =>
omit(currentMatchingTracks, recordingMSID)
);
};

if (!missingData) {
if (!unlinkedListens) {
return null;
}
const matchingTracksEntries =
matchingTracks && Object.entries(matchingTracks);
const hasMatches = Boolean(matchingTracksEntries?.length);
const unmatchedItems =
missingData.filter((md) => !matchingTracks?.[md.recording_msid]) ?? [];
unlinkedListens.filter((md) => !matchingTracks?.[md.recording_msid]) ?? [];

// We may need to escape or replace the Lucene search special characters
// + - && || ! ( ) { } [ ] ^ " ~ * ? : \ / as described in
Expand All @@ -309,20 +309,20 @@ export default NiceModal.create(
lucineSpecialCharRegex,
"\\$&"
);
const escapedArtistName = missingData[0]?.artist_name?.replace(
const escapedArtistName = unlinkedListens[0]?.artist_name?.replace(
lucineSpecialCharRegex,
"\\$&"
);
let searchTerm = escapeSpecialCharacters ? escapedReleaseName : releaseName;
if (
includeArtistNameSearch &&
(missingData[0]?.artist_name ||
(unlinkedListens[0]?.artist_name ||
(escapeSpecialCharacters && escapedArtistName))
) {
searchTerm += ` artist:(${
escapeSpecialCharacters
? escapedArtistName
: missingData[0]?.artist_name
: unlinkedListens[0]?.artist_name
})`;
}

Expand Down

0 comments on commit d594c6c

Please sign in to comment.