drives page

This commit is contained in:
Aaron Greenspan
2021-01-06 20:24:19 -07:00
committed by Aiden McClelland
parent 31318687bf
commit 0cd2a32b24
16 changed files with 173 additions and 5 deletions

View File

@@ -29,6 +29,11 @@
"glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg"
},
{
"glob": "**/*.svg",
"input": "src/assets/icon",
"output": "./svg"
}
],
"styles": [

View File

@@ -33,6 +33,11 @@ const routes: Routes = [
canActivateChild: [AuthGuard],
loadChildren: () => import('./pages/apps-routes/apps-routing.module').then(m => m.AppsRoutingModule),
},
{
path: 'drives',
canActivate: [AuthGuard],
loadChildren: () => import('./pages/server-routes/external-drives/external-drives.module').then( m => m.ExternalDrivesPageModule),
},
]
@NgModule({

View File

@@ -47,6 +47,11 @@ export class AppComponent {
url: '/notifications',
icon: 'notifications-outline',
},
{
title: 'External Drives',
url: '/drives',
icon: 'albums-outline',
},
]
constructor (

View File

@@ -161,7 +161,7 @@ export interface DiskInfo {
export interface DiskPartition {
logicalname: string,
isMounted: boolean, // Do not let them back up to this if true
isMounted: boolean, // We do not allow backups to mounted partitions
size: string | null,
label: string | null,
}

View File

@@ -0,0 +1,30 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { ExternalDrivesPage } from './external-drives.page'
import { Routes, RouterModule } from '@angular/router'
import { SharingModule } from 'src/app/modules/sharing.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { ObjectConfigComponentModule } from 'src/app/components/object-config/object-config.component.module'
const routes: Routes = [
{
path: '',
component: ExternalDrivesPage,
},
]
@NgModule({
imports: [
CommonModule,
IonicModule,
SharingModule,
ObjectConfigComponentModule,
RouterModule.forChild(routes),
PwaBackComponentModule,
BadgeMenuComponentModule,
],
declarations: [ExternalDrivesPage],
})
export class ExternalDrivesPageModule { }

View File

@@ -0,0 +1,38 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>External Drives</ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content *ngIf="!($loading$ | async)" class="ion-padding-top">
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content>
</ion-refresher>
<ion-item-divider>
Storage
</ion-item-divider>
<ion-item-group>
<!-- <ion-list> -->
<ion-item *ngFor="let d of disks; let i = index">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>{{d.logicalname}}</ion-label>
<ion-button *ngIf="!(d.$ejecting$ | async)" slot="end" fill="clear" color="medium" (click)="ejectDisk(i)">
<ion-icon color="primary" class="icon" src="/assets/icon/eject.svg"></ion-icon>
</ion-button>
<ion-spinner *ngIf="d.$ejecting$ | async" name="lines" color="medium"></ion-spinner>
</ion-item>
<!-- </ion-list> -->
</ion-item-group>
</ion-content>
<ion-content *ngIf="$loading$ | async" class="ion-padding-top">
<ion-spinner class="center" name="lines" color="warning"></ion-spinner>
</ion-content>

View File

@@ -0,0 +1,61 @@
import { Component } from '@angular/core'
import { pauseFor } from 'src/app/util/misc.util'
import { ApiService } from 'src/app/services/api/api.service'
import { DiskInfo } from 'src/app/models/server-model'
import { markAsLoadingDuring$, markAsLoadingDuringAsync, markAsLoadingDuringP } from 'src/app/services/loader.service'
import { BehaviorSubject } from 'rxjs'
import { AlertController } from '@ionic/angular'
type Ejectable<T> = T & { $ejecting$: BehaviorSubject<boolean> }
@Component({
selector: 'external-drives',
templateUrl: './external-drives.page.html',
styleUrls: ['./external-drives.page.scss'],
})
export class ExternalDrivesPage {
disks: Ejectable<DiskInfo>[] = []
$loading$ = new BehaviorSubject(false)
constructor (
private readonly apiService: ApiService,
private readonly alertCtrl: AlertController,
) { }
ngOnInit () {
markAsLoadingDuringP(this.$loading$, this.fetchDisks())
}
async doRefresh (event: any) {
await Promise.all([
this.fetchDisks(),
pauseFor(600),
])
event.target.complete()
}
async fetchDisks () {
return this.apiService.getExternalDisks().then(ds => {
this.disks = ds.map(d => ({ ...d, $ejecting$: new BehaviorSubject(false)})).sort( (a, b) => a.logicalname < b.logicalname ? -1 : 1 )
})
}
async ejectDisk (diskIndex: number) {
const d = this.disks[diskIndex]
markAsLoadingDuringP(d.$ejecting$, this.apiService.ejectExternalDisk(d.logicalname))
.then(() => this.disks.splice(diskIndex, 1))
.catch((e: Error) => {
this.alertError(`Could not eject ${d.logicalname}: ${e.message}`)
})
}
async alertError (desc: string) {
const alert = await this.alertCtrl.create({
backdropDismiss: true,
message: desc,
cssClass: 'alert-error-message',
})
await alert.present()
}
}

View File

@@ -37,7 +37,7 @@ const routes: Routes = [
path: 'developer',
canActivate: [AuthGuard],
loadChildren: () => import('./developer-routes/developer-routing.module').then( m => m.DeveloperRoutingModule),
},
}
]
@NgModule({

View File

@@ -57,6 +57,7 @@ export abstract class ApiService {
abstract deleteWifi (ssid: string): Promise<Unit>
abstract restartServer (): Promise<Unit>
abstract shutdownServer (): Promise<Unit>
abstract ejectExternalDisk (logicalName: string): Promise<Unit>
}
export module ReqRes {

View File

@@ -9,7 +9,7 @@ import { HttpErrorResponse } from '@angular/common/http'
import { isUnauthorized } from 'src/app/util/web.util'
import { Replace } from 'src/app/util/types.util'
import { AppMetrics, parseMetricsPermissive } from 'src/app/util/metrics.util'
import { modulateTime } from 'src/app/util/misc.util'
// import { modulateTime } from 'src/app/util/misc.util'
@Injectable()
export class LiveApiService extends ApiService {
@@ -61,6 +61,10 @@ export class LiveApiService extends ApiService {
return this.authRequest<ReqRes.GetExternalDisksRes>({ method: Method.GET, url: `/disks` })
}
async ejectExternalDisk (logicalName: string): Promise<Unit> {
return this.authRequest({ method: Method.DELETE, url: `/disks/${logicalName}` })
}
async updateAgent (version: string): Promise<Unit> {
const data: ReqRes.PostUpdateAgentReq = {
version: `=${version}`,

View File

@@ -29,13 +29,17 @@ export class MockApiService extends ApiService {
async postConfigureDependency (dependencyId: string, dependentId: string, dryRun?: boolean): Promise<{ config: object, breakages: DependentBreakage[] }> {
await pauseFor(2000)
throw new Error ('some misc backend error ohh we forgot to make this endpoint or something')
// return { config: mockCupsDependentConfig, breakages: [ ] }
}
async getServer (): Promise<ApiServer> {
return mockGetServer()
}
async ejectExternalDisk (): Promise<Unit> {
await pauseFor(2000)
return { }
}
async getCheckAuth (): Promise<ReqRes.GetCheckAuthRes> {
return { }
}

View File

@@ -0,0 +1,4 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M413.822 269.498L274.042 106.378C271.789 103.751 268.994 101.641 265.849 100.195C262.704 98.7488 259.284 98 255.822 98C252.36 98 248.94 98.7488 245.795 100.195C242.65 101.641 239.855 103.751 237.602 106.378L97.822 269.498C84.482 285.068 95.542 309.118 116.042 309.118H395.642C416.142 309.118 427.202 285.068 413.822 269.498V269.498Z" fill="black"/>
<line x1="116" y1="356" x2="396" y2="356" stroke="black" stroke-width="48" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 589 B

View File

@@ -0,0 +1,4 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M455.761 282.295L278.654 75.6159C275.799 72.2862 272.258 69.6135 268.273 67.7811C264.289 65.9487 259.955 65 255.569 65C251.183 65 246.849 65.9487 242.864 67.7811C238.879 69.6135 235.338 72.2862 232.483 75.6159L55.3767 282.295C38.4744 302.023 52.4879 332.495 78.4621 332.495H432.726C458.7 332.495 472.714 302.023 455.761 282.295V282.295Z" fill="#989898"/>
<line x1="76" y1="415.305" x2="435.589" y2="415.305" stroke="#989898" stroke-width="56" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 608 B

View File

@@ -194,6 +194,12 @@ ion-popover {
}
}
.alert-error-message {
.alert-message {
color: var(--ion-color-danger);
}
}
ion-slides {
.slider-wrapper {
height: 100%;

View File

@@ -33,6 +33,7 @@
"src/polyfills.ts",
],
"include": [
"src/**/*.d.ts"
"src/**/*.d.ts",
"*.d.ts",
]
}