Skip to content

Commit

Permalink
Merge pull request #4 from hurry-harry/hf-create-stats-page
Browse files Browse the repository at this point in the history
Start with Stats page
  • Loading branch information
hurry-harry authored Jan 23, 2024
2 parents 23d6a32 + 4bf0861 commit b8d3d96
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 36 deletions.
6 changes: 6 additions & 0 deletions src/app/_shared/models/home-card.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface HomeCard {
imageSource: string;
title: string;
description: string;
url: string;
}
14 changes: 14 additions & 0 deletions src/app/_shared/models/spotify-shared.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface ExternalUrls {
spotify: string;
}

export interface Followers {
href: string; // will always be null, Spotify Web API currently does not support it
total: number;
}

export interface Image {
url: string;
height: number; // in pixels
width: number; // in pixels
}
17 changes: 2 additions & 15 deletions src/app/_shared/models/user-profile-response.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ExternalUrls, Followers, Image } from "./spotify-shared.model";

export interface UserProfileResponse {
country: string;
display_name: string;
Expand All @@ -17,18 +19,3 @@ export interface ExplicitContent {
filter_enabled: boolean;
filter_locked: boolean;
}

export interface ExternalUrls {
spotify: string;
}

export interface Followers {
href: string; // will always be null, Spotify Web API currently does not support it
total: number;
}

export interface Image {
url: string;
height: number; // in pixels
width: number; // in pixels
}
91 changes: 91 additions & 0 deletions src/app/_shared/models/user-top-items-response.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ExternalUrls, Followers, Image } from "./spotify-shared.model";

export interface UserTopItems {
href: string;
limit: number;
offset: number;
total: number;
next: string;
previous: string;
items: Artist[] | Track[];
}

export interface Artist {
external_urls: ExternalUrls;
followers: Followers;
genres: string[];
href: string;
id: string;
images: Image[];
name: string;
popularity: number;
type: string;
uri: string;
}

export interface Track {
album: Album;
artists: Artist[];
available_markets: string[];
disc_number: number;
duration_ms: number;
explicit: boolean;
external_ids: ExternalIds;
external_urls: ExternalUrls;
href: string;
id: string;
is_playable: boolean;
linked_from: LinkedFrom;
restrictions: Restriction;
name: string;
popularity: number;
preview_url: string;
track_number: number;
type: string;
uri: string;
is_local: boolean;
}

export interface Album {
album_type: string;
total_tracks: number;
available_markets: string[];
external_urls: ExternalUrls;
href: string;
id: string;
images: Image[];
name: string;
release_date: string;
release_date_precision: string;
restrictions: Restriction;
type: string;
uri: string;
artists: SimplifiedArtist[];
}

export interface Restriction {
reason: string;
}

export interface SimplifiedArtist {
external_urls: ExternalUrls;
href: string;
id: string;
name: string;
type: string;
uri: string;
}

export interface ExternalIds {
isrc: string;
ean: string;
upc: string;
}

export interface LinkedFrom {
external_urls: ExternalUrls;
href: string;
id: string;
type: string;
uri: string;
}
11 changes: 9 additions & 2 deletions src/app/_shared/services/spotify.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import { Injectable } from "@angular/core";
import { UserProfileResponse } from "../models/user-profile-response.model";
import { Observable } from "rxjs";
import { HttpClient } from "@angular/common/http";
import { SPOTIFY_MY_PROFILE } from "../constants/spotify-url.constants";
import { SPOTIFY_MY_PROFILE, SPOTIFY_MY_TOP_ITEMS } from "../constants/spotify-url.constants";
import { UserTopItems } from "../models/user-top-items-response.model";

