mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Include startup icon
Fix UI layout
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
<div fxLayout="column" *ngIf="connecting()" class="overlay loading" fxLayoutAlign="center center">
|
||||
<mat-spinner></mat-spinner>
|
||||
<h2>Loading...</h2>
|
||||
</div>
|
||||
<div fxLayout="column" *ngIf="noConnectionState()" class="overlay no-connection" fxLayoutAlign="center center">
|
||||
<mat-icon class="overlay-icon">error</mat-icon>
|
||||
<h2>The app seems to be shutdown</h2>
|
||||
</div>
|
||||
<div [ngClass]="{ electron: electronService.isElectronApp }" fxLayout="column" id="app-container">
|
||||
<div id="app-header" fxFlex="nogrow">
|
||||
<app-toolbar></app-toolbar>
|
||||
<ng-container *ngIf="appState | async as state">
|
||||
<div *ngIf="state === 'CONNECTING'" class="overlay loading" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="sk-chase">
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app-body" fxFlex="grow">
|
||||
<router-outlet></router-outlet>
|
||||
<div *ngIf="state === 'DISCONNECTED'" class="overlay no-connection" fxLayout="column" fxLayoutAlign="center center">
|
||||
<mat-icon class="overlay-icon">error</mat-icon>
|
||||
<h2>Error connecting to daemon, try restarting the application</h2>
|
||||
</div>
|
||||
<div *ngIf="state === 'CONNECTED'" [ngClass]="{ electron: electron | async }" fxLayout="column" id="app-container">
|
||||
<div fxFlex="nogrow" id="app-header">
|
||||
<app-toolbar></app-toolbar>
|
||||
</div>
|
||||
<div fxFlex="grow" id="app-body" style="margin-bottom: 20px;">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-status-bar style="width: 100%"></app-status-bar>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,13 +1,76 @@
|
||||
#title {
|
||||
user-select: none
|
||||
}
|
||||
|
||||
.overlay-icon {
|
||||
font-size: 128px;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
font-size: 64px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.no-connection {
|
||||
color: #eb6060;
|
||||
.no-connection, .loading {
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.sk-chase {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
position: relative;
|
||||
animation: sk-chase 2.5s infinite linear both;
|
||||
}
|
||||
|
||||
.sk-chase-dot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: sk-chase-dot 2.0s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.sk-chase-dot:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 25%;
|
||||
height: 25%;
|
||||
background-color: #fff;
|
||||
border-radius: 100%;
|
||||
animation: sk-chase-dot-before 2.0s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.sk-chase-dot:nth-child(1) { animation-delay: -1.1s; }
|
||||
.sk-chase-dot:nth-child(2) { animation-delay: -1.0s; }
|
||||
.sk-chase-dot:nth-child(3) { animation-delay: -0.9s; }
|
||||
.sk-chase-dot:nth-child(4) { animation-delay: -0.8s; }
|
||||
.sk-chase-dot:nth-child(5) { animation-delay: -0.7s; }
|
||||
.sk-chase-dot:nth-child(6) { animation-delay: -0.6s; }
|
||||
.sk-chase-dot:nth-child(1):before { animation-delay: -1.1s; }
|
||||
.sk-chase-dot:nth-child(2):before { animation-delay: -1.0s; }
|
||||
.sk-chase-dot:nth-child(3):before { animation-delay: -0.9s; }
|
||||
.sk-chase-dot:nth-child(4):before { animation-delay: -0.8s; }
|
||||
.sk-chase-dot:nth-child(5):before { animation-delay: -0.7s; }
|
||||
.sk-chase-dot:nth-child(6):before { animation-delay: -0.6s; }
|
||||
|
||||
@keyframes sk-chase {
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes sk-chase-dot {
|
||||
80%, 100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes sk-chase-dot-before {
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
} 100%, 0% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,47 @@
|
||||
import { AppService } from './app.service';
|
||||
import { ClipboardService } from './clipboard.service';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { Component, OnInit, OnDestroy, NgZone, AfterViewInit, Renderer2 } from '@angular/core';
|
||||
import { Component, OnDestroy, AfterViewInit, Renderer2, ChangeDetectionStrategy, NgZone } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Subscription, BehaviorSubject, Subject } from 'rxjs';
|
||||
import { WsConnectionService, WSState } from './ws-connection.service';
|
||||
import { WsHandler } from './ws-handler';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
styleUrls: ['./app.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
export class AppComponent implements OnDestroy, AfterViewInit {
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private dialog: MatDialog,
|
||||
private ws: WsConnectionService,
|
||||
public electronService: ElectronService,
|
||||
private clipboardService: ClipboardService,
|
||||
private ngZone: NgZone,
|
||||
private appService: AppService,
|
||||
private renderer: Renderer2
|
||||
) {}
|
||||
|
||||
subscriptions: Subscription[] = [];
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
currentState: WSState;
|
||||
themeLoaded = false;
|
||||
|
||||
noConnectionState(): boolean {
|
||||
return this.currentState === WSState.CLOSE || this.currentState === WSState.ERROR;
|
||||
private renderer: Renderer2,
|
||||
private ngZone: NgZone
|
||||
) {
|
||||
this.electron = new BehaviorSubject(electronService.isElectronApp);
|
||||
}
|
||||
|
||||
connecting(): boolean {
|
||||
return (this.currentState === WSState.INIT || this.currentState === WSState.CONNECTING) && !this.themeLoaded ;
|
||||
}
|
||||
private subscriptions: Subscription[] = [];
|
||||
appState: Subject<string> = new BehaviorSubject('CONNECTING');
|
||||
electron: Subject<boolean>;
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.appService.renderer = this.renderer;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.currentState = WSState.INIT;
|
||||
this.subscriptions.push(this.ws.state.subscribe(e => {
|
||||
this.currentState = e;
|
||||
if (this.currentState === WSState.CLOSE || this.currentState === WSState.ERROR) {
|
||||
this.dialog.closeAll();
|
||||
} else if (this.currentState === WSState.OPEN) {
|
||||
this.subscriptions.push(this.ws.state.subscribe(wsState => {
|
||||
if (wsState === WSState.CLOSE || wsState === WSState.ERROR) {
|
||||
setTimeout(() => this.ngZone.run(() => {
|
||||
this.dialog.closeAll();
|
||||
this.appState.next('DISCONNECTED');
|
||||
}), 500);
|
||||
} else if (wsState === WSState.OPEN) {
|
||||
this.clipboardService.init();
|
||||
this.appService
|
||||
.loadTheme()
|
||||
.subscribe(() => this.ngZone.run(() => this.themeLoaded = true));
|
||||
.subscribe(() => this.ngZone.run(() => this.appState.next('CONNECTED')));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatMenuModule,
|
||||
MatDialogModule,
|
||||
MatCardModule,
|
||||
@@ -28,7 +27,6 @@ import {
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatMenuModule,
|
||||
MatDialogModule,
|
||||
MatCardModule,
|
||||
|
||||
@@ -1,110 +1,3 @@
|
||||
<!-- <div class="container" style="height: 100%; background-color: white">
|
||||
<div
|
||||
[ngClass]="{
|
||||
error: postState.status === 'ERROR',
|
||||
downloading: postState.status === 'DOWNLOADING' || postState.status === 'PARTIAL',
|
||||
complete: postState.status === 'COMPLETE',
|
||||
stopped: postState.status === 'STOPPED'
|
||||
}"
|
||||
class="progress-bar-back"
|
||||
style="position: relative; height: 100%; max-height: 48px;"
|
||||
>
|
||||
<div
|
||||
class="progress-foreground"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="space-between"
|
||||
style="background-color: transparent; width: 100%; height: 100%;"
|
||||
>
|
||||
<div class="progress-bar" color="accent" style="position: absolute; width: 100%; top: 37px; padding: 0 50px">
|
||||
<mat-progress-bar [value]="postState.progress" class="example-margin" color="primary" mode="determinate">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
<span fxLayout="row">
|
||||
<span (click)="toggleExpand()" class="chevron">
|
||||
<button *ngIf="expanded; else notExpanded" mat-icon-button>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<ng-template #notExpanded>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</span>
|
||||
<span>{{ postState.title }}</span>
|
||||
<span class="filler" fxFlex="grow"></span>
|
||||
</span>
|
||||
<span>
|
||||
<span class="progress-percentage">{{ postState.done + '/' + postState.total }}</span>
|
||||
<button [matMenuTriggerFor]="menu" mat-icon-button>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button
|
||||
(click)="restart()"
|
||||
*ngIf="
|
||||
(postState.status === 'COMPLETE' && postState.progress !== 100) ||
|
||||
postState.status === 'ERROR' ||
|
||||
postState.status === 'STOPPED'
|
||||
"
|
||||
mat-menu-item
|
||||
>
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
<span>Start</span>
|
||||
</button>
|
||||
<button
|
||||
(click)="stop()"
|
||||
*ngIf="
|
||||
postState.status === 'DOWNLOADING' || postState.status === 'PARTIAL' || postState.status === 'PENDING'
|
||||
"
|
||||
mat-menu-item
|
||||
>
|
||||
<ng-container>
|
||||
<mat-icon>stop</mat-icon>
|
||||
<span>Stop</span>
|
||||
</ng-container>
|
||||
</button>
|
||||
<button (click)="seeDetails()" mat-menu-item>
|
||||
<mat-icon>list</mat-icon>
|
||||
<span>Files</span>
|
||||
</button>
|
||||
<button (click)="remove()" mat-menu-item>
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span>Remove</span>
|
||||
</button>
|
||||
<button (click)="open()" *ngIf="electronService.isElectronApp" mat-menu-item>
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
<span>Download Location</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<section *ngIf="expanded">
|
||||
<div class="details table">
|
||||
<div class="row" style="display: table-row">
|
||||
<h4 class="cell attribute">Status:</h4>
|
||||
<p class="cell value">
|
||||
{{ postState.status | titlecase }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="row" style="display: table-row">
|
||||
<h4 class="cell attribute">Post URL:</h4>
|
||||
<p class="cell value">
|
||||
<a (click)="goTo()" [appPreview]="postState.previews" href="javascript:void(0)"
|
||||
>https://vipergirls/threads/?p={{ postState.postId }}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div style="display: table-row">
|
||||
<h4 class="cell attribute">Host:</h4>
|
||||
<p class="cell value">
|
||||
{{ postState.hosts }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div> -->
|
||||
|
||||
<div class="container" style="height: 100%; background-color: white">
|
||||
<ng-container *ngIf="postState$ | async as postState">
|
||||
<div
|
||||
@@ -124,16 +17,14 @@
|
||||
fxLayoutGap="5px"
|
||||
style="background-color: transparent; width: 100%; height: 100%;"
|
||||
>
|
||||
<div class="checkbox" style="display: inline-block; width: 5px; height: 100%;"></div>
|
||||
<span fxFlex="grow" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span
|
||||
><a (click)="goTo()" [appPreview]="postState.previews" href="javascript:void(0)">{{
|
||||
postState.title
|
||||
}}</a></span
|
||||
<div class="checkbox" fxFlex="nogrow" style="width: 5px; height: 100%;"></div>
|
||||
<span class="row-text" fxFlex="grow" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="20px">
|
||||
<span [title]="postState.title">{{ postState.title }}</span>
|
||||
<span [title]="postState.done + '/' + postState.total + ' from ' + postState.hosts"
|
||||
>{{ postState.done + '/' + postState.total }} from {{ postState.hosts }}</span
|
||||
>
|
||||
<span>{{postState.done + '/' + postState.total}} done from {{postState.hosts}}</span>
|
||||
</span>
|
||||
<button [matMenuTriggerFor]="menu" mat-icon-button>
|
||||
<button [matMenuTriggerFor]="menu" fxFlex="nogrow" mat-icon-button>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
.checkbox {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.row-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.row-text > span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -38,9 +38,11 @@ export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
selected: EventEmitter<number> = new EventEmitter();
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.selected.emit(0);
|
||||
this.globalState.emit(new GlobalState(0, 0, 0, 0));
|
||||
this.downloadSpeed.emit(new DownloadSpeed('0 B'));
|
||||
this.ngZone.run(() => {
|
||||
this.selected.emit(0);
|
||||
this.globalState.emit(new GlobalState(0, 0, 0, 0));
|
||||
this.downloadSpeed.emit(new DownloadSpeed('0 B'));
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -63,7 +65,9 @@ export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
handler.send(new WSMessage(CMD.GLOBAL_STATE_SUB.toString()));
|
||||
handler.send(new WSMessage(CMD.SPEED_SUB.toString()));
|
||||
});
|
||||
this.subscriptions.push(this.selectionService.selected$.subscribe(selected => this.selected.emit(selected.length)));
|
||||
this.subscriptions.push(
|
||||
this.selectionService.selected$.subscribe(selected => this.ngZone.run(() => this.selected.emit(selected.length)))
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@@ -210,8 +210,10 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.disableSelection.next(true);
|
||||
this.loggedUser.emit(new LoggedUser(null));
|
||||
this.ngZone.run(() => {
|
||||
this.disableSelection.next(true);
|
||||
this.loggedUser.emit(new LoggedUser(null));
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -229,7 +231,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
this.selectionService.selected$.subscribe(selected => {
|
||||
this.selected = selected;
|
||||
this.disableSelection.next(this.selected.length === 0);
|
||||
this.ngZone.run(() => this.disableSelection.next(this.selected.length === 0));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -75,26 +75,6 @@ app-root {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.loading {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.no-connection {
|
||||
background-color: whitesmoke !important;
|
||||
}
|
||||
|
||||
#app-container {
|
||||
position: fixed;
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user