Scrolling Enhancements (#2417)

This commit is contained in:
Joe Milazzo 2023-11-08 16:11:50 -06:00 committed by GitHub
parent 8875bc3585
commit f4e8daf983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 51 additions and 71 deletions

View File

@ -21,8 +21,6 @@
"@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
"@iplab/ngx-file-upload": "^16.0.2",
"@lithiumjs/angular": "^7.3.0",
"@lithiumjs/ngx-virtual-scroll": "^0.3.0",
"@microsoft/signalr": "^7.0.12",
"@ng-bootstrap/ng-bootstrap": "^15.1.2",
"@ngneat/transloco": "^6.0.0",
@ -3716,33 +3714,6 @@
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
"dev": true
},
"node_modules/@lithiumjs/angular": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/@lithiumjs/angular/-/angular-7.3.0.tgz",
"integrity": "sha512-81nXyT9I2J+VpeFEDtOvfP4imlrLueoqFYBZR8PCrlY9cjDzgFAZBq7mCOLxOhi0xL5wF9hM0iDqlmI9LDct1Q==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": ">=11.0.0 <17.0.0",
"rxjs": ">=7.x.x",
"typescript": ">=4.1.0"
}
},
"node_modules/@lithiumjs/ngx-virtual-scroll": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@lithiumjs/ngx-virtual-scroll/-/ngx-virtual-scroll-0.3.0.tgz",
"integrity": "sha512-fYZR1S66c4ATg6mDVwJaZxsZ8rT/jcJ07b95x5sZVV7gtiDv7DDUCiMa4mtvj71fqMzcoeRe99G0FivVUwvZ0Q==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "8.x.x - 16.x.x",
"@angular/core": "8.x.x - 16.x.x",
"@lithiumjs/angular": ">=7.0.0",
"rxjs": "6.x.x - 7.x.x"
}
},
"node_modules/@microsoft/signalr": {
"version": "7.0.12",
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.12.tgz",

View File

@ -26,8 +26,6 @@
"@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
"@iplab/ngx-file-upload": "^16.0.2",
"@lithiumjs/angular": "^7.3.0",
"@lithiumjs/ngx-virtual-scroll": "^0.3.0",
"@microsoft/signalr": "^7.0.12",
"@ng-bootstrap/ng-bootstrap": "^15.1.2",
"@ngneat/transloco": "^6.0.0",

View File

@ -10,7 +10,7 @@ export interface Library {
lastScanned: string;
type: LibraryType;
folders: string[];
coverImage?: string;
coverImage?: string | null;
folderWatching: boolean;
includeInDashboard: boolean;
includeInRecommended: boolean;

View File

@ -61,7 +61,7 @@
<ng-template #jumpBar>
<div class="jump-bar">
<ng-container *ngFor="let jumpKey of jumpBarKeysToRender; let i = index;">
<button class="btn btn-link" [ngClass]="{'disabled': hasCustomSort()}" (click)="scrollTo(jumpKey)" [ngbTooltip]="jumpKey.size + ' Series'" placement="left">
<button class="btn btn-link" [ngClass]="{'disabled': hasCustomSort()}" (click)="scrollTo(jumpKey)" [ngbTooltip]="t('jumpkey-count', {count: jumpKey.size})" placement="left">
{{jumpKey.title}}
</button>
</ng-container>

View File

@ -39,6 +39,9 @@ import {TranslocoDirective} from "@ngneat/transloco";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {SeriesFilterV2} from "../../_models/metadata/v2/series-filter-v2";
const ANIMATION_TIME_MS = 0;
@Component({
selector: 'app-card-detail-layout',
standalone: true,
@ -49,6 +52,13 @@ import {SeriesFilterV2} from "../../_models/metadata/v2/series-filter-v2";
})
export class CardDetailLayoutComponent implements OnInit, OnChanges {
private readonly filterUtilityService = inject(FilterUtilitiesService);
protected readonly utilityService = inject(UtilityService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly jumpbarService = inject(JumpbarService);
private readonly router = inject(Router);
private readonly scrollService = inject(ScrollService);
@Input() header: string = '';
@Input() isLoading: boolean = false;
@Input() items: any[] = [];
@ -89,7 +99,6 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
@ViewChild(VirtualScrollerComponent) private virtualScroller!: VirtualScrollerComponent;
private readonly filterUtilityService = inject(FilterUtilitiesService);
filter: SeriesFilterV2 = this.filterUtilityService.createSeriesV2Filter();
libraries: Array<FilterItem<Library>> = [];
@ -97,15 +106,9 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
hasResumedJumpKey: boolean = false;
bufferAmount: number = 1;
protected readonly Breakpoint = Breakpoint;
get Breakpoint() {
return Breakpoint;
}
constructor(public utilityService: UtilityService,
@Inject(DOCUMENT) private document: Document, private cdRef: ChangeDetectorRef,
private jumpbarService: JumpbarService, private router: Router, private scrollService: ScrollService) {
}
constructor(@Inject(DOCUMENT) private document: Document) {}
@HostListener('window:resize', ['$event'])
@HostListener('window:orientationchange', ['$event'])
@ -136,6 +139,8 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
this.virtualScroller.refresh();
});
}
}
@ -143,6 +148,8 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
this.jumpBarKeysToRender = [...this.jumpBarKeys];
this.resizeJumpBar();
// TODO: I wish I had signals so I can tap into when isLoading is false and trigger the scroll code
// Don't resume jump key when there is a custom sort order, as it won't work
if (!this.hasCustomSort()) {
if (!this.hasResumedJumpKey && this.jumpBarKeysToRender.length > 0) {
@ -154,14 +161,15 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
this.hasResumedJumpKey = true;
setTimeout(() => this.scrollTo(keys[0]), 100);
}
} else {
// I will come back and refactor this to work
// const scrollPosition = this.jumpbarService.getResumePosition(this.router.url);
// console.log('scroll position: ', scrollPosition);
// if (scrollPosition > 0) {
// setTimeout(() => this.virtualScroller.scrollToIndex(scrollPosition, true, 0, 1000), 100);
// }
}
// else {
// // I will come back and refactor this to work
// // const scrollPosition = this.jumpbarService.getResumePosition(this.router.url);
// // console.log('scroll position: ', scrollPosition);
// // if (scrollPosition > 0) {
// // setTimeout(() => this.virtualScroller.scrollToIndex(scrollPosition, true, 0, 1000), 100);
// // }
// }
}
hasCustomSort() {
@ -192,10 +200,11 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
targetIndex += this.jumpBarKeys[i].size;
}
this.virtualScroller.scrollToIndex(targetIndex, true, 0, 1000);
this.virtualScroller.scrollToIndex(targetIndex, true, 0, ANIMATION_TIME_MS);
this.jumpbarService.saveResumeKey(this.router.url, jumpKey.key);
// TODO: This doesn't work, we need the offset from virtual scroller
this.jumpbarService.saveScrollOffset(this.router.url, this.scrollService.scrollPosition);
this.cdRef.markForCheck();
}

