Skip to content

Commit

Permalink
Merge pull request #2990 from metabrainz/stats-explanation-modal
Browse files Browse the repository at this point in the history
Add a modal explaining how stats are calculated to user stats page
  • Loading branch information
anshg1214 authored Oct 1, 2024
2 parents 085db4e + 98a9bf5 commit 130c5c6
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 11 deletions.
104 changes: 104 additions & 0 deletions frontend/js/src/common/stats/StatsExplanationsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as React from "react";
import { get as _get } from "lodash";
import NiceModal, { useModal } from "@ebay/nice-modal-react";

/** A note about this modal:
* We use Bootstrap 3 modals, which work with jQuery and data- attributes
* In order to show the modal properly, including backdrop and visibility,
* you'll need dataToggle="modal" and dataTarget="#StatsExplanationsModal"
* on the buttons that open this modal as well as data-dismiss="modal"
* on the buttons that close the modal. Modals won't work (be visible) without it
* until we move to Bootstrap 5 / Bootstrap React which don't require those attributes.
*/

export default NiceModal.create(() => {
const modal = useModal();

const closeModal = () => {
modal.hide();
document?.body?.classList?.remove("modal-open");
setTimeout(modal.remove, 200);
};

return (
<div
className={`modal fade ${modal.visible ? "in" : ""}`}
id="StatsExplanationsModal"
tabIndex={-1}
role="dialog"
aria-labelledby="StatsExplanationsModalLabel"
data-backdrop="static"
>
<div className="modal-dialog" role="document">
<form className="modal-content">
<div className="modal-header">
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={closeModal}
>
<span aria-hidden="true">&times;</span>
</button>
<h4 className="modal-title" id="StatsExplanationsModalLabel">
How and when are statistics calculated?
</h4>
</div>
<div className="modal-body">
We calculate our statistics exclusively from listens submitted
directly to ListenBrainz by you, our users.{" "}
<b>We do not rely on third-party statistics.</b>
<br />
This applies to all our popularity, similarity, listen counts and
user similarity statistics.
<br />
For full transparency, all this data is{" "}
<a
target="_blank"
href="https://metabrainz.org/datasets"
rel="noreferrer"
>
public and available
</a>{" "}
for you to explore.
<br />
<br />
Artist, album and track information comes from{" "}
<a
target="_blank"
href="https://musicbrainz.org/doc/About"
rel="noreferrer"
>
the MusicBrainz project.
</a>
<br />
Stats are automatically calculated at regular intervals; for more
information please{" "}
<a
href="https://listenbrainz.readthedocs.io/en/latest/general/data-update-intervals.html"
target="_blank"
rel="noopener noreferrer"
>
see this page
</a>
.
<br />
However if you encounter an issue please&nbsp;
<a href="mailto:[email protected]">contact us</a>.
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-default"
data-dismiss="modal"
onClick={closeModal}
>
Close
</button>
</div>
</form>
</div>
</div>
);
});
33 changes: 22 additions & 11 deletions frontend/js/src/user/stats/UserReports.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useLoaderData, useNavigate, useSearchParams } from "react-router-dom";
import { Helmet } from "react-helmet";

import Tooltip from "react-tooltip";
import NiceModal from "@ebay/nice-modal-react";
import Pill from "../../components/Pill";
import UserListeningActivity from "./components/UserListeningActivity";
import UserTopEntity from "./components/UserTopEntity";
import UserDailyActivity from "./components/UserDailyActivity";
import UserArtistMap from "./components/UserArtistMap";
import { getAllStatRanges, isInvalidStatRange } from "./utils";
import GlobalAppContext from "../../utils/GlobalAppContext";
import StatsExplanationsModal from "../../common/stats/StatsExplanationsModal";

export type UserReportsProps = {
user?: ListenBrainzUser;
Expand Down Expand Up @@ -61,6 +64,21 @@ export default function UserReports() {
const userStatsTitle =
user?.name === currentUser?.name ? "Your" : `${userOrLoggedInUser}'s`;

const statsExplanationModalButton = (
<button
type="button"
className="btn btn-link"
data-toggle="modal"
data-target="#StatsExplanationsModal"
onClick={() => {
NiceModal.show(StatsExplanationsModal);
}}
>
<FontAwesomeIcon icon={faInfoCircle} />
&nbsp; How and when are statistics calculated?
</button>
);

return (
<div data-testid="User Reports">
<Helmet>
Expand Down Expand Up @@ -110,21 +128,12 @@ export default function UserReports() {
</button>
</div>
</div>
<small>
<FontAwesomeIcon icon={faInfoCircle} />
&nbsp;
<a
href="https://listenbrainz.readthedocs.io/en/latest/general/data-update-intervals.html"
target="_blank"
rel="noopener noreferrer"
>
How often are my stats updated?
</a>
</small>
<section id="listening-activity">
{statsExplanationModalButton}
<UserListeningActivity range={range} user={user} />
</section>
<section id="top-entity">
{statsExplanationModalButton}
<div className="row">
<div className="col-md-4">
<UserTopEntity
Expand Down Expand Up @@ -154,10 +163,12 @@ export default function UserReports() {
</section>
{user && (
<section id="daily-activity">
{statsExplanationModalButton}
<UserDailyActivity range={range} user={user} />
</section>
)}
<section id="artist-origin">
{statsExplanationModalButton}
<UserArtistMap range={range} user={user} />
</section>
</div>
Expand Down

0 comments on commit 130c5c6

Please sign in to comment.