mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
enable UI during backups, fix state management bugs
This commit is contained in:
committed by
Aiden McClelland
parent
d5dd37b165
commit
896069f1a1
@@ -0,0 +1,16 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button (click)="close()">
|
||||
<ion-icon [name]="type === 'backup' ? 'arrow-back' : 'close'"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button [disabled]="backupService.loading" (click)="refresh()">
|
||||
Refresh
|
||||
<ion-icon slot="end" name="refresh"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
@@ -0,0 +1,70 @@
|
||||
<ion-item-group>
|
||||
<!-- always -->
|
||||
<ion-item class="ion-margin-bottom">
|
||||
<ion-label>{{ message }}</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- loading -->
|
||||
<ng-container *ngIf="backupService.loading; else loaded">
|
||||
<ion-item-divider>
|
||||
<ion-skeleton-text animated style="width: 120px; height: 16px;"></ion-skeleton-text>
|
||||
</ion-item-divider>
|
||||
<ion-item>
|
||||
<ion-avatar slot="start" style="margin-right: 24px;">
|
||||
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<ion-skeleton-text animated style="width: 100px; height: 20px; margin-bottom: 12px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 50px; height: 16px; margin-bottom: 16px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 100px;"></ion-skeleton-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- loaded -->
|
||||
<ng-template #loaded>
|
||||
|
||||
<!-- error -->
|
||||
<ion-item *ngIf="backupService.loadingError; else noError">
|
||||
<ion-label>
|
||||
<ion-text color="danger">
|
||||
{{ backupService.loadingError }}
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-template #noError>
|
||||
|
||||
<ion-item *ngIf="!backupService.drives.length; else hasDrives">
|
||||
<ion-label>
|
||||
<ion-text color="warning">
|
||||
No drives found. Insert a backup drive into your Embassy and click "Refresh" above.
|
||||
</ion-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-template #hasDrives>
|
||||
<ion-item-group>
|
||||
<div *ngFor="let drive of backupService.drives">
|
||||
<ion-item-divider>
|
||||
{{ drive.vendor || 'Unknown Vendor' }} - {{ drive.model || 'Unknown Model' }} - {{ drive.capacity | convertBytes }}
|
||||
</ion-item-divider>
|
||||
<ion-item button *ngFor="let partition of drive.partitions" [disabled]="type === 'restore' && !partition.hasBackup" (click)="handleSelect(partition)">
|
||||
<ion-icon slot="start" name="save-outline" size="large"></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ partition.label || partition.logicalname }}</h1>
|
||||
<h2>{{ partition.capacity | convertBytes }}</h2>
|
||||
<p *ngIf="partition.hasBackup">
|
||||
<ion-text color="success">
|
||||
Embassy backups detected
|
||||
</ion-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
<ion-icon *ngIf="partition.hasBackup" slot="end" color="danger" name="lock-closed-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-item-group>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ion-item-group>
|
||||
@@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { BackupDrivesComponent, BackupDrivesHeaderComponent } from './backup-drives.component'
|
||||
import { SharingModule } from '../../modules/sharing.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BackupDrivesComponent,
|
||||
BackupDrivesHeaderComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
],
|
||||
exports: [
|
||||
BackupDrivesComponent,
|
||||
BackupDrivesHeaderComponent,
|
||||
],
|
||||
})
|
||||
export class BackupDrivesComponentModule { }
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
import { BackupService } from './backup.service'
|
||||
import { MappedPartitionInfo } from 'src/app/util/misc.util'
|
||||
|
||||
@Component({
|
||||
selector: 'backup-drives',
|
||||
templateUrl: './backup-drives.component.html',
|
||||
styleUrls: ['./backup-drives.component.scss'],
|
||||
})
|
||||
export class BackupDrivesComponent {
|
||||
@Input() type: 'backup' | 'recover'
|
||||
@Output() onSelect: EventEmitter<MappedPartitionInfo> = new EventEmitter()
|
||||
message: string
|
||||
|
||||
constructor (
|
||||
public readonly backupService: BackupService,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
if (this.type === 'backup') {
|
||||
this.message = 'Select the drive where you want to create a backup of your Embassy.'
|
||||
} else {
|
||||
this.message = 'Select the drive containing the backup you would like to restore.'
|
||||
}
|
||||
this.backupService.getExternalDrives()
|
||||
}
|
||||
|
||||
handleSelect (partition: MappedPartitionInfo): void {
|
||||
this.onSelect.emit(partition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'backup-drives-header',
|
||||
templateUrl: './backup-drives-header.component.html',
|
||||
styleUrls: ['./backup-drives.component.scss'],
|
||||
})
|
||||
export class BackupDrivesHeaderComponent {
|
||||
@Input() type: 'backup' | 'recover'
|
||||
@Output() onClose: EventEmitter<void> = new EventEmitter()
|
||||
title: string
|
||||
|
||||
constructor (
|
||||
public readonly backupService: BackupService,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
if (this.type === 'backup') {
|
||||
this.title = 'Create Backup'
|
||||
} else {
|
||||
this.title = 'Restore From Backup'
|
||||
}
|
||||
}
|
||||
|
||||
close (): void {
|
||||
this.onClose.emit()
|
||||
}
|
||||
|
||||
refresh () {
|
||||
this.backupService.getExternalDrives()
|
||||
}
|
||||
}
|
||||
45
ui/src/app/components/backup-drives/backup.service.ts
Normal file
45
ui/src/app/components/backup-drives/backup.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { IonicSafeString } from '@ionic/core'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
import { MappedDriveInfo, MappedPartitionInfo } from 'src/app/util/misc.util'
|
||||
import { Emver } from 'src/app/services/emver.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class BackupService {
|
||||
drives: MappedDriveInfo[]
|
||||
loading = true
|
||||
loadingError: string | IonicSafeString
|
||||
|
||||
constructor (
|
||||
private readonly embassyApi: ApiService,
|
||||
private readonly emver: Emver,
|
||||
) { }
|
||||
|
||||
async getExternalDrives (): Promise<void> {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
const drives = await this.embassyApi.getDrives({ })
|
||||
this.drives = drives.map(d => {
|
||||
const partionInfo: MappedPartitionInfo[] = d.partitions.map(p => {
|
||||
return {
|
||||
...p,
|
||||
hasBackup: [0, 1].includes(this.emver.compare(p['embassy-os']?.version, '0.3.0')),
|
||||
}
|
||||
})
|
||||
return {
|
||||
...d,
|
||||
partitions: partionInfo,
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
this.loadingError = getErrorMessage(e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
ion-note {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user