View File

@ -243,7 +243,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
this.pagination = series.pagination;
this.jumpbarKeys = this.jumpbarService.getJumpKeys(this.series, (series: Series) => series.name);
this.isLoading = false;
window.scrollTo(0, 0);
this.cdRef.markForCheck();
});
}

View File

@ -281,10 +281,8 @@ export class LibraryDetailComponent implements OnInit {
this.pagination = series.pagination;
this.loadingSeries = false;
this.cdRef.markForCheck();
window.scrollTo(0, 0);
});
}
trackByIdentity = (index: number, item: Series) => `${item.id}_${item.name}_${item.localizedName}_${item.pagesRead}`;
protected readonly undefined = undefined;
}

View File

@ -18,7 +18,6 @@ import {BulkSelectionService} from "../../../cards/bulk-selection.service";
import {SeriesCardComponent} from "../../../cards/series-card/series-card.component";
import {FormsModule} from "@angular/forms";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {NgxVirtualScrollModule} from "@lithiumjs/ngx-virtual-scroll";
export interface IndexUpdateEvent {
fromPosition: number;
@ -39,8 +38,7 @@ export interface ItemRemoveEvent {
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIf, VirtualScrollerModule, NgFor, NgTemplateOutlet, CdkDropList, CdkDrag,
CdkDragHandle, TranslocoDirective, NgClass, SeriesCardComponent, FormsModule,
NgxVirtualScrollModule, NgxVirtualScrollModule]
CdkDragHandle, TranslocoDirective, NgClass, SeriesCardComponent, FormsModule]
})
export class DraggableOrderedListComponent {

View File

@ -115,7 +115,6 @@ export class ReadingListsComponent implements OnInit {
this.loadingLists = false;
this.actions = {};
this.lists.forEach(l => this.actions[l.id] = this.getActions(l));
window.scrollTo(0, 0);
this.cdRef.markForCheck();
});
}