@Injectable({ providedIn: 'root' })
export class SpotifyService {
constructor(private http: HttpClient) { }

getUserProfile(authToken: string): Observable<UserProfileResponse> {
return this.http.get<UserProfileResponse>(SPOTIFY_MY_PROFILE, { headers: { Authorization: 'Bearer ' + authToken } });
return this.http.get<UserProfileResponse>(SPOTIFY_MY_PROFILE, { headers: { Authorization: "Bearer " + authToken } });
}

getTopItems(authToken: string, timeRange: string, offset: number, topItem: string): Observable<UserTopItems> {
const url = `${SPOTIFY_MY_TOP_ITEMS}/${topItem}?time_range=${timeRange}&limit=50&offset=${offset}`;

return this.http.get<UserTopItems>(url, { headers: { Authorization: "Bearer " + authToken } })
}
}
1 change: 1 addition & 0 deletions src/app/_shared/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class UserService {
private user: UserProfileResponse = {} as UserProfileResponse;

userSignal: WritableSignal<UserProfileResponse> = signal(this.user);
authTokenSignal: WritableSignal<string> = signal(this.authToken);

constructor(
private http: HttpClient) { }
Expand Down
1 change: 1 addition & 0 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
<app-nav-bar></app-nav-bar>
<router-outlet></router-outlet>
5 changes: 3 additions & 2 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router, RouterOutlet } from '@angular/router';
import { UserService } from './_shared/services/user.service';
import { NavBarComponent } from './components/nav-bar/nav-bar.component';

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
imports: [CommonModule, RouterOutlet, NavBarComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
Expand Down Expand Up @@ -34,7 +35,7 @@ export class AppComponent implements OnInit {
if (this.urlParams == 'authorized=true') {
this.authToken = url.split('#')[1];
sessionStorage.setItem('authToken', this.authToken);
this.userService.setAuthToken(this.authToken);
this.userService.authTokenSignal.set(this.authToken);
this.router.navigate(['./home']);
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { HomeComponent } from './components/home/home.component';
import { AuthorizedComponent } from './components/authorized/authorized.component';
import { StatsComponent } from './components/stats/stats.component';

export const routes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'home', component: HomeComponent },
{ path: 'authorized', component: AuthorizedComponent },

{ path: '**', redirectTo: '' }
{ path: "", component: LoginComponent },
{ path: "home", component: HomeComponent },
{ path: "authorized", component: AuthorizedComponent },
{ path: "stats", component: StatsComponent },
{ path: "**", redirectTo: "" }
];
7 changes: 4 additions & 3 deletions src/app/components/home-card/home-card.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div class="card">
<img class="image" src="/assets/headphones.png">
<h2>Play Heardle</h2>
<div class="card" (click)="navigateTo()">
<img class="image card__item" src="{{ card.imageSource }}">
<h2 class="card__item">{{ card.title }}</h2>
<p class="card__description">{{ card.description }}</p>
</div>
11 changes: 9 additions & 2 deletions src/app/components/home-card/home-card.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@
border-radius: 25px;
padding: 0.5rem;
margin: 1rem;
display: flex;
flex-wrap: wrap;

&__title {

&__item {
padding: 1.75rem 0.5rem 0rem 0.5rem;
}

&__description {
margin: -2rem 0.75rem 0rem 0.75rem;
font-weight: 600;
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/app/components/home-card/home-card.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Component } from '@angular/core';
import { Component, Input } from '@angular/core';
import { HomeCard } from '../../_shared/models/home-card.model';
import { Router } from '@angular/router';

@Component({
selector: 'app-home-card',
Expand All @@ -8,5 +10,11 @@ import { Component } from '@angular/core';
styleUrl: './home-card.component.scss'
})
export class HomeCardComponent {
@Input() card!: HomeCard;

constructor(private router: Router) {}

navigateTo(): void {
this.router.navigate([this.card.url]);
}
}
5 changes: 2 additions & 3 deletions src/app/components/home/home.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<app-nav-bar></app-nav-bar>
<div class="card-container">
<app-home-card></app-home-card>
<app-home-card></app-home-card>
<app-home-card [card]="playCard"></app-home-card>
<app-home-card [card]="statsCard"></app-home-card>
</div>
1 change: 1 addition & 0 deletions src/app/components/home/home.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
flex-direction: row;
align-content: center;
justify-content: center;
padding-top: 3rem;
}
23 changes: 20 additions & 3 deletions src/app/components/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SpotifyService } from '../../_shared/services/spotify.service';
import { UserProfileResponse } from '../../_shared/models/user-profile-response.model';
import { NavBarComponent } from '../nav-bar/nav-bar.component';
import { HomeCardComponent } from '../home-card/home-card.component';
import { HomeCard } from '../../_shared/models/home-card.model';

@Component({
selector: 'app-home',
Expand All @@ -13,14 +14,30 @@ import { HomeCardComponent } from '../home-card/home-card.component';
styleUrl: './home.component.scss'
})
export class HomeComponent implements OnInit {
playCard: HomeCard;
statsCard: HomeCard;

constructor(
private spotifyService: SpotifyService,
private userService: UserService) {}
private userService: UserService) {
this.playCard = {
imageSource: "/assets/headphones.png",
title: "Play Heardle",
description: "Test your knowledge on how well you know your songs!",
url: "play"
};

this.statsCard = {
imageSource: "/assets/medal.png",
title: "View Stats",
description: "Take a look at your most played songs or artists!",
url: "stats"
};
}

ngOnInit(): void {
this.spotifyService.getUserProfile(this.userService.getAuthToken()).subscribe((response: UserProfileResponse) => {
this.spotifyService.getUserProfile(this.userService.authTokenSignal()).subscribe((response: UserProfileResponse) => {
this.userService.userSignal.set(response);
this.userService.setUser(response);
});
}
}
1 change: 1 addition & 0 deletions src/app/components/stats/stats.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>stats works!</p>
Empty file.
23 changes: 23 additions & 0 deletions src/app/components/stats/stats.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { StatsComponent } from './stats.component';

describe('StatsComponent', () => {
let component: StatsComponent;
let fixture: ComponentFixture<StatsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [StatsComponent]
})
.compileComponents();

fixture = TestBed.createComponent(StatsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
47 changes: 47 additions & 0 deletions src/app/components/stats/stats.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Component, OnInit } from '@angular/core';
import { SpotifyService } from '../../_shared/services/spotify.service';
import { UserService } from '../../_shared/services/user.service';
import { UserTopItems } from '../../_shared/models/user-top-items-response.model';

@Component({
selector: 'app-stats',
standalone: true,
imports: [],
templateUrl: './stats.component.html',
styleUrl: './stats.component.scss'
})
export class StatsComponent implements OnInit {
topItems!: UserTopItems;

timeRange: string;
offset: number;
topItemMode: string;

TimeRangeMap = {
"4 weeks": "short_term",
"6 weeks": "medium_term",
Lifetime: "long_term"
}

TopItemModeMap = {
Artists: "artists",
Tracks: "tracks"
}

constructor(
private spotifyService: SpotifyService,
private userService: UserService) {
this.timeRange = this.TimeRangeMap['4 weeks'];
this.offset = 0;
this.topItemMode = this.TopItemModeMap.Tracks;
}

ngOnInit(): void {
this.spotifyService.getTopItems(this.userService.authTokenSignal(), this.timeRange, this.offset, this.topItemMode)
.subscribe((response) => {
this.topItems = response;
console.log('total', this.topItems.total);
console.log('first', this.topItems.items[0].name);
});
}
}
Binary file modified src/assets/headphones.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/medal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit b8d3d96

Please sign in to comment.