Pagina nueva
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
import {HomepageComponent} from "./homepage/homepage.component";
|
import {HomepageComponent} from "./homepage/homepage.component";
|
||||||
|
import {SearchComponent} from "./search/search.component";
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: HomepageComponent}
|
{path: '', component: HomepageComponent},
|
||||||
|
{path: 'search', component: SearchComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ import { NgModule } from '@angular/core';
|
|||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { SearchHomeComponent } from './homepage/searchHome/searchHome.component';
|
import { SearchBarComponent } from './homepage/searchBar/searchBar.component';
|
||||||
import {HttpClientModule} from "@angular/common/http";
|
import {HttpClientModule} from "@angular/common/http";
|
||||||
import { HomepageComponent } from './homepage/homepage.component';
|
import { HomepageComponent } from './homepage/homepage.component';
|
||||||
import { NavComponent } from './nav/nav.component';
|
import { NavComponent } from './nav/nav.component';
|
||||||
|
import { SearchComponent } from './search/search.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
SearchHomeComponent,
|
SearchBarComponent,
|
||||||
HomepageComponent,
|
HomepageComponent,
|
||||||
NavComponent,
|
NavComponent,
|
||||||
|
SearchComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="hero-body">
|
<div class="hero-body">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title">Busca la musica que disfrutas!</h1>
|
<h1 class="title">Busca la musica que disfrutas!</h1>
|
||||||
<app-search></app-search>
|
<app-search-bar></app-search-bar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input #input class="input" type="search" (focus)="showList = true" (input)="searchTerm$.next(input.value)">
|
<input #input class="input" type="search" (focus)="showList = true" [value]="query$.getValue()" (input)="query$.next(input.value)" (keyup.enter)="search()">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a class="button is-white"><span class="icon"><i class="fas fa-search"></i></span></a>
|
<a class="button" [class]="button_color"><span class="icon"><i class="fas fa-search"></i></span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -22,8 +22,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngFor="let result of discs" class="media disc">
|
<div *ngFor="let result of discs" class="media disc">
|
||||||
<figure class="media-left">
|
<figure class="media-left">
|
||||||
<img *ngIf="result.cover_art" class="image is-64x64 cropped" src="{{result.cover_art.small}}" alt="{{result.title}} cover art">
|
<img *ngIf="result.cover_art" class="image is-64x64 cropped" src="{{result.cover_art.small}}"
|
||||||
<img *ngIf="!result.cover_art" class="image is-64x64 cropped" src="../../../assets/svg/placeholder.svg" alt="{{result.title}} missing cover art">
|
alt="{{result.title}} cover art">
|
||||||
|
<img *ngIf="!result.cover_art" class="image is-64x64 cropped" src="../../../assets/svg/placeholder.svg"
|
||||||
|
alt="{{result.title}} missing cover art">
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<p>{{result.title}}</p>
|
<p>{{result.title}}</p>
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { SearchHomeComponent } from './searchHome.component';
|
import { SearchBarComponent } from './searchBar.component';
|
||||||
|
|
||||||
describe('SearchComponent', () => {
|
describe('SearchComponent', () => {
|
||||||
let component: SearchHomeComponent;
|
let component: SearchBarComponent;
|
||||||
let fixture: ComponentFixture<SearchHomeComponent>;
|
let fixture: ComponentFixture<SearchBarComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ SearchHomeComponent ]
|
declarations: [ SearchBarComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(SearchHomeComponent);
|
fixture = TestBed.createComponent(SearchBarComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
66
src/app/homepage/searchBar/searchBar.component.ts
Normal file
66
src/app/homepage/searchBar/searchBar.component.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {BehaviorSubject, forkJoin, Subject} from "rxjs";
|
||||||
|
import {SearchService} from "../../services/search.service";
|
||||||
|
import {Artist, Disc, Recording} from "../../models/brainz";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search-bar',
|
||||||
|
templateUrl: './searchBar.component.html',
|
||||||
|
styleUrls: ['./searchBar.component.scss']
|
||||||
|
})
|
||||||
|
export class SearchBarComponent implements OnInit {
|
||||||
|
@ViewChild("input") input: ElementRef;
|
||||||
|
showList: boolean;
|
||||||
|
|
||||||
|
artists: Artist[] = [];
|
||||||
|
discs: Disc[] = [];
|
||||||
|
recordings: Recording[] = [];
|
||||||
|
|
||||||
|
@Input() query$ = new BehaviorSubject<string>("");
|
||||||
|
@Input() button_color = "is-white";
|
||||||
|
@Input() autocomplete = true;
|
||||||
|
|
||||||
|
constructor(private searchService: SearchService, private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
areResults() {
|
||||||
|
return this.artists.length > 0 || this.discs.length > 0 || this.recordings.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
if (this.input.nativeElement === document.activeElement) return true;
|
||||||
|
this.showList = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.query$.subscribe(() => {
|
||||||
|
this.artists = [];
|
||||||
|
this.discs = [];
|
||||||
|
this.recordings = [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let searchArtist = this.searchService.searchArtist(this.query$).subscribe((result) => {
|
||||||
|
this.artists = result.artists;
|
||||||
|
});
|
||||||
|
let searchDisc = this.searchService.searchDisc(this.query$).subscribe((result) => {
|
||||||
|
this.discs = result.discs;
|
||||||
|
});
|
||||||
|
let searchRecording = this.searchService.searchRecording(this.query$).subscribe((result) => {
|
||||||
|
this.recordings = result.recordings;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
search() {
|
||||||
|
this.router.navigate(['search'], {
|
||||||
|
state: {
|
||||||
|
artists: this.artists,
|
||||||
|
discs: this.discs,
|
||||||
|
recordings: this.recordings
|
||||||
|
},
|
||||||
|
queryParams: {query: this.query$.getValue()}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
|
|
||||||
import {BehaviorSubject, forkJoin, Subject} from "rxjs";
|
|
||||||
import {SearchService} from "../../search.service";
|
|
||||||
import {Artist, Disc, Recording} from "../../models/brainz";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-search',
|
|
||||||
templateUrl: './searchHome.component.html',
|
|
||||||
styleUrls: ['./searchHome.component.scss']
|
|
||||||
})
|
|
||||||
export class SearchHomeComponent implements OnInit {
|
|
||||||
@ViewChild("input") input: ElementRef;
|
|
||||||
showList: boolean;
|
|
||||||
|
|
||||||
artists: Artist[] = [];
|
|
||||||
discs: Disc[] = [];
|
|
||||||
recordings: Recording[] = [];
|
|
||||||
|
|
||||||
searchTerm$ = new Subject<string>();
|
|
||||||
|
|
||||||
constructor(private searchService: SearchService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
areResults() {
|
|
||||||
return this.artists.length > 0 || this.discs.length > 0 || this.recordings.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
if (this.input.nativeElement === document.activeElement) return true;
|
|
||||||
this.showList = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.searchTerm$.subscribe(() => {
|
|
||||||
this.artists = [];
|
|
||||||
this.discs = [];
|
|
||||||
this.recordings = [];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let searchArtist = this.searchService.searchArtist(this.searchTerm$).subscribe((result) => {
|
|
||||||
this.artists = result.artists;
|
|
||||||
});
|
|
||||||
let searchDisc = this.searchService.searchDisc(this.searchTerm$).subscribe((result) => {
|
|
||||||
this.discs = result.discs;
|
|
||||||
});
|
|
||||||
let searchRecording = this.searchService.searchRecording(this.searchTerm$).subscribe((result) => {
|
|
||||||
this.recordings = result.recordings;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
60
src/app/search/search.component.html
Normal file
60
src/app/search/search.component.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<app-nav></app-nav>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<app-search-bar [query$]="query$" [autocomplete]="false" [button_color]="'is-primary'"></app-search-bar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="container tabs is-boxed">
|
||||||
|
<ul>
|
||||||
|
<li [class.is-active]="artists_active" (click)="show_artist()">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fas fa-user" aria-hidden="true"></i></span>
|
||||||
|
<span>Artistas</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li [class.is-active]="discs_active" (click)="show_discs()">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fas fa-compact-disc" aria-hidden="true"></i></span>
|
||||||
|
<span>Discos</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li [class.is-active]="songs_active" (click)="show_songs()">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fas fa-music" aria-hidden="true"></i></span>
|
||||||
|
<span>Canciones</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="artists_active">
|
||||||
|
<div class="container">
|
||||||
|
<div class="media" *ngFor="let artist of artists">
|
||||||
|
<div class="media-content">
|
||||||
|
<p>{{artist.name}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="discs_active">
|
||||||
|
<div class="container">
|
||||||
|
<div class="media" *ngFor="let disc of discs">
|
||||||
|
<div class="media-content">
|
||||||
|
<p>{{disc.title}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="songs_active">
|
||||||
|
<div class="container">
|
||||||
|
<div class="media" *ngFor="let song of recordings">
|
||||||
|
<div class="media-content">
|
||||||
|
<p>{{song.title}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
0
src/app/search/search.component.scss
Normal file
0
src/app/search/search.component.scss
Normal file
25
src/app/search/search.component.spec.ts
Normal file
25
src/app/search/search.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SearchComponent } from './search.component';
|
||||||
|
|
||||||
|
describe('SearchComponent', () => {
|
||||||
|
let component: SearchComponent;
|
||||||
|
let fixture: ComponentFixture<SearchComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SearchComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
66
src/app/search/search.component.ts
Normal file
66
src/app/search/search.component.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {SearchService} from "../services/search.service";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
import {Artist, Disc, Recording} from "../models/brainz";
|
||||||
|
import {BehaviorSubject, Observable} from "rxjs";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search',
|
||||||
|
templateUrl: './search.component.html',
|
||||||
|
styleUrls: ['./search.component.scss']
|
||||||
|
})
|
||||||
|
export class SearchComponent implements OnInit {
|
||||||
|
query$ = new BehaviorSubject<string>("");
|
||||||
|
|
||||||
|
artists: Artist[] = [];
|
||||||
|
discs: Disc[] = [];
|
||||||
|
recordings: Recording[] = [];
|
||||||
|
|
||||||
|
artists_active = true;
|
||||||
|
discs_active = false;
|
||||||
|
songs_active= false;
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute, private searchService: SearchService) {
|
||||||
|
this.route.queryParams.pipe(map(params => params['query'] as string)).subscribe((query) => {
|
||||||
|
this.query$.next(query);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.query$.subscribe(() => {
|
||||||
|
this.artists = [];
|
||||||
|
this.discs = [];
|
||||||
|
this.recordings = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchService.searchArtist(this.query$).subscribe((result) => this.artists = result.artists);
|
||||||
|
this.searchService.searchDisc(this.query$).subscribe((result) => this.discs = result.discs);
|
||||||
|
this.searchService.searchRecording(this.query$).subscribe((result) => this.recordings = result.recordings);
|
||||||
|
}
|
||||||
|
|
||||||
|
show_artist() {
|
||||||
|
if(this.artists_active) return;
|
||||||
|
|
||||||
|
this.artists_active = true;
|
||||||
|
this.discs_active = false;
|
||||||
|
this.songs_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_discs() {
|
||||||
|
if(this.discs_active) return;
|
||||||
|
|
||||||
|
this.artists_active = false;
|
||||||
|
this.discs_active = true;
|
||||||
|
this.songs_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_songs() {
|
||||||
|
if(this.songs_active) return;
|
||||||
|
|
||||||
|
this.artists_active = false;
|
||||||
|
this.discs_active = false;
|
||||||
|
this.songs_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
|||||||
import {forkJoin, merge, Observable, of} from "rxjs";
|
import {forkJoin, merge, Observable, of} from "rxjs";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {debounceTime, distinctUntilChanged, switchMap, tap} from "rxjs/operators";
|
import {debounceTime, distinctUntilChanged, switchMap, tap} from "rxjs/operators";
|
||||||
import {ArtistSearch, DiscSearch, RecordingSearch} from "./models/brainz";
|
import {ArtistSearch, DiscSearch, RecordingSearch} from "../models/brainz";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -13,39 +13,39 @@ export class SearchService {
|
|||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
searchArtist(terms: Observable<string>): Observable<ArtistSearch> {
|
searchArtist(terms: Observable<string>, page: number = 1): Observable<ArtistSearch> {
|
||||||
return terms.pipe(
|
return terms.pipe(
|
||||||
debounceTime(400),
|
debounceTime(400),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(term => this.searchArtistQuery(term))
|
switchMap(term => this.searchArtistQuery(term, page))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchDisc(terms: Observable<string>): Observable<DiscSearch> {
|
searchDisc(terms: Observable<string>, page: number = 1): Observable<DiscSearch> {
|
||||||
return terms.pipe(
|
return terms.pipe(
|
||||||
debounceTime(400),
|
debounceTime(400),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(term => this.searchDiscQuery(term))
|
switchMap(term => this.searchDiscQuery(term, page))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchRecording(terms: Observable<string>): Observable<RecordingSearch> {
|
searchRecording(terms: Observable<string>, page: number = 1): Observable<RecordingSearch> {
|
||||||
return terms.pipe(
|
return terms.pipe(
|
||||||
debounceTime(400),
|
debounceTime(400),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(term => this.searchRecordingQuery(term))
|
switchMap(term => this.searchRecordingQuery(term, page))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private searchArtistQuery(term: string): Observable<ArtistSearch> {
|
private searchArtistQuery(term: string, page: number): Observable<ArtistSearch> {
|
||||||
if (!term) {
|
if (!term) {
|
||||||
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, artists: []} as ArtistSearch);
|
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, artists: []} as ArtistSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.http.get<ArtistSearch>(`${this.baseUrl}/artist?query=${term}`);
|
return this.http.get<ArtistSearch>(`${this.baseUrl}/artist?query=${term}&page=${page}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private searchDiscQuery(term: string): Observable<DiscSearch> {
|
private searchDiscQuery(term: string, page: number): Observable<DiscSearch> {
|
||||||
if (!term) {
|
if (!term) {
|
||||||
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, discs: []} as DiscSearch);
|
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, discs: []} as DiscSearch);
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ export class SearchService {
|
|||||||
return this.http.get<DiscSearch>(`${this.baseUrl}/disc?query=${term}`);
|
return this.http.get<DiscSearch>(`${this.baseUrl}/disc?query=${term}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private searchRecordingQuery(term: string): Observable<RecordingSearch> {
|
private searchRecordingQuery(term: string, page: number): Observable<RecordingSearch> {
|
||||||
if (!term) {
|
if (!term) {
|
||||||
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, recordings: []} as RecordingSearch);
|
return of({paginate: {total: 0, current_page: 0, last_page: 0, per_page: 0}, recordings: []} as RecordingSearch);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user