From 6280d04ee79ddb1431699bc8c2883b56eabd2181 Mon Sep 17 00:00:00 2001 From: benjamin Date: Wed, 11 Mar 2026 18:33:41 +0100 Subject: [PATCH] fix scroll position --- src/app/app.component.ts | 9 +------- .../songs/services/song-data.service.ts | 9 +++++++- .../songs/services/song-list.resolver.ts | 2 +- .../modules/songs/services/song.service.ts | 1 + .../songs/song-list/song-list.component.ts | 22 +++++-------------- src/app/modules/songs/songs-routing.module.ts | 4 ++++ src/styles/styles.less | 15 +++++++------ 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5ba896e..8b9274c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,5 @@ -import {ChangeDetectionStrategy, Component, OnInit, inject} from '@angular/core'; +import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core'; import {fader} from './animations'; -import {ScrollService} from './services/scroll.service'; import {register} from 'swiper/element/bundle'; import {RouterOutlet} from '@angular/router'; import {NavigationComponent} from './widget-modules/components/application-frame/navigation/navigation.component'; @@ -14,8 +13,6 @@ import {NavigationComponent} from './widget-modules/components/application-frame imports: [RouterOutlet, NavigationComponent], }) export class AppComponent implements OnInit { - private scrollService = inject(ScrollService); - public constructor() { register(); } @@ -24,8 +21,4 @@ export class AppComponent implements OnInit { setTimeout(() => document.querySelector('#load-bg')?.classList.add('hidden'), 1000); setTimeout(() => document.querySelector('#load-bg')?.remove(), 5000); } - - public onScoll($event: {srcElement: {scrollTop: number}}): void { - this.scrollService.saveScrollPosition($event.srcElement.scrollTop); - } } diff --git a/src/app/modules/songs/services/song-data.service.ts b/src/app/modules/songs/services/song-data.service.ts index 2d25473..ecec4f6 100644 --- a/src/app/modules/songs/services/song-data.service.ts +++ b/src/app/modules/songs/services/song-data.service.ts @@ -11,13 +11,20 @@ export class SongDataService { private dbService = inject(DbService); private collection = 'songs'; - public list$: Observable = this.dbService.col$(this.collection).pipe( + private loadedList$: Observable = this.dbService.col$(this.collection).pipe( + shareReplay({ + bufferSize: 1, + refCount: false, // keep the listener alive after first subscription to avoid reloading on navigation + }) + ); + public list$: Observable = this.loadedList$.pipe( startWith([] as Song[]), // immediate empty emit keeps UI responsive while first snapshot arrives shareReplay({ bufferSize: 1, refCount: false, // keep the listener alive after first subscription to avoid reloading on navigation }) ); + public listLoaded$ = (): Observable => this.loadedList$; public read$ = (songId: string): Observable => this.dbService.doc$(this.collection + '/' + songId); public update$ = async (songId: string, data: Partial): Promise => await this.dbService.doc(this.collection + '/' + songId).update(data); diff --git a/src/app/modules/songs/services/song-list.resolver.ts b/src/app/modules/songs/services/song-list.resolver.ts index d1d0424..446bb41 100644 --- a/src/app/modules/songs/services/song-list.resolver.ts +++ b/src/app/modules/songs/services/song-list.resolver.ts @@ -12,6 +12,6 @@ export class SongListResolver { private songService = inject(SongService); public resolve(): Observable { - return this.songService.list$().pipe(take(1)); + return this.songService.listLoaded$().pipe(take(1)); } } diff --git a/src/app/modules/songs/services/song.service.ts b/src/app/modules/songs/services/song.service.ts index a80c907..338c803 100644 --- a/src/app/modules/songs/services/song.service.ts +++ b/src/app/modules/songs/services/song.service.ts @@ -26,6 +26,7 @@ export class SongService { public static LEGAL_TYPE: SongLegalType[] = ['open', 'allowed']; public list$ = (): Observable => this.songDataService.list$; //.pipe(tap(_ => (this.list = _))); + public listLoaded$ = (): Observable => this.songDataService.listLoaded$(); public read$ = (songId: string): Observable => this.songDataService.read$(songId); public read = (songId: string): Promise => firstValueFrom(this.read$(songId)); diff --git a/src/app/modules/songs/song-list/song-list.component.ts b/src/app/modules/songs/song-list/song-list.component.ts index a8e647d..402ac44 100644 --- a/src/app/modules/songs/song-list/song-list.component.ts +++ b/src/app/modules/songs/song-list/song-list.component.ts @@ -1,13 +1,11 @@ -import {ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject} from '@angular/core'; -import {SongService} from '../services/song.service'; +import {ChangeDetectionStrategy, Component, inject} from '@angular/core'; import {Song} from '../services/song'; import {map} from 'rxjs/operators'; import {combineLatest, Observable} from 'rxjs'; import {fade} from '../../../animations'; -import {RouterLink} from '@angular/router'; +import {ActivatedRoute, RouterLink} from '@angular/router'; import {filterSong} from '../../../services/filter.helper'; import {FilterValues} from './filter/filter-values'; -import {ScrollService} from '../../../services/scroll.service'; import {faBalanceScaleRight, faCheck, faPencilRuler} from '@fortawesome/free-solid-svg-icons'; import {TextRenderingService} from '../services/text-rendering.service'; import {FilterStoreService} from '../../../services/filter-store.service'; @@ -30,16 +28,15 @@ interface SongListItem extends Song { animations: [fade], imports: [ListHeaderComponent, FilterComponent, CardComponent, RouterLink, RoleDirective, FaIconComponent, AsyncPipe], }) -export class SongListComponent implements OnInit, OnDestroy { - private songService = inject(SongService); - private scrollService = inject(ScrollService); +export class SongListComponent { + private route = inject(ActivatedRoute); private textRenderingService = inject(TextRenderingService); private filterStore = inject(FilterStoreService); public anyFilterActive = false; public songs$: Observable = combineLatest([ this.filterStore.songFilter$, - this.songService.list$().pipe(map(songs => [...songs].sort((a, b) => a.number - b.number))), + this.route.data.pipe(map(data => (data['songs'] as Song[]).slice().sort((a, b) => a.number - b.number))), ]).pipe( map(([filter, songs]) => { this.anyFilterActive = this.checkIfFilterActive(filter); @@ -56,15 +53,6 @@ export class SongListComponent implements OnInit, OnDestroy { public faDraft = faPencilRuler; public faFinal = faCheck; - public ngOnInit(): void { - setTimeout(() => this.scrollService.restoreScrollPositionFor('songlist'), 100); - setTimeout(() => this.scrollService.restoreScrollPositionFor('songlist'), 300); - } - - public ngOnDestroy(): void { - this.scrollService.storeScrollPositionFor('songlist'); - } - public trackBy = (index: number, show: SongListItem) => show.id; private filter(song: Song, filter: FilterValues): boolean { diff --git a/src/app/modules/songs/songs-routing.module.ts b/src/app/modules/songs/songs-routing.module.ts index 30dc1cf..6ae4cef 100644 --- a/src/app/modules/songs/songs-routing.module.ts +++ b/src/app/modules/songs/songs-routing.module.ts @@ -5,12 +5,16 @@ import {SongListComponent} from './song-list/song-list.component'; import {EditComponent} from './song/edit/edit.component'; import {NewComponent} from './song/new/new.component'; import {EditSongGuard} from './song/edit/edit-song.guard'; +import {SongListResolver} from './services/song-list.resolver'; const routes: Routes = [ { path: '', component: SongListComponent, pathMatch: 'full', + resolve: { + songs: SongListResolver, + }, }, { path: 'new', diff --git a/src/styles/styles.less b/src/styles/styles.less index d225fc0..3f08beb 100644 --- a/src/styles/styles.less +++ b/src/styles/styles.less @@ -37,8 +37,7 @@ } html { - scroll-behavior: smooth; - overflow: hidden; + scroll-behavior: auto; } body { @@ -46,9 +45,15 @@ body { font-family: Roboto, "Helvetica Neue", sans-serif; font-size: 14px; color: var(--text); + position: relative; +} +body::before { + content: ''; + position: fixed; + inset: 0; background: linear-gradient(39deg, var(--bg-deep), var(--bg-mid), var(--bg-soft)); - overflow: auto; + z-index: -1; } p { @@ -103,10 +108,6 @@ body .cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing { } } -html, body { - height: 100vh; -} - body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif;