From 199374b2e27bb407ed9181ab22491b5e2b38e42d Mon Sep 17 00:00:00 2001 From: Geomitron <22552797+Geomitron@users.noreply.github.com> Date: Mon, 25 Dec 2023 10:29:57 -0600 Subject: [PATCH] Fix checkboxes and bulk download --- .../components/browse/browse.component.html | 9 +-- .../app/components/browse/browse.component.ts | 33 +++------ .../chart-sidebar/chart-sidebar.component.ts | 5 +- .../result-table-row.component.html | 2 +- .../result-table-row.component.ts | 2 +- .../result-table/result-table.component.html | 2 +- .../result-table/result-table.component.ts | 20 +++--- .../browse/status-bar/status-bar.component.ts | 67 ++----------------- .../app/core/services/download.service.ts | 15 +++-- .../app/core/services/search.service.ts | 11 ++- .../app/core/services/selection.service.ts | 18 ++--- src-electron/ipc/download/DownloadQueue.ts | 8 ++- 12 files changed, 66 insertions(+), 126 deletions(-) diff --git a/src-angular/app/components/browse/browse.component.html b/src-angular/app/components/browse/browse.component.html index fb7d630..bf89a12 100644 --- a/src-angular/app/components/browse/browse.component.html +++ b/src-angular/app/components/browse/browse.component.html @@ -1,12 +1,13 @@ - +
- +
- +
- + diff --git a/src-angular/app/components/browse/browse.component.ts b/src-angular/app/components/browse/browse.component.ts index c326844..1b4d740 100644 --- a/src-angular/app/components/browse/browse.component.ts +++ b/src-angular/app/components/browse/browse.component.ts @@ -1,36 +1,21 @@ -import { Component, HostBinding, ViewChild } from '@angular/core' +import { AfterViewInit, Component, ElementRef, HostBinding, ViewChild } from '@angular/core' import { SearchService } from 'src-angular/app/core/services/search.service' -import { ChartSidebarComponent } from './chart-sidebar/chart-sidebar.component' -import { ResultTableComponent } from './result-table/result-table.component' -import { StatusBarComponent } from './status-bar/status-bar.component' - @Component({ selector: 'app-browse', templateUrl: './browse.component.html', }) -export class BrowseComponent { +export class BrowseComponent implements AfterViewInit { @HostBinding('class.contents') contents = true - @ViewChild('resultTable', { static: true }) resultTable: ResultTableComponent - @ViewChild('chartSidebar', { static: true }) chartSidebar: ChartSidebarComponent - @ViewChild('statusBar', { static: true }) statusBar: StatusBarComponent + + @ViewChild('resultTableDiv', { static: true }) resultTableDiv: ElementRef constructor(private searchService: SearchService) { } - // TODO - // ngAfterViewInit() { - // const $tableColumn = $('#table-column') - // $tableColumn.on('scroll', () => { - // const pos = $tableColumn[0].scrollTop + $tableColumn[0].offsetHeight - // const max = $tableColumn[0].scrollHeight - // if (pos >= max - 5) { - // this.searchService.updateScroll() - // } - // }) - - // this.searchService.onNewSearch(() => { - // $tableColumn.scrollTop(0) - // }) - // } + ngAfterViewInit() { + this.searchService.newSearch.subscribe(() => { + this.resultTableDiv.nativeElement.scrollTop = 0 + }) + } } diff --git a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts index 0d429c0..f623b79 100644 --- a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts +++ b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts @@ -44,7 +44,7 @@ export class ChartSidebarComponent implements OnInit { ) { } ngOnInit() { - this.searchService.searchUpdated.subscribe(() => { + this.searchService.newSearch.subscribe(() => { this.charts = null this.selectedChart = null }) @@ -280,8 +280,7 @@ export class ChartSidebarComponent implements OnInit { * Adds the selected chart to the download queue. */ onDownloadClicked() { - this.downloadService.addDownload(this.selectedChart!.md5, `${this.selectedChart!.artist ?? 'Unknown Artist' - } - ${this.selectedChart!.name ?? 'Unknown Name'} (${this.selectedChart!.charter ?? 'Unknown Charter'})`) + this.downloadService.addDownload(this.selectedChart!) } public showMenu() { diff --git a/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.html b/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.html index 7e6e55b..9ec6986 100644 --- a/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.html +++ b/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.html @@ -1,5 +1,5 @@ - + {{ song.length }} {{ song[0].name }} diff --git a/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.ts b/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.ts index baf33b0..f1c257a 100644 --- a/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.ts +++ b/src-angular/app/components/browse/result-table/result-table-row/result-table-row.component.ts @@ -13,7 +13,7 @@ export class ResultTableRowComponent implements OnInit { constructor(private selectionService: SelectionService) { } ngOnInit() { - this.selectionService.selections[this.groupId] = false + this.selectionService.selections[this.groupId] = this.selectionService.isAllSelected() } get groupId() { diff --git a/src-angular/app/components/browse/result-table/result-table.component.html b/src-angular/app/components/browse/result-table/result-table.component.html index c98a478..37a4a05 100644 --- a/src-angular/app/components/browse/result-table/result-table.component.html +++ b/src-angular/app/components/browse/result-table/result-table.component.html @@ -2,7 +2,7 @@ - + Name diff --git a/src-angular/app/components/browse/result-table/result-table.component.ts b/src-angular/app/components/browse/result-table/result-table.component.ts index 442feee..50ebd92 100644 --- a/src-angular/app/components/browse/result-table/result-table.component.ts +++ b/src-angular/app/components/browse/result-table/result-table.component.ts @@ -32,8 +32,13 @@ export class ResultTableComponent implements OnInit { ) { } ngOnInit() { - this.searchService.searchUpdated.subscribe(() => { + this.searchService.newSearch.subscribe(() => { this.activeSong = null + this.sortDirection = 'ascending' + this.sortColumn = null + this.updateSort() + }) + this.searchService.updateSearch.subscribe(() => { this.updateSort() }) } @@ -65,18 +70,17 @@ export class ResultTableComponent implements OnInit { private updateSort() { const col = this.sortColumn if (col !== null) { - const groupedSongs = sortBy(this.searchService.groupedSongs, song => song[0][col]) + const groupedSongs = sortBy(this.searchService.groupedSongs, song => song[0][col]?.toLowerCase()) if (this.sortDirection === 'descending') { groupedSongs.reverse() } this.searchService.groupedSongs = groupedSongs } } - /** - * Called when the user checks the `checkboxColumn`. - */ - checkAll(isChecked: boolean) { - console.log(isChecked) - if (isChecked) { + get allSelected() { + return this.selectionService.isAllSelected() + } + set allSelected(value: boolean) { + if (value) { this.selectionService.selectAll() } else { this.selectionService.deselectAll() diff --git a/src-angular/app/components/browse/status-bar/status-bar.component.ts b/src-angular/app/components/browse/status-bar/status-bar.component.ts index c547c02..f8c2a12 100644 --- a/src-angular/app/components/browse/status-bar/status-bar.component.ts +++ b/src-angular/app/components/browse/status-bar/status-bar.component.ts @@ -14,16 +14,6 @@ export class StatusBarComponent { @ViewChild('downloadsModal', { static: false }) downloadsModalComponent: ElementRef - multipleCompleted = false - downloading = false - error = false - percent = 0 - // TODO - // eslint-disable-next-line @typescript-eslint/no-explicit-any - batchResults: any[] - // eslint-disable-next-line @typescript-eslint/no-explicit-any - chartGroups: any[][] - constructor( public downloadService: DownloadService, public searchService: SearchService, @@ -41,59 +31,10 @@ export class StatusBarComponent { } async downloadSelected() { - // this.chartGroups = [] - // // TODO - // // this.batchResults = await window.electron.invoke.getBatchSongDetails(this.selectedResults.map(result => result.id)) - // const versionGroups = groupBy(this.batchResults, 'songID') - // for (const versionGroup of versionGroups) { - // if (versionGroup.findIndex(version => version.chartID !== versionGroup[0].chartID) !== -1) { - // // Must have multiple charts of this song - // this.chartGroups.push(versionGroup.filter(version => version.versionID === version.latestVersionID)) - // } - // } - - // if (this.chartGroups.length === 0) { - // for (const versions of versionGroups) { - // // this.searchService.sortChart(versions) - // const downloadVersion = versions[0] - // const downloadSong = this.selectedResults.find(song => song.id === downloadVersion.songID)! - // this.downloadService.addDownload( - // downloadVersion.versionID, { - // chartName: downloadVersion.chartName, - // artist: downloadSong.artist, - // charter: downloadVersion.charters, - // driveData: downloadVersion.driveData, - // }) - // } - // } else { - // // TODO - // // $('#selectedModal').modal('show') - // // [download all charts for each song] [deselect these songs] [X] - // } - } - - downloadAllCharts() { - // const songChartGroups = groupBy(this.batchResults, 'songID', 'chartID') - // for (const chart of songChartGroups) { - // // this.searchService.sortChart(chart) - // const downloadVersion = chart[0] - // const downloadSong = this.selectedResults.find(song => song.id === downloadVersion.songID)! - // this.downloadService.addDownload( - // downloadVersion.versionID, { - // chartName: downloadVersion.chartName, - // artist: downloadSong.artist, - // charter: downloadVersion.charters, - // driveData: downloadVersion.driveData, - // } - // ) - // } - } - - deselectSongsWithMultipleCharts() { - // TODO - // for (const chartGroup of this.chartGroups) { - // this.selectionService.deselectSong(chartGroup[0].songID) - // } + const selectedGroupIds = this.selectedGroupIds + for (const chart of this.searchService.groupedSongs.filter(gs => selectedGroupIds.includes(gs[0].groupId))) { + this.downloadService.addDownload(chart[0]) + } } clearCompleted() { diff --git a/src-angular/app/core/services/download.service.ts b/src-angular/app/core/services/download.service.ts index 097579f..04ac0be 100644 --- a/src-angular/app/core/services/download.service.ts +++ b/src-angular/app/core/services/download.service.ts @@ -1,6 +1,8 @@ import { EventEmitter, Injectable, NgZone } from '@angular/core' import { assign } from 'lodash' +import { ChartData } from 'src-shared/interfaces/search.interface' +import { removeStyleTags } from 'src-shared/UtilFunctions' import { DownloadProgress } from '../../../../src-shared/interfaces/download.interface' @@ -16,7 +18,7 @@ export class DownloadService { window.electron.on.downloadQueueUpdate(download => zone.run(() => { const downloadIndex = this.downloads.findIndex(d => d.md5 === download.md5) if (download.type === 'cancel') { - this.downloads = this.downloads.filter(d => d.md5 !== this.downloads[downloadIndex].md5) + this.downloads = this.downloads.filter(d => d.md5 !== this.downloads[downloadIndex]?.md5) } else if (downloadIndex === -1) { this.downloads.push(download) } else { @@ -50,13 +52,16 @@ export class DownloadService { return this.downloads.find(download => download.type === 'error') ? true : false } - addDownload(md5: string, chartName: string) { - if (!this.downloads.find(d => d.md5 === md5)) { // Don't download something twice + addDownload(chart: ChartData) { + if (!this.downloads.find(d => d.md5 === chart.md5)) { // Don't download something twice if (this.downloads.every(d => d.type === 'done')) { // Reset overall progress bar if it finished this.downloads.forEach(d => d.stale = true) } + const chartName = `${removeStyleTags(chart.artist ?? 'Unknown Artist') + } - ${removeStyleTags(chart.name ?? 'Unknown Name') + } (${removeStyleTags(chart.charter ?? 'Unknown Charter')})` this.downloads.push({ - md5, + md5: chart.md5, chartName, header: 'Waiting for other downloads to finish...', body: '', @@ -64,7 +69,7 @@ export class DownloadService { type: 'good', isPath: false, }) - window.electron.emit.download({ action: 'add', md5, chartName }) + window.electron.emit.download({ action: 'add', md5: chart.md5, chartName }) } this.downloadCountChanges.emit(this.downloadCount) } diff --git a/src-angular/app/core/services/search.service.ts b/src-angular/app/core/services/search.service.ts index 4f969ca..0f0351b 100644 --- a/src-angular/app/core/services/search.service.ts +++ b/src-angular/app/core/services/search.service.ts @@ -18,7 +18,8 @@ export class SearchService { public searchLoading = false public songsResponse: Partial public currentPage = 1 - public searchUpdated = new EventEmitter>() + public newSearch = new EventEmitter>() + public updateSearch = new EventEmitter>() public isDefaultSearch = true public groupedSongs: ChartData[][] @@ -115,7 +116,11 @@ export class SearchService { .value() ) - this.searchUpdated.emit(response) + if (nextPage) { + this.updateSearch.emit(response) + } else { + this.newSearch.emit(response) + } }) ) } @@ -154,7 +159,7 @@ export class SearchService { .value() ) - this.searchUpdated.emit(response) + this.newSearch.emit(response) }) ) } diff --git a/src-angular/app/core/services/selection.service.ts b/src-angular/app/core/services/selection.service.ts index 05425a5..67d2f69 100644 --- a/src-angular/app/core/services/selection.service.ts +++ b/src-angular/app/core/services/selection.service.ts @@ -2,34 +2,29 @@ import { EventEmitter, Injectable } from '@angular/core' import { SearchService } from './search.service' -// Note: this class prevents event cycles by only emitting events if the checkbox changes - @Injectable({ providedIn: 'root', }) export class SelectionService { + private allSelected = false private selectAllChangedEmitter = new EventEmitter() public selections: { [groupId: number]: boolean | undefined } = {} constructor(searchService: SearchService) { - searchService.searchUpdated.subscribe(() => { + searchService.newSearch.subscribe(() => { this.selections = {} + this.deselectAll() }) } - getSelectedResults() { - // TODO - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return [] as any[] // this.searchResults.filter(result => this.selections[result.id] === true) - } - - onSelectAllChanged(callback: (selected: boolean) => void) { - this.selectAllChangedEmitter.subscribe(callback) + isAllSelected() { + return this.allSelected } deselectAll() { + this.allSelected = false for (const groupId in this.selections) { this.selections[groupId] = false } @@ -37,6 +32,7 @@ export class SelectionService { } selectAll() { + this.allSelected = true for (const groupId in this.selections) { this.selections[groupId] = true } diff --git a/src-electron/ipc/download/DownloadQueue.ts b/src-electron/ipc/download/DownloadQueue.ts index 2838b22..706633f 100644 --- a/src-electron/ipc/download/DownloadQueue.ts +++ b/src-electron/ipc/download/DownloadQueue.ts @@ -67,13 +67,17 @@ export class DownloadQueue { } remove(md5: string) { - if (this.downloadQueue[0]?.md5 === md5) { - this.downloadQueue[0].cancel() + const currentDownload = this.downloadQueue[0] + if (currentDownload?.md5 === md5) { + currentDownload.cancel() this.downloadRunning = false } this.downloadQueue = this.downloadQueue.filter(cd => cd.md5 !== md5) this.retryQueue = this.retryQueue.filter(cd => cd.md5 !== md5) this.erroredQueue = this.erroredQueue.filter(cd => cd.md5 !== md5) + if (currentDownload) { + this.moveQueue() + } emitIpcEvent('downloadQueueUpdate', { md5,