mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
0.3.0 refactor
ui: adds overlay layer to patch-db-client ui: getting towards mocks ui: cleans up factory init ui: nice type hack ui: live api for patch ui: api service source + http starts up ui: api source + http ui: rework patchdb config, pass stashTimeout into patchDbModel wires in temp patching into api service ui: example of wiring patchdbmodel into page begin integration remove unnecessary method linting first data rendering rework app initialization http source working for ssh delete call temp patches working entire Embassy tab complete not in kansas anymore ripping, saving progress progress for API request response types and endoint defs Update data-model.ts shambles, but in a good way progress big progress progress installed list working big progress progress progress begin marketplace redesign Update api-types.ts Update api-types.ts marketplace improvements cosmetic dependencies and recommendations begin nym auth approach install wizard restore flow and donations
This commit is contained in:
committed by
Aiden McClelland
parent
46f32cb90b
commit
8d01ebe8b2
@@ -0,0 +1,28 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppPropertiesPage } from './app-properties.page'
|
||||
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
|
||||
import { QRComponentModule } from 'src/app/components/qr/qr.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AppPropertiesPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
PwaBackComponentModule,
|
||||
QRComponentModule,
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [AppPropertiesPage],
|
||||
})
|
||||
export class AppPropertiesPageModule { }
|
||||
@@ -0,0 +1,74 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<pwa-back-button></pwa-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Properties</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
<ion-spinner *ngIf="loading; else loaded" class="center" name="lines" color="warning"></ion-spinner>
|
||||
|
||||
<ng-template #loaded>
|
||||
<ng-container *ngIf="patch.watch$('package-data', pkgId) | ngrxPush as pkg">
|
||||
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<ion-item *ngIf="error" style="margin-bottom: 16px;">
|
||||
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
|
||||
</ion-item>
|
||||
|
||||
<!-- not running -->
|
||||
<ion-item *ngIf="pkg.installed.status.main.status !== FeStatus.Running" class="ion-margin-bottom">
|
||||
<ion-label class="ion-text-wrap">
|
||||
<p><ion-text color="warning">Service not running. Information on this page could be inaccurate.</ion-text></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- no properties -->
|
||||
<ion-item *ngIf="(hasProperties$ | ngrxPush) === false">
|
||||
<ion-label class="ion-text-wrap">
|
||||
<p>No properties.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- properties -->
|
||||
<ion-item-group *ngIf="(hasProperties$ | ngrxPush) === true">
|
||||
<div *ngFor="let prop of properties$ | ngrxPush | keyvalue: asIsOrder">
|
||||
<!-- object -->
|
||||
<ion-item button detail="true" *ngIf="prop.value.type === 'object'" (click)="goToNested(prop.key)">
|
||||
<ion-button *ngIf="prop.value.description" class="help-button" fill="clear" slot="start" (click)="presentDescription(prop, $event)">
|
||||
<ion-icon size="small" slot="icon-only" name="help-circle-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>{{ prop.key }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<!-- not object -->
|
||||
<ion-item *ngIf="prop.value.type === 'string'">
|
||||
<ion-button *ngIf="prop.value.description" class="help-button" fill="clear" slot="start" (click)="presentDescription(prop, $event)">
|
||||
<ion-icon size="small" slot="icon-only" name="help-circle-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>{{ prop.key }}</h2>
|
||||
<p>{{ prop.value.masked && !unmasked[prop.key] ? (prop.value.value | mask ) : (prop.value.value | truncateEnd : 100) }}</p>
|
||||
</ion-label>
|
||||
<div slot="end" *ngIf="prop.value.copyable || prop.value.qr">
|
||||
<ion-button *ngIf="prop.value.masked" fill="clear" (click)="toggleMask(prop.key)">
|
||||
<ion-icon slot="icon-only" [name]="unmasked[prop.key] ? 'eye-off-outline' : 'eye-outline'" [color]="unmasked[prop.key] ? 'danger' : 'primary'" size="small"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="prop.value.qr" fill="clear" (click)="showQR(prop.value.value)">
|
||||
<ion-icon slot="icon-only" name="qr-code-outline" size="small" color="primary"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="prop.value.copyable" fill="clear" (click)="copy(prop.value.value)">
|
||||
<ion-icon slot="icon-only" name="copy-outline" size="small" color="primary"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-item-group>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ion-content>
|
||||
@@ -0,0 +1,3 @@
|
||||
.metric-note {
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { isEmptyObject, pauseFor } from 'src/app/util/misc.util'
|
||||
import { BehaviorSubject, Subject } from 'rxjs'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { AlertController, NavController, PopoverController, ToastController } from '@ionic/angular'
|
||||
import { PackageProperties } from 'src/app/util/properties.util'
|
||||
import { QRComponent } from 'src/app/components/qr/qr.component'
|
||||
import { PropertyStore } from './property-store'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import * as JSONpointer from 'json-pointer'
|
||||
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-properties',
|
||||
templateUrl: './app-properties.page.html',
|
||||
styleUrls: ['./app-properties.page.scss'],
|
||||
})
|
||||
export class AppPropertiesPage {
|
||||
error = ''
|
||||
loading = true
|
||||
pkgId: string
|
||||
pointer: string
|
||||
qrCode: string
|
||||
properties$ = new BehaviorSubject<PackageProperties>({ })
|
||||
hasProperties$ = new BehaviorSubject<boolean>(null)
|
||||
unmasked: { [key: string]: boolean } = { }
|
||||
FeStatus = FEStatus
|
||||
|
||||
constructor (
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly apiService: ApiService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly popoverCtrl: PopoverController,
|
||||
private readonly propertyStore: PropertyStore,
|
||||
private readonly navCtrl: NavController,
|
||||
public patch: PatchDbModel,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
|
||||
this.pointer = this.route.queryParams['pointer']
|
||||
|
||||
this.getProperties().then(() => this.loading = false)
|
||||
|
||||
this.propertyStore.watch$().subscribe(m => {
|
||||
const properties = JSONpointer.get(m, this.pointer || '')
|
||||
this.properties$.next(properties)
|
||||
})
|
||||
this.properties$.subscribe(m => {
|
||||
this.hasProperties$.next(!isEmptyObject(m))
|
||||
})
|
||||
this.route.queryParams.subscribe(queryParams => {
|
||||
if (queryParams['pointer'] === this.pointer) return
|
||||
this.pointer = queryParams['pointer']
|
||||
const properties = JSONpointer.get(this.propertyStore.properties$.getValue(), this.pointer || '')
|
||||
this.properties$.next(properties)
|
||||
})
|
||||
}
|
||||
|
||||
async doRefresh (event: any) {
|
||||
await this.getProperties(),
|
||||
event.target.complete()
|
||||
}
|
||||
|
||||
async presentDescription (property: { key: string, value: PackageProperties[''] }, e: Event) {
|
||||
e.stopPropagation()
|
||||
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: property.key,
|
||||
message: property.value.description,
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async goToNested (key: string): Promise<any> {
|
||||
this.navCtrl.navigateForward(`/services/installed/${this.pkgId}/properties`, {
|
||||
queryParams: {
|
||||
pointer: `${this.pointer || ''}/${key}/value`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async copy (text: string): Promise<void> {
|
||||
let message = ''
|
||||
await copyToClipboard(text).then(success => { message = success ? 'copied to clipboard!' : 'failed to copy'})
|
||||
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: message,
|
||||
position: 'bottom',
|
||||
duration: 1000,
|
||||
cssClass: 'notification-toast',
|
||||
})
|
||||
await toast.present()
|
||||
}
|
||||
|
||||
async showQR (text: string, ev: any): Promise<void> {
|
||||
const popover = await this.popoverCtrl.create({
|
||||
component: QRComponent,
|
||||
cssClass: 'qr-popover',
|
||||
event: ev,
|
||||
componentProps: {
|
||||
text,
|
||||
},
|
||||
})
|
||||
return await popover.present()
|
||||
}
|
||||
|
||||
toggleMask (key: string) {
|
||||
this.unmasked[key] = !this.unmasked[key]
|
||||
}
|
||||
|
||||
asIsOrder (a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
|
||||
private async getProperties (): Promise<void> {
|
||||
try {
|
||||
const properties = await this.apiService.getPackageProperties({ id: this.pkgId })
|
||||
this.propertyStore.update(properties)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error = e.message
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { PackageProperties } from '../../../util/properties.util'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PropertyStore {
|
||||
properties$: BehaviorSubject<PackageProperties> = new BehaviorSubject({ })
|
||||
watch$ () { return this.properties$.asObservable() }
|
||||
|
||||
update (properties: PackageProperties): void {
|
||||
this.properties$.next(properties)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user