diff --git a/src/app/components/browse/browse.component.ts b/src/app/components/browse/browse.component.ts index aa830fd..bbb0762 100644 --- a/src/app/components/browse/browse.component.ts +++ b/src/app/components/browse/browse.component.ts @@ -2,6 +2,7 @@ import { Component, ViewChild, AfterViewInit } from '@angular/core' import { ChartSidebarComponent } from './chart-sidebar/chart-sidebar.component' import { StatusBarComponent } from './status-bar/status-bar.component' import { ResultTableComponent } from './result-table/result-table.component' +import { SearchService } from 'src/app/core/services/search.service' @Component({ selector: 'app-browse', @@ -14,7 +15,7 @@ export class BrowseComponent implements AfterViewInit { @ViewChild('chartSidebar', { static: true }) chartSidebar: ChartSidebarComponent @ViewChild('statusBar', { static: true }) statusBar: StatusBarComponent - constructor() { } + constructor(private searchService: SearchService) { } ngAfterViewInit() { const $tableColumn = $('#table-column') @@ -27,14 +28,9 @@ export class BrowseComponent implements AfterViewInit { let pos = $tableColumn[0].scrollTop + $tableColumn[0].offsetHeight let max = $tableColumn[0].scrollHeight if (pos >= max - 5) { - // TODO: load more results (should be debounced or something; wait until results have loaded before sending the request for more) - console.log('UPDATE SCROLL') + this.searchService.updateScroll() } } }) } - - loadMoreResults() { - // TODO: use the same query as the current search, but append more results if there are any more to be viewed - } } \ No newline at end of file diff --git a/src/app/core/services/search.service.ts b/src/app/core/services/search.service.ts index 9443a31..8f542e4 100644 --- a/src/app/core/services/search.service.ts +++ b/src/app/core/services/search.service.ts @@ -1,6 +1,6 @@ import { Injectable, EventEmitter } from '@angular/core' import { ElectronService } from './electron.service' -import { SearchType, SongResult } from 'src/electron/shared/interfaces/search.interface' +import { SearchType, SongResult, SongSearch } from 'src/electron/shared/interfaces/search.interface' @Injectable({ providedIn: 'root' @@ -10,11 +10,24 @@ export class SearchService { private resultsChangedEmitter = new EventEmitter() // For when any results change private newResultsEmitter = new EventEmitter() // For when a new search happens private results: SongResult[] = [] + private awaitingResults = false + private currentQuery: SongSearch + private allResultsVisible = true constructor(private electronService: ElectronService) { } async newSearch(query: string) { - this.results = await this.electronService.invoke('song-search', { query, type: SearchType.Any }) + this.awaitingResults = true + this.currentQuery = { query, type: SearchType.Any, offset: 0, length: 20 + 1 } // TODO: make length a setting + this.results = await this.electronService.invoke('song-search', this.currentQuery) + if (this.results.length > 20) { + this.results.splice(20, 1) + this.allResultsVisible = false + } else { + this.allResultsVisible = true + } + this.awaitingResults = false + this.newResultsEmitter.emit(this.results) this.resultsChangedEmitter.emit(this.results) } @@ -30,4 +43,22 @@ export class SearchService { get resultCount() { return this.results.length } + + async updateScroll() { + if (!this.awaitingResults && !this.allResultsVisible) { + this.awaitingResults = true + this.currentQuery.offset += 20 + const newResults = await this.electronService.invoke('song-search', this.currentQuery) + if (newResults.length > 20) { + newResults.splice(20, 1) + this.allResultsVisible = false + } else { + this.allResultsVisible = true + } + this.results.push(...newResults) + this.awaitingResults = false + + this.resultsChangedEmitter.emit(this.results) + } + } } diff --git a/src/electron/ipc/SearchHandler.ipc.ts b/src/electron/ipc/SearchHandler.ipc.ts index bf3c08b..69bb745 100644 --- a/src/electron/ipc/SearchHandler.ipc.ts +++ b/src/electron/ipc/SearchHandler.ipc.ts @@ -23,7 +23,7 @@ class SearchHandler implements IPCInvokeHandler<'song-search'> { */ private getSearchQuery(search: SongSearch) { switch (search.type) { - case SearchType.Any: return this.getGeneralSearchQuery(search.query) + case SearchType.Any: return this.getGeneralSearchQuery(search) default: return '<<>>' // TODO: add more search types } } @@ -31,12 +31,12 @@ class SearchHandler implements IPCInvokeHandler<'song-search'> { /** * @returns a database query that returns the top 20 songs that match `search`. */ - private getGeneralSearchQuery(searchString: string) { + private getGeneralSearchQuery(search: SongSearch) { return ` SELECT id, name, artist, album, genre, year FROM Song - WHERE MATCH (name,artist,album,genre) AGAINST (${escape(searchString)}) > 0 - LIMIT ${20} OFFSET ${0}; + WHERE MATCH (name,artist,album,genre) AGAINST (${escape(search.query)}) > 0 + LIMIT ${search.length} OFFSET ${search.offset}; ` // TODO: add parameters for the limit and offset } } diff --git a/src/electron/shared/interfaces/search.interface.ts b/src/electron/shared/interfaces/search.interface.ts index 7fe802a..205a146 100644 --- a/src/electron/shared/interfaces/search.interface.ts +++ b/src/electron/shared/interfaces/search.interface.ts @@ -4,6 +4,8 @@ export interface SongSearch { query: string type: SearchType + offset: number + length: number } /**