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:
Aaron Greenspan
2021-02-16 13:45:09 -07:00
committed by Aiden McClelland
parent 46f32cb90b
commit 8d01ebe8b2
238 changed files with 25509 additions and 14852 deletions

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
.metric-note {
font-size: 16px;
}

View File

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

View File

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