Skip to content

Commit

Permalink
UI updates (#922)
Browse files Browse the repository at this point in the history
* Fixed download spinner in player component

* Downloads UI is more mobile friendly (#905)

* Code cleanup

* Fixed size of actions in home screen downloads

* Errored downloads now display their stage as "Error" in the UI

* Moved personal settings from about dialog to profile dialog

* Profile dialog can now be opened without logging in/without multi-user mode

* Fixed issue where archive dialog could be accessed from anywhere

* Misc internationalization improvements

* Combined download stage and download progress columns

* Added back loading spinner to download actions

* Adjusted thresholds for consolidating download action buttons

* Implemented virtual scrolling for notifications (helps if many notifications exist)

* Fixed minor console error
  • Loading branch information
Tzahi12345 authored May 26, 2023
1 parent ba0de7f commit 2c61260
Show file tree
Hide file tree
Showing 23 changed files with 646 additions and 529 deletions.
4 changes: 2 additions & 2 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
</mat-menu>
<button [matMenuTriggerFor]="menuSettings" mat-icon-button><mat-icon>more_vert</mat-icon></button>
<mat-menu #menuSettings="matMenu">
<button class="top-menu-button" (click)="openProfileDialog()" *ngIf="postsService.isLoggedIn" mat-menu-item>
<button class="top-menu-button" (click)="openProfileDialog()" mat-menu-item>
<mat-icon>person</mat-icon>
<span i18n="Profile menu label">Profile</span>
</button>
<button class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item>
<button *ngIf="!postsService.config?.Advanced.multi_user_mode || postsService.isLoggedIn" class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item>
<mat-icon>topic</mat-icon>
<span i18n="Archives menu label">Archives</span>
</button>
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { MatBadgeModule } from '@angular/material/badge';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { TextFieldModule } from '@angular/cdk/text-field';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
Expand Down Expand Up @@ -189,6 +190,7 @@ registerLocaleData(es, 'es');
DragDropModule,
ClipboardModule,
TextFieldModule,
ScrollingModule,
NgxFileDropModule,
AvatarModule,
ContentLoaderModule,
Expand Down
58 changes: 32 additions & 26 deletions src/app/components/downloads/downloads.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

<!-- Title Column -->
<ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element">
<mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 2"> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element" style="flex: 2">
<span class="one-line" [matTooltip]="element.title ? element.title : null">
{{element.title}}
</span>
Expand All @@ -31,41 +31,47 @@
</mat-cell>
</ng-container>

<!-- Stage Column -->
<ng-container matColumnDef="step_index">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Stage">Stage</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> {{STEP_INDEX_TO_LABEL[element.step_index]}} </mat-cell>
</ng-container>

<!-- Progress Column -->
<ng-container matColumnDef="percent_complete">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element">
<ng-container *ngIf="!element.error && element.step_index !== 2">
{{STEP_INDEX_TO_LABEL[element.step_index]}}
</ng-container>
<ng-container *ngIf="!element.error && element.step_index === 2">
<ng-container *ngIf="element.percent_complete">
{{+(element.percent_complete) > 100 ? '100' : element.percent_complete}}%
{{+(element.percent_complete) > 100 ? '100' : element.percent_complete}}%
</ng-container>
<ng-container *ngIf="!element.percent_complete">
N/A
N/A
</ng-container>
</ng-container>
<ng-container *ngIf="element.error" i18n="Error">Error</ng-container>
</mat-cell>
</ng-container>

<!-- Actions Column -->
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element">
<div>
<ng-container *ngIf="!element.finished">
<button (click)="pauseDownload(element.uid)" *ngIf="!element.paused || !element.finished_step" [disabled]="element.paused && !element.finished_step" mat-icon-button matTooltip="Pause" i18n-matTooltip="Pause"><mat-spinner [diameter]="28" *ngIf="element.paused && !element.finished_step" class="icon-button-spinner"></mat-spinner><mat-icon>pause</mat-icon></button>
<button (click)="resumeDownload(element.uid)" *ngIf="element.paused && element.finished_step" mat-icon-button matTooltip="Resume" i18n-matTooltip="Resume"><mat-icon>play_arrow</mat-icon></button>
<button *ngIf="false && !element.paused" (click)="cancelDownload(element.uid)" mat-icon-button matTooltip="Cancel" i18n-matTooltip="Cancel"><mat-icon>cancel</mat-icon></button>
<mat-header-cell *matHeaderCellDef [ngStyle]="{flex: actionsFlex}"> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element" [ngStyle]="{flex: actionsFlex}">
<div *ngIf="!minimizeButtons">
<ng-container *ngFor="let downloadAction of downloadActions">
<span class="button-span">
<mat-spinner [diameter]="28" *ngIf="downloadAction.loading && downloadAction.loading(element)" class="icon-button-spinner"></mat-spinner>
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" [matTooltip]="downloadAction.tooltip" mat-icon-button><mat-icon>{{downloadAction.icon}}</mat-icon></button>
</span>
</ng-container>
<ng-container *ngIf="element.finished">
<button *ngIf="!element.error" (click)="watchContent(element)" mat-icon-button matTooltip="Watch content" i18n-matTooltip="Watch content"><mat-icon>smart_display</mat-icon></button>
<button *ngIf="element.error" (click)="showError(element)" mat-icon-button matTooltip="Show error" i18n-matTooltip="Show error"><mat-icon>warning</mat-icon></button>
<button (click)="restartDownload(element.uid)" mat-icon-button matTooltip="Restart" i18n-matTooltip="Restart"><mat-icon>restart_alt</mat-icon></button>
</ng-container>
<button *ngIf="element.finished || element.paused" (click)="clearDownload(element.uid)" mat-icon-button matTooltip="Clear" i18n-matTooltip="Clear"><mat-icon>delete</mat-icon></button>
</div>
<div *ngIf="minimizeButtons">
<button [matMenuTriggerFor]="download_actions" mat-icon-button><mat-icon>more_vert</mat-icon></button>
<mat-menu #download_actions="matMenu">
<ng-container *ngFor="let downloadAction of downloadActions">
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" mat-menu-item>
<mat-icon>{{downloadAction.icon}}</mat-icon>
<span>{{downloadAction.tooltip}}</span>
</button>
</ng-container>
</mat-menu>
</div>
</mat-cell>
</ng-container>
Expand All @@ -80,9 +86,9 @@
</mat-paginator>
</div>
<div *ngIf="!uids" class="downloads-action-button-div">
<button [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
<button style="margin-left: 10px;" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
<button color="warn" style="margin-left: 10px;" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button>
<button class="downloads-action-button" [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
<button class="downloads-action-button" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
<button class="downloads-action-button" color="warn" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button>
</div>
</div>

Expand Down
14 changes: 11 additions & 3 deletions src/app/components/downloads/downloads.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ mat-header-cell, mat-cell {

.icon-button-spinner {
position: absolute;
top: 7px;
left: 6px;
top: -13px;
left: 10px;
}

.button-span {
position: relative;;
}

.downloads-action-button-div {
margin-top: 10px;
margin-left: 5px;
}

.downloads-action-button {
margin-top: 10px;
margin-right: 10px;
}

.rounded-top {
border-radius: 16px 16px 0px 0px !important;
}
Expand Down
139 changes: 102 additions & 37 deletions src/app/components/downloads/downloads.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild, Input, EventEmitter } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, Input, EventEmitter, HostListener } from '@angular/core';
import { PostsService } from 'app/posts.services';
import { trigger, transition, animateChild, stagger, query, style, animate } from '@angular/animations';
import { Router } from '@angular/router';
Expand All @@ -13,31 +13,7 @@ import { Download } from 'api-types';
@Component({
selector: 'app-downloads',
templateUrl: './downloads.component.html',
styleUrls: ['./downloads.component.scss'],
animations: [
// nice stagger effect when showing existing elements
trigger('list', [
transition(':enter', [
// child animation selector + stagger
query('@items',
stagger(100, animateChild()), { optional: true }
)
]),
]),
trigger('items', [
// cubic-bezier for a tiny bouncing feel
transition(':enter', [
style({ transform: 'scale(0.5)', opacity: 0 }),
animate('500ms cubic-bezier(.8,-0.6,0.2,1.5)',
style({ transform: 'scale(1)', opacity: 1 }))
]),
transition(':leave', [
style({ transform: 'scale(1)', opacity: 1, height: '*' }),
animate('1s cubic-bezier(.8,-0.6,0.2,1.5)',
style({ transform: 'scale(0.5)', opacity: 0, height: '0px', margin: '0px' }))
]),
])
],
styleUrls: ['./downloads.component.scss']
})
export class DownloadsComponent implements OnInit, OnDestroy {

Expand All @@ -62,13 +38,79 @@ export class DownloadsComponent implements OnInit, OnDestroy {
3: $localize`Complete`
}

displayedColumns: string[] = ['timestamp_start', 'title', 'step_index', 'sub_name', 'percent_complete', 'actions'];
actionsFlex = 2;
minimizeButtons = false;
displayedColumnsBig: string[] = ['timestamp_start', 'title', 'sub_name', 'percent_complete', 'actions'];
displayedColumnsSmall: string[] = ['title', 'percent_complete', 'actions'];
displayedColumns: string[] = this.displayedColumnsBig;
dataSource = null; // new MatTableDataSource<Download>();

// The purpose of this is to reduce code reuse for displaying these actions as icons or in a menu
downloadActions: DownloadAction[] = [
{
tooltip: $localize`Watch content`,
action: (download: Download) => this.watchContent(download),
show: (download: Download) => download.finished && !download.error,
icon: 'smart_display'
},
{
tooltip: $localize`Show error`,
action: (download: Download) => this.showError(download),
show: (download: Download) => download.finished && !!download.error,
icon: 'warning'
},
{
tooltip: $localize`Restart`,
action: (download: Download) => this.restartDownload(download),
show: (download: Download) => download.finished,
icon: 'restart_alt'
},
{
tooltip: $localize`Pause`,
action: (download: Download) => this.pauseDownload(download),
show: (download: Download) => !download.finished && (!download.paused || !download.finished_step),
icon: 'pause',
loading: (download: Download) => download.paused && !download.finished_step
},
{
tooltip: $localize`Resume`,
action: (download: Download) => this.resumeDownload(download),
show: (download: Download) => !download.finished && download.paused && download.finished_step,
icon: 'play_arrow'
},
{
tooltip: $localize`Resume`,
action: (download: Download) => this.resumeDownload(download),
show: (download: Download) => !download.finished && download.paused && download.finished_step,
icon: 'play_arrow'
},
{
tooltip: $localize`Cancel`,
action: (download: Download) => this.cancelDownload(download),
show: (download: Download) => false && !download.finished && !download.paused, // TODO: add possibility to cancel download
icon: 'cancel'
},
{
tooltip: $localize`Clear`,
action: (download: Download) => this.clearDownload(download),
show: (download: Download) => download.finished || download.paused,
icon: 'delete'
}
]

downloads_retrieved = false;

innerWidth: number;

@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;

@HostListener('window:resize', ['$event'])
onResize(): void {
this.innerWidth = window.innerWidth;
this.recalculateColumns();
}

sort_downloads = (a: Download, b: Download): number => {
const result = b.timestamp_start - a.timestamp_start;
return result;
Expand All @@ -77,6 +119,10 @@ export class DownloadsComponent implements OnInit, OnDestroy {
constructor(public postsService: PostsService, private router: Router, private dialog: MatDialog, private clipboard: Clipboard) { }

ngOnInit(): void {
// Remove sub name as it's not necessary for one-off downloads
if (this.uids) this.displayedColumnsBig = this.displayedColumnsBig.filter(col => col !== 'sub_name');
this.innerWidth = window.innerWidth;
this.recalculateColumns();
if (this.postsService.initialized) {
this.getCurrentDownloadsRecurring();
} else {
Expand Down Expand Up @@ -164,8 +210,8 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}

pauseDownload(download_uid: string): void {
this.postsService.pauseDownload(download_uid).subscribe(res => {
pauseDownload(download: Download): void {
this.postsService.pauseDownload(download['uid']).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar($localize`Failed to pause download! See server logs for more info.`);
}
Expand All @@ -180,8 +226,8 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}

resumeDownload(download_uid: string): void {
this.postsService.resumeDownload(download_uid).subscribe(res => {
resumeDownload(download: Download): void {
this.postsService.resumeDownload(download['uid']).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar($localize`Failed to resume download! See server logs for more info.`);
}
Expand All @@ -196,8 +242,8 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}

restartDownload(download_uid: string): void {
this.postsService.restartDownload(download_uid).subscribe(res => {
restartDownload(download: Download): void {
this.postsService.restartDownload(download['uid']).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar($localize`Failed to restart download! See server logs for more info.`);
} else {
Expand All @@ -208,16 +254,16 @@ export class DownloadsComponent implements OnInit, OnDestroy {
});
}

cancelDownload(download_uid: string): void {
this.postsService.cancelDownload(download_uid).subscribe(res => {
cancelDownload(download: Download): void {
this.postsService.cancelDownload(download['uid']).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar($localize`Failed to cancel download! See server logs for more info.`);
}
});
}

clearDownload(download_uid: string): void {
this.postsService.clearDownload(download_uid).subscribe(res => {
clearDownload(download: Download): void {
this.postsService.clearDownload(download['uid']).subscribe(res => {
if (!res['success']) {
this.postsService.openSnackBar($localize`Failed to pause download! See server logs for more info.`);
}
Expand Down Expand Up @@ -257,6 +303,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
}

showError(download: Download): void {
console.log(download)
const copyToClipboardEmitter = new EventEmitter<boolean>();
this.dialog.open(ConfirmDialogComponent, {
data: {
Expand All @@ -276,4 +323,22 @@ export class DownloadsComponent implements OnInit, OnDestroy {
}
});
}

recalculateColumns() {
if (this.innerWidth < 650) this.displayedColumns = this.displayedColumnsSmall;
else this.displayedColumns = this.displayedColumnsBig;

this.actionsFlex = this.uids || this.innerWidth < 800 ? 1 : 2;

if (this.innerWidth < 800 && !this.uids || this.innerWidth < 1100 && this.uids) this.minimizeButtons = true;
else this.minimizeButtons = false;
}
}

interface DownloadAction {
tooltip: string,
action: (download: Download) => void,
show: (download: Download) => boolean,
icon: string,
loading?: (download: Download) => boolean
}
Loading

0 comments on commit 2c61260

Please sign in to comment.