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

@@ -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 { }
}