Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src-angular/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import { RouteReuseStrategy, RouterModule, Routes } from '@angular/router'

import { BrowseComponent } from './components/browse/browse.component'
import { SettingsComponent } from './components/settings/settings.component'
import { SongListsComponent } from './components/songlists/songlists.component'
import { SongListDetailComponent } from './components/songlists/songlist-detail/songlist-detail.component'
import { ToolsComponent } from './components/tools/tools.component'
import { TabPersistStrategy } from './core/tab-persist.strategy'

const routes: Routes = [
{ path: 'browse', component: BrowseComponent, data: { shouldReuse: true } },
{ path: 'library', redirectTo: '/browse' },
{ path: 'lists', component: SongListsComponent, data: { shouldReuse: true } },
{ path: 'lists/:id', component: SongListDetailComponent },
{ path: 'tools', component: ToolsComponent, data: { shouldReuse: true } },
{ path: 'settings', component: SettingsComponent, data: { shouldReuse: true } },
{ path: 'about', redirectTo: '/browse' },
Expand Down
5 changes: 4 additions & 1 deletion src-angular/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component } from '@angular/core'

import { SettingsService } from './core/services/settings.service'
import { SongListService } from './core/services/songlist.service'

@Component({
selector: 'app-root',
Expand All @@ -12,9 +13,11 @@ export class AppComponent {

settingsLoaded = false

constructor(settingsService: SettingsService) {
constructor(settingsService: SettingsService, songListService: SongListService) {
// Ensure settings are loaded before rendering the application
settingsService.loadSettings().then(() => this.settingsLoaded = true)
// Load song lists in the background (not critical for initial render)
songListService.loadLists()

document.addEventListener('keydown', event => {
if (event.ctrlKey && (event.key === '+' || event.key === '-' || event.key === '=' || event.key === '0')) {
Expand Down
8 changes: 8 additions & 0 deletions src-angular/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import { SearchBarComponent } from './components/browse/search-bar/search-bar.co
import { DownloadsModalComponent } from './components/browse/status-bar/downloads-modal/downloads-modal.component'
import { StatusBarComponent } from './components/browse/status-bar/status-bar.component'
import { SettingsComponent } from './components/settings/settings.component'
import { SongListsComponent } from './components/songlists/songlists.component'
import { SongListDetailComponent } from './components/songlists/songlist-detail/songlist-detail.component'
import { AddToListModalComponent } from './components/songlists/add-to-list-modal/add-to-list-modal.component'
import { ToolbarComponent } from './components/toolbar/toolbar.component'
import { ToolsComponent } from './components/tools/tools.component'
import { RemoveStyleTagsPipe } from './core/pipes/remove-style-tags.pipe'

@NgModule({
Expand All @@ -35,6 +39,10 @@ import { RemoveStyleTagsPipe } from './core/pipes/remove-style-tags.pipe'
DownloadsModalComponent,
RemoveStyleTagsPipe,
SettingsComponent,
SongListsComponent,
SongListDetailComponent,
AddToListModalComponent,
ToolsComponent,
],
bootstrap: [AppComponent], imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@
</div>
<div class="join">
<button class="btn rounded-md flex-1 join-item btn-primary capitalize" (click)="onDownloadClicked()">Download</button>
<button
class="btn rounded-md join-item btn-neutral tooltip tooltip-top"
data-tip="Add to List"
(click)="onAddToListClicked()">
<i class="bi bi-collection text-lg"></i>
</button>
<div
#menu
class="cursor-pointer bg-neutral rounded-md join-item dropdown dropdown-top dropdown-end p-1 flex items-center"
Expand All @@ -166,5 +172,6 @@ <h3 class="text-lg font-bold">Chart can't be downloaded yet</h3>
</form>
</dialog>
</div>
<app-add-to-list-modal #addToListModal />
</div>
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SettingsService } from 'src-angular/app/core/services/settings.service'
import { ChartData } from 'src-shared/interfaces/search.interface'
import { setlistNames } from 'src-shared/setlist-names'
import { difficulties, difficultyDisplay, driveLink, hasIssues, instruments, msToRoughTime, removeStyleTags, shortInstrumentDisplay } from 'src-shared/UtilFunctions'
import { AddToListModalComponent } from '../../songlists/add-to-list-modal/add-to-list-modal.component'

@Component({
selector: 'app-chart-sidebar',
Expand All @@ -20,6 +21,7 @@ export class ChartSidebarComponent implements OnInit {

@ViewChild('menu') menu: ElementRef
@ViewChild('libraryDirectoryErrorModal') libraryDirectoryErrorModal: ElementRef<HTMLDialogElement>
@ViewChild('addToListModal') addToListModal: AddToListModalComponent

public shortInstrumentDisplay = shortInstrumentDisplay
public difficultyDisplay = difficultyDisplay
Expand Down Expand Up @@ -292,4 +294,10 @@ export class ChartSidebarComponent implements OnInit {
}
})
}

onAddToListClicked() {
if (this.selectedChart) {
this.addToListModal.open([this.selectedChart])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
<div *ngIf="searchService.songsResponse" class="text-nowrap">
{{ searchService.songsResponse.found | number: '1.0-0' }} Result{{ searchService.songsResponse.found === 1 ? '' : 's' }}
</div>
<div class="flex-1">
<div class="flex-1 flex gap-2">
<button *ngIf="selectedGroupIds.length > 1" (click)="downloadSelected()" class="btn btn-sm btn-primary text-nowrap">
Download {{ selectedGroupIds.length }} Results
</button>
<button *ngIf="selectedGroupIds.length > 1" (click)="addSelectedToList()" class="btn btn-sm btn-neutral text-nowrap">
<i class="bi bi-collection"></i>
Add to List
</button>
</div>
<div *ngIf="downloadService.downloadCount > 0" class="text-ellipsis text-nowrap overflow-hidden whitespace-nowrap max-w-full">
{{ downloadService.currentDownloadText }}
Expand Down Expand Up @@ -33,4 +37,6 @@
<button>close</button>
</form>
</dialog>

<app-add-to-list-modal #addToListModal />
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { removeStyleTags } from '../../../../../src-shared/UtilFunctions.js'
import { DownloadService } from '../../../core/services/download.service'
import { SearchService } from '../../../core/services/search.service'
import { SelectionService } from '../../../core/services/selection.service'
import { AddToListModalComponent } from '../../songlists/add-to-list-modal/add-to-list-modal.component'

@Component({
selector: 'app-status-bar',
Expand All @@ -15,6 +16,7 @@ import { SelectionService } from '../../../core/services/selection.service'
export class StatusBarComponent {

@ViewChild('downloadsModal', { static: false }) downloadsModalComponent: ElementRef
@ViewChild('addToListModal') addToListModal: AddToListModalComponent

constructor(
public downloadService: DownloadService,
Expand Down Expand Up @@ -46,4 +48,15 @@ export class StatusBarComponent {
clearCompleted() {
this.downloadService.cancelAllCompleted()
}

addSelectedToList() {
const selectedGroupIds = this.selectedGroupIds
const selectedCharts = this.searchService.groupedSongs.filter(gs => selectedGroupIds.includes(gs[0].groupId))

const uniqueCharts = _.uniqBy(selectedCharts, gs => `${removeStyleTags(gs[0].artist ?? 'Unknown Artist')
} - ${removeStyleTags(gs[0].name ?? 'Unknown Name')
} (${removeStyleTags(gs[0].charter ?? 'Unknown Charter')})`).map(gs => gs[0])

this.addToListModal.open(uniqueCharts)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<div class="text-xs">{{ '{name}, {artist}, {album}, {genre}, {year}, {charter}' }}</div>
<br />
Example:
<div class="text-xs">"{artist}/{name} ({charter})" will create chart folders that look like "{name} ({charter})" inside subfolders that look like "{artist}"</div>
<div class="text-xs">"{{ '{artist}' }}/{{ '{name}' }} ({{ '{charter}' }})" will create chart folders that look like "{{ '{name}' }} ({{ '{charter}' }})" inside subfolders that look like "{{ '{artist}' }}"</div>
</ul>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!-- Main Add to List Modal -->
<dialog #addToListModal class="modal">
<div class="modal-box bg-base-100 text-base-content">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
<i class="bi bi-x-lg text-lg"></i>
</button>
</form>
<h3 class="text-lg font-bold mb-2">Add to List</h3>
<p class="text-sm text-base-content/70 mb-4">{{ chartDescription }}</p>

@if (lists.length === 0) {
<div class="text-center py-8 text-base-content/60">
<i class="bi bi-collection text-4xl mb-2"></i>
<p>No song lists yet</p>
<p class="text-sm">Create your first list to get started</p>
</div>
} @else {
<div class="max-h-64 overflow-y-auto">
@for (list of lists; track list.id) {
<label class="flex items-center gap-3 p-2 hover:bg-base-200 rounded cursor-pointer">
<input
type="checkbox"
class="checkbox"
[checked]="isSelected(list.id)"
[indeterminate]="isPartiallyInList(list) && !isSelected(list.id)"
(change)="toggleList(list.id)" />
<div class="flex-1 min-w-0">
<p class="font-medium truncate">{{ list.name }}</p>
<p class="text-xs text-base-content/60">{{ list.entries.length }} songs</p>
</div>
</label>
}
</div>
}

<div class="divider my-2"></div>

<button class="btn btn-ghost btn-sm w-full justify-start" (click)="openCreateNewList()">
<i class="bi bi-plus-lg"></i>
Create new list
</button>

<div class="modal-action">
<form method="dialog">
<button class="btn btn-ghost">Cancel</button>
</form>
<button class="btn btn-primary" (click)="save()">Save</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>

<!-- Create New List Modal (nested) -->
<dialog #createNewListModal class="modal">
<div class="modal-box bg-base-100 text-base-content">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
<i class="bi bi-x-lg text-lg"></i>
</button>
</form>
<h3 class="text-lg font-bold mb-4">Create New List</h3>
<div class="form-control mb-4">
<label class="label">
<span class="label-text">Name</span>
</label>
<input
type="text"
[formControl]="newListName"
class="input input-bordered"
placeholder="My Song List"
(keydown.enter)="createNewList()" />
</div>
<div class="form-control mb-4">
<label class="label">
<span class="label-text">Description (optional)</span>
</label>
<textarea
[formControl]="newListDescription"
class="textarea textarea-bordered"
placeholder="A collection of songs..."
rows="2"></textarea>
</div>
<div class="modal-action">
<form method="dialog">
<button class="btn btn-ghost">Cancel</button>
</form>
<button class="btn btn-primary" [disabled]="!newListName.valid" (click)="createNewList()">Create</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
Loading