View File

@ -777,8 +777,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
const modalRef = this.modalService.open(EditSeriesModalComponent, { size: 'xl' });
modalRef.componentInstance.series = this.series;
modalRef.closed.subscribe((closeResult: {success: boolean, series: Series, coverImageUpdate: boolean}) => {
window.scrollTo(0, 0);
if (closeResult.success) {
window.scrollTo(0, 0);
this.loadSeries(this.seriesId);
}

View File

@ -272,12 +272,19 @@ export class DownloadService {
private save(blob: Blob, filename: string) {
const saveLink = document.createElement('a');
if (saveLink.href) {
URL.revokeObjectURL(saveLink.href);
}
saveLink.href = URL.createObjectURL(blob);
saveLink.style.display = 'none';
document.body.appendChild(saveLink);
const url = URL.createObjectURL(blob);
saveLink.href = url;
saveLink.download = filename;
saveLink.dispatchEvent( new MouseEvent( 'click' ) );
// Trigger the click event
saveLink.click();
// Cleanup
URL.revokeObjectURL(url);
document.body.removeChild(saveLink);
}
}

View File

@ -80,7 +80,7 @@
<p *ngIf="isAddLibrary" class="alert alert-secondary" role="alert">{{t('cover-description')}}</p>
<p>{{t('cover-description-extra')}}</p>
<app-cover-image-chooser [(imageUrls)]="imageUrls" (imageSelected)="updateCoverImageIndex($event)"
(selectedBase64Url)="applyCoverImage($event)" [showReset]="library.coverImage !== undefined "
(selectedBase64Url)="applyCoverImage($event)" [showReset]="library.coverImage !== null"
(resetClicked)="resetCoverImage()"></app-cover-image-chooser>
</ng-template>
</li>

View File

@ -55,9 +55,10 @@ enum StepID {
})
export class LibrarySettingsModalComponent implements OnInit {
@Input({required: true}) library!: Library;
private readonly destroyRef = inject(DestroyRef);
@Input({required: true}) library!: Library;
active = TabID.General;
imageUrls: Array<string> = [];
@ -81,8 +82,8 @@ export class LibrarySettingsModalComponent implements OnInit {
isAddLibrary = false;
setupStep = StepID.General;
get Breakpoint() { return Breakpoint; }
get TabID() { return TabID; }
protected readonly Breakpoint = Breakpoint;
protected readonly TabID = TabID;
constructor(public utilityService: UtilityService, private uploadService: UploadService, private modalService: NgbModal,
private settingService: SettingsService, public modal: NgbActiveModal, private confirmService: ConfirmService,

View File

@ -176,7 +176,6 @@ export class WantToReadComponent implements OnInit, AfterContentChecked {
this.pagination = paginatedList.pagination;
this.jumpbarKeys = this.jumpbarService.getJumpKeys(this.series, (series: Series) => series.name);
this.isLoading = false;
window.scrollTo(0, 0);
this.cdRef.markForCheck();
});
}

View File

@ -892,7 +892,8 @@
},
"card-detail-layout": {
"total-items": "{{count}} total items"
"total-items": "{{count}} total items",
"jumpkey-count": "{{count}} Series"
},
"card-item": {