Skip to content

Commit

Permalink
LB-640: Add "Listening Activity" statistics (Frontend) (#935)
Browse files Browse the repository at this point in the history
* Create template

* Create basic layout for desktop

* Add pill component

* Add code for fetching data

* Update Card component

* Change to functional component

* Change to bar graph

* Change from dual card to single card layout

* Add legend

* Make page responsive

* Add error handling

* stats -> reports

* Fix minor issues and improve loader

* Improve error logging

* Fix typo

* Add tests for "Pill".

* Add tests for "Card"

* Add snapshot tests for "BarDualTone"

* Add tests for "UserReports"

* Add tests for "APIService"

* Add snapshot tests for "UserListeningActivity"

* Add tests for "userListeningActivity"
  • Loading branch information
ishaanshah committed Jul 14, 2020
1 parent 7c744e3 commit 8331566
Show file tree
Hide file tree
Showing 39 changed files with 5,875 additions and 337 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = {
"react/prop-tyes": "off",
"react/jsx-filename-extension": "off",
"react/jsx-props-no-spreading": "off",
"react/no-did-update-set-state": "off",
"import/extensions": "off",
"no-unused-vars": "off",
camelcase: "warn",
Expand Down
24 changes: 24 additions & 0 deletions listenbrainz/webserver/static/css/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,27 @@ h2.page-title { @media (max-width: @grid-float-breakpoint) { margin-top: 0px; }
}
}
}

.mt-5 {
margin-top: 5px
}

.mt-10 {
margin-top: 10px
}

.mt-15 {
margin-top: 15px
}

.mb-5 {
margin-bottom: 5px
}

.mb-10 {
margin-bottom: 10px
}

.mb-15 {
margin-bottom: 15px
}
49 changes: 48 additions & 1 deletion listenbrainz/webserver/static/js/src/APIService.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import APIService from "./APIService";
import APIError from "./APIError";

const apiService = new APIService("foobar");

Expand Down Expand Up @@ -238,6 +237,54 @@ describe("getUserEntity", () => {
});
});

describe("getUserListeningActivity", () => {
beforeEach(() => {
// Mock function for fetch
window.fetch = jest.fn().mockImplementation(() => {
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({ latest_import: "0" }),
});
});
});

it("calls fetch correctly when optional parameters are passed", async () => {
await apiService.getUserListeningActivity("foobar", "week");
expect(window.fetch).toHaveBeenCalledWith(
"foobar/1/stats/user/foobar/listening-activity?range=week"
);
});

it("calls fetch correctly when optional parameters are not passed", async () => {
await apiService.getUserListeningActivity("foobar");
expect(window.fetch).toHaveBeenCalledWith(
"foobar/1/stats/user/foobar/listening-activity?range=all_time"
);
});

it("throws appropriate error if statistics haven't been calculated", async () => {
window.fetch = jest.fn().mockImplementationOnce(() => {
return Promise.resolve({
ok: true,
status: 204,
statusText: "NO CONTENT",
});
});

await expect(apiService.getUserListeningActivity("foobar")).rejects.toThrow(
Error("HTTP Error NO CONTENT")
);
});

it("calls checkStatus once", async () => {
apiService.checkStatus = jest.fn();

await apiService.getUserListeningActivity("foobar");
expect(apiService.checkStatus).toHaveBeenCalledTimes(1);
});
});

describe("getLatestImport", () => {
beforeEach(() => {
// Mock function for fetch
Expand Down
19 changes: 18 additions & 1 deletion listenbrainz/webserver/static/js/src/APIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export default class APIService {
getUserEntity = async (
userName: string,
entity: Entity,
range: UserEntityAPIRange = "all_time",
range: UserStatsAPIRange = "all_time",
offset: number = 0,
count?: number
): Promise<
Expand All @@ -205,6 +205,23 @@ export default class APIService {
return data;
};

getUserListeningActivity = async (
userName: string,
range: UserStatsAPIRange = "all_time"
): Promise<UserListeningActivityResponse> => {
const url = `${this.APIBaseURI}/stats/user/${userName}/listening-activity?range=${range}`;
const response = await fetch(url);
this.checkStatus(response);
if (response.status === 204) {
const error = new APIError(`HTTP Error ${response.statusText}`);
error.status = response.statusText;
error.response = response;
throw error;
}
const data = response.json();
return data;
};

checkStatus = (response: Response): void => {
if (response.status >= 200 && response.status < 300) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"payload": {
"from_ts": 1009843200,
"last_updated": 1593539726,
"listening_activity": [
{
"from_ts": 1483228800,
"listen_count": 96,
"time_range": "2017",
"to_ts": 1514764799
},
{
"from_ts": 1514764800,
"listen_count": 169,
"time_range": "2018",
"to_ts": 1546300799
},
{
"from_ts": 1577836800,
"listen_count": 3580,
"time_range": "2020",
"to_ts": 1609459199
}
],
"range": "all_time",
"to_ts": 1592611115,
"user_id": "ishaanshah"
}
}
Loading

0 comments on commit 8331566

Please sign in to comment.