mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
rework modals and more
This commit is contained in:
committed by
Aiden McClelland
parent
b16ef3c621
commit
01c6b91c52
@@ -1,34 +1,24 @@
|
||||
<ion-app>
|
||||
<ion-split-pane [disabled]="!showMenu" (ionSplitPaneVisible)="splitPaneVisible($event)" contentId="main-content">
|
||||
<ion-menu contentId="main-content" type="overlay">
|
||||
<ion-header>
|
||||
<ion-toolbar style="--background: var(--ion-background-color);">
|
||||
<ion-title>Menu</ion-title>
|
||||
<ng-template #dots>
|
||||
<ion-title><ion-spinner name="dots" color="warning"></ion-spinner></ion-title>
|
||||
</ng-template>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content scrollY="false" class="menu-style">
|
||||
<ng-container>
|
||||
<ion-list style="padding: 0px">
|
||||
<ion-menu-toggle auto-hide="false" *ngFor="let page of appPages; let i = index">
|
||||
<ion-item
|
||||
button
|
||||
(click)="selectedIndex = i"
|
||||
routerDirection="root"
|
||||
[routerLink]="[page.url]"
|
||||
lines="none"
|
||||
detail="false"
|
||||
[class.selected]="selectedIndex === i"
|
||||
>
|
||||
<ion-icon slot="start" [name]="page.icon"></ion-icon>
|
||||
<ion-label style="font-family: 'Montserrat';">{{ page.title }}</ion-label>
|
||||
<ion-badge *ngIf="page.url === '/notifications' && unreadCount" color="danger" style="margin-right: 3%;" [class.selected-badge]="selectedIndex == i">{{ unreadCount }}</ion-badge>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
</ion-list>
|
||||
</ng-container>
|
||||
<ion-list style="padding: 0px">
|
||||
<ion-menu-toggle auto-hide="false" *ngFor="let page of appPages; let i = index">
|
||||
<ion-item
|
||||
button
|
||||
(click)="selectedIndex = i"
|
||||
routerDirection="root"
|
||||
[routerLink]="[page.url]"
|
||||
lines="none"
|
||||
detail="false"
|
||||
[class.selected]="selectedIndex === i"
|
||||
>
|
||||
<ion-icon slot="start" [name]="page.icon"></ion-icon>
|
||||
<ion-label style="font-family: 'Montserrat';">{{ page.title }}</ion-label>
|
||||
<ion-badge *ngIf="page.url === '/notifications' && unreadCount" color="danger" style="margin-right: 3%;" [class.selected-badge]="selectedIndex == i">{{ unreadCount }}</ion-badge>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
<ion-footer style="padding-bottom: 16px; text-align: center;" class="menu-style">
|
||||
<ion-menu-toggle auto-hide="false">
|
||||
|
||||
@@ -3,17 +3,16 @@ import { Storage } from '@ionic/storage'
|
||||
import { AuthService, AuthState } from './services/auth.service'
|
||||
import { ApiService } from './services/api/api.service'
|
||||
import { Router, RoutesRecognized } from '@angular/router'
|
||||
import { distinctUntilChanged, filter, finalize, takeWhile } from 'rxjs/operators'
|
||||
import { debounceTime, distinctUntilChanged, filter, finalize, takeWhile } from 'rxjs/operators'
|
||||
import { AlertController, ToastController } from '@ionic/angular'
|
||||
import { LoaderService } from './services/loader.service'
|
||||
import { Emver } from './services/emver.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { LoadingOptions } from '@ionic/core'
|
||||
import { PatchDbModel } from './models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from './services/patch-db/patch-db.service'
|
||||
import { HttpService } from './services/http.service'
|
||||
import { ServerStatus } from './models/patch-db/data-model'
|
||||
import { ServerStatus } from './services/patch-db/data-model'
|
||||
import { ConnectionFailure, ConnectionService } from './services/connection.service'
|
||||
import { combineLatest, merge } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -29,7 +28,7 @@ export class AppComponent {
|
||||
unreadCount: number
|
||||
appPages = [
|
||||
{
|
||||
title: 'Services',
|
||||
title: 'Installed Services',
|
||||
url: '/services',
|
||||
icon: 'grid-outline',
|
||||
},
|
||||
@@ -39,7 +38,7 @@ export class AppComponent {
|
||||
icon: 'cube-outline',
|
||||
},
|
||||
{
|
||||
title: 'Marketplace',
|
||||
title: 'Service Marketplace',
|
||||
url: '/marketplace',
|
||||
icon: 'storefront-outline',
|
||||
},
|
||||
@@ -120,19 +119,38 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
private watchConnection (auth: AuthState): void {
|
||||
this.connectionService.watch$()
|
||||
this.connectionService.watchFailure$()
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(500),
|
||||
takeWhile(() => auth === AuthState.VERIFIED),
|
||||
)
|
||||
.subscribe(connectionFailure => {
|
||||
if (connectionFailure !== ConnectionFailure.None) {
|
||||
this.presentToastOffline()
|
||||
} else {
|
||||
.subscribe(async connectionFailure => {
|
||||
if (connectionFailure === ConnectionFailure.None) {
|
||||
if (this.offlineToast) {
|
||||
this.offlineToast.dismiss()
|
||||
this.offlineToast = undefined
|
||||
}
|
||||
} else {
|
||||
let message: string
|
||||
switch (connectionFailure) {
|
||||
case ConnectionFailure.Network:
|
||||
message = 'No network'
|
||||
break
|
||||
case ConnectionFailure.Diagnosing:
|
||||
message = 'Diagnosing'
|
||||
break
|
||||
case ConnectionFailure.Embassy:
|
||||
message = 'Embassy is unreachable'
|
||||
break
|
||||
case ConnectionFailure.Tor:
|
||||
message = 'Tor issues'
|
||||
break
|
||||
case ConnectionFailure.Internet:
|
||||
message = 'No Internet'
|
||||
break
|
||||
}
|
||||
await this.presentToastOffline(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -237,10 +255,15 @@ export class AppComponent {
|
||||
await toast.present()
|
||||
}
|
||||
|
||||
private async presentToastOffline () {
|
||||
private async presentToastOffline (message: string) {
|
||||
if (this.offlineToast) {
|
||||
this.offlineToast.message = message
|
||||
return
|
||||
}
|
||||
|
||||
this.offlineToast = await this.toastCtrl.create({
|
||||
header: 'No Internet',
|
||||
message: `Please check your Internet connection and try again.`,
|
||||
header: 'Connection Issue',
|
||||
message,
|
||||
position: 'bottom',
|
||||
duration: 0,
|
||||
buttons: [
|
||||
|
||||
@@ -9,16 +9,17 @@ import { AppComponent } from './app.component'
|
||||
import { AppRoutingModule } from './app-routing.module'
|
||||
import { ApiService } from './services/api/api.service'
|
||||
import { ApiServiceFactory } from './services/api/api.service.factory'
|
||||
import { PatchDbModelFactory } from './models/patch-db/patch-db-model.factory'
|
||||
import { PatchDbModelFactory } from './services/patch-db/patch-db.factory'
|
||||
import { HttpService } from './services/http.service'
|
||||
import { ConfigService } from './services/config.service'
|
||||
import { QRCodeModule } from 'angularx-qrcode'
|
||||
import { APP_CONFIG_COMPONENT_MAPPING } from './modals/app-config-injectable/modal-injectable-token'
|
||||
import { appConfigComponents } from './modals/app-config-injectable/modal-injectable-value'
|
||||
import { appConfigComponents } from './modals/app-config-injectable'
|
||||
import { OSWelcomePageModule } from './modals/os-welcome/os-welcome.module'
|
||||
import { PatchDbModel } from './models/patch-db/patch-db-model'
|
||||
import { LocalStorageBootstrap } from './models/patch-db/local-storage-bootstrap'
|
||||
import { MarkdownPageModule } from './modals/markdown/markdown.module'
|
||||
import { PatchDbModel } from './services/patch-db/patch-db.service'
|
||||
import { LocalStorageBootstrap } from './services/patch-db/local-storage-bootstrap'
|
||||
import { SharingModule } from './modules/sharing.module'
|
||||
import { APP_CONFIG_COMPONENT_MAPPING } from './services/tracking-modal-controller.service'
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
@@ -36,6 +37,7 @@ import { SharingModule } from './modules/sharing.module'
|
||||
}),
|
||||
QRCodeModule,
|
||||
OSWelcomePageModule,
|
||||
MarkdownPageModule,
|
||||
SharingModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { combineLatest, Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<div style="padding: 15px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
background: var(--ion-color-light);
|
||||
border-radius: 10px;
|
||||
border-color: var(--ion-color-warning);
|
||||
color: white;
|
||||
font-size: small;
|
||||
"
|
||||
[innerHTML]="unsafeInformation">
|
||||
</div>
|
||||
@@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { InformationPopoverComponent } from './information-popover.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
InformationPopoverComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
],
|
||||
exports: [InformationPopoverComponent],
|
||||
})
|
||||
export class InformationPopoverComponentModule { }
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
|
||||
|
||||
@Component({
|
||||
selector: 'app-information-popover',
|
||||
templateUrl: './information-popover.component.html',
|
||||
styleUrls: ['./information-popover.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class InformationPopoverComponent implements OnInit {
|
||||
@Input() title: string
|
||||
@Input() information: string
|
||||
unsafeInformation: SafeHtml
|
||||
constructor (private sanitizer: DomSanitizer) { }
|
||||
ngOnInit () {
|
||||
this.unsafeInformation = this.sanitizer.bypassSecurityTrustHtml(this.information)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import { DependentsComponent } from './dependents.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { InformationPopoverComponentModule } from '../../information-popover/information-popover.component.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -15,7 +14,6 @@ import { InformationPopoverComponentModule } from '../../information-popover/inf
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
InformationPopoverComponentModule,
|
||||
],
|
||||
exports: [DependentsComponent],
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { InstalledPackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { InstalledPackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { Breakages } from 'src/app/services/api/api-types'
|
||||
import { exists } from 'src/app/util/misc.util'
|
||||
import { ApiService } from '../../services/api/api.service'
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<ion-fab id="recommendation" vertical="top" horizontal="end" slot="fixed">
|
||||
<ion-fab-button [disabled]="disabled" style="border-style: solid;
|
||||
border-radius: 100px;
|
||||
border-color: #FFEB3B;
|
||||
border-width: medium;
|
||||
box-shadow: 0 0 10px white;" size="small" (click)="presentPopover($event)">
|
||||
<img [src]="rec.iconURL" />
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
@@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { RecommendationButtonComponent } from './recommendation-button.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
RecommendationButtonComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
],
|
||||
exports: [RecommendationButtonComponent],
|
||||
})
|
||||
export class RecommendationButtonComponentModule { }
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { PopoverController } from '@ionic/angular'
|
||||
import { filter, take } from 'rxjs/operators'
|
||||
import { capitalizeFirstLetter } from 'src/app/util/misc.util'
|
||||
import { InformationPopoverComponent } from '../information-popover/information-popover.component'
|
||||
|
||||
@Component({
|
||||
selector: 'recommendation-button',
|
||||
templateUrl: './recommendation-button.component.html',
|
||||
styleUrls: ['./recommendation-button.component.scss'],
|
||||
})
|
||||
export class RecommendationButtonComponent {
|
||||
@Input() rec: Recommendation
|
||||
@Input() raise?: { id: string }
|
||||
constructor (
|
||||
private readonly router: Router,
|
||||
private readonly popoverController: PopoverController,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
if (!this.raise) return
|
||||
const mainContent = document.getElementsByTagName('ion-app')[0]
|
||||
const recButton = document.getElementById(this.raise.id)
|
||||
mainContent.appendChild(recButton)
|
||||
|
||||
this.router.events.pipe(filter(e => !!(e as any).urlAfterRedirects, take(1))).subscribe((e: any) => {
|
||||
recButton.remove()
|
||||
})
|
||||
}
|
||||
|
||||
disabled = false
|
||||
|
||||
async presentPopover (ev: any) {
|
||||
const popover = await this.popoverController.create({
|
||||
component: InformationPopoverComponent,
|
||||
event: ev,
|
||||
translucent: false,
|
||||
showBackdrop: true,
|
||||
backdropDismiss: true,
|
||||
componentProps: {
|
||||
information: `
|
||||
<div style="font-size: medium; font-style: italic; margin: 5px 0px;">
|
||||
${capitalizeFirstLetter(this.rec.dependentTitle)} Installation Recommendations
|
||||
</div>
|
||||
<div>
|
||||
${this.rec.description}
|
||||
</div>`,
|
||||
},
|
||||
})
|
||||
popover.onWillDismiss().then(() => {
|
||||
this.disabled = false
|
||||
})
|
||||
this.disabled = true
|
||||
return await popover.present()
|
||||
}
|
||||
}
|
||||
|
||||
export type Recommendation = {
|
||||
dependentId: string
|
||||
dependentTitle: string
|
||||
dependentIcon: string,
|
||||
description: string
|
||||
version?: string
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { ConnectionStatus } from 'patch-db-client'
|
||||
|
||||
@Component({
|
||||
selector: 'status',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { CanActivate, Router, CanActivateChild } from '@angular/router'
|
||||
import { tap } from 'rxjs/operators'
|
||||
import { ServerStatus } from '../models/patch-db/data-model'
|
||||
import { PatchDbModel } from '../models/patch-db/patch-db-model'
|
||||
import { ServerStatus } from '../services/patch-db/data-model'
|
||||
import { PatchDbModel } from '../services/patch-db/patch-db.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { CanActivate, Router } from '@angular/router'
|
||||
import { tap } from 'rxjs/operators'
|
||||
import { ServerStatus } from '../models/patch-db/data-model'
|
||||
import { PatchDbModel } from '../models/patch-db/patch-db-model'
|
||||
import { ServerStatus } from '../services/patch-db/data-model'
|
||||
import { PatchDbModel } from '../services/patch-db/patch-db.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
19
ui/src/app/modals/app-config-injectable.ts
Normal file
19
ui/src/app/modals/app-config-injectable.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { AppConfigObjectPage } from './app-config-object/app-config-object.page'
|
||||
import { AppConfigListPage } from './app-config-list/app-config-list.page'
|
||||
import { AppConfigUnionPage } from './app-config-union/app-config-union.page'
|
||||
import { AppConfigValuePage } from './app-config-value/app-config-value.page'
|
||||
import { Type } from '@angular/core'
|
||||
import { ValueType } from 'src/app/pkg-config/config-types'
|
||||
|
||||
export const appConfigComponents: AppConfigComponentMapping = {
|
||||
'string': AppConfigValuePage,
|
||||
'number': AppConfigValuePage,
|
||||
'enum': AppConfigValuePage,
|
||||
'boolean': AppConfigValuePage,
|
||||
'list': AppConfigListPage,
|
||||
'object': AppConfigObjectPage,
|
||||
'union': AppConfigUnionPage,
|
||||
'pointer': undefined,
|
||||
}
|
||||
|
||||
export type AppConfigComponentMapping = { [k in ValueType]: Type<any> }
|
||||
@@ -1,3 +0,0 @@
|
||||
import { InjectionToken } from '@angular/core'
|
||||
|
||||
export const APP_CONFIG_COMPONENT_MAPPING = new InjectionToken<string>('APP_CONFIG_COMPONENTS')
|
||||
@@ -1,4 +0,0 @@
|
||||
import { Type } from '@angular/core'
|
||||
import { ValueType } from 'src/app/pkg-config/config-types'
|
||||
|
||||
export type AppConfigComponentMapping = { [k in ValueType]: Type<any> }
|
||||
@@ -1,16 +0,0 @@
|
||||
import { AppConfigObjectPage } from '../app-config-object/app-config-object.page'
|
||||
import { AppConfigListPage } from '../app-config-list/app-config-list.page'
|
||||
import { AppConfigUnionPage } from '../app-config-union/app-config-union.page'
|
||||
import { AppConfigValuePage } from '../app-config-value/app-config-value.page'
|
||||
import { AppConfigComponentMapping } from './modal-injectable-type'
|
||||
|
||||
export const appConfigComponents: AppConfigComponentMapping = {
|
||||
'string': AppConfigValuePage,
|
||||
'number': AppConfigValuePage,
|
||||
'enum': AppConfigValuePage,
|
||||
'boolean': AppConfigValuePage,
|
||||
'list': AppConfigListPage,
|
||||
'object': AppConfigObjectPage,
|
||||
'union': AppConfigUnionPage,
|
||||
'pointer': undefined,
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { getDefaultConfigValue, getDefaultDescription, Range } from 'src/app/pkg-config/config-utilities'
|
||||
import { AlertController, ToastController } from '@ionic/angular'
|
||||
import { AlertController, ModalController, ToastController } from '@ionic/angular'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { TrackingModalController } from 'src/app/services/tracking-modal-controller.service'
|
||||
import { ConfigCursor } from 'src/app/pkg-config/config-cursor'
|
||||
import { ValueSpecOf } from 'src/app/pkg-config/config-types'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
@@ -31,7 +30,7 @@ export class AppConfigValuePage {
|
||||
|
||||
constructor (
|
||||
private readonly loader: LoaderService,
|
||||
private readonly trackingModalCtrl: TrackingModalController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly toastCtrl: ToastController,
|
||||
) { }
|
||||
@@ -58,7 +57,7 @@ export class AppConfigValuePage {
|
||||
if ((!!this.saveFn && this.edited) || (!this.saveFn && this.error)) {
|
||||
await this.presentAlert()
|
||||
} else {
|
||||
await this.trackingModalCtrl.dismiss()
|
||||
await this.modalCtrl.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ export class AppConfigValuePage {
|
||||
}),
|
||||
)
|
||||
|
||||
await this.trackingModalCtrl.dismiss(this.value)
|
||||
await this.modalCtrl.dismiss(this.value)
|
||||
}
|
||||
|
||||
refreshDefault () {
|
||||
@@ -172,7 +171,7 @@ export class AppConfigValuePage {
|
||||
text: `Leave`,
|
||||
cssClass: 'alert-danger',
|
||||
handler: () => {
|
||||
this.trackingModalCtrl.dismiss()
|
||||
this.modalCtrl.dismiss()
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<!-- TODO: EJECT-DISKS, add a check box to allow a user to eject a disk on backup completion. -->
|
||||
<ion-content>
|
||||
<div style="height: 85%; margin: 20px; display: flex; flex-direction: column; justify-content: space-between;">
|
||||
<div>
|
||||
<h4><ion-text color="dark">Ready to Backup</ion-text></h4>
|
||||
<div style="height: 85%; margin: 24px; display: flex; flex-direction: column; justify-content: space-between;">
|
||||
<div *ngIf="type === 'backup'">
|
||||
<h4><ion-text color="dark">Encrypt Backup</ion-text></h4>
|
||||
<p><ion-text color="medium">Enter your master password to create an encrypted backup.</ion-text></p>
|
||||
</div>
|
||||
<div *ngIf="type === 'restore'">
|
||||
<h4><ion-text color="dark">Decrypt Backup</ion-text></h4>
|
||||
<p><ion-text color="medium">Enter the password that was originally used to encrypt this backup.</ion-text></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ion-item lines="none" style="--background: var(--ion-background-color); --border-color: var(--ion-color-medium);">
|
||||
<ion-label style="font-size: small" position="floating">Master Password</ion-label>
|
||||
@@ -14,12 +18,13 @@
|
||||
<ion-label style="font-size: small" color="danger" class="ion-text-wrap">{{ error }}</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; align-items: center;">
|
||||
<ion-button fill="clear" color="medium" (click)="cancel()">
|
||||
Cancel
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="primary" (click)="submit()">
|
||||
Create Backup
|
||||
<ion-button fill="clear" (click)="submit()">
|
||||
{{ type === 'backup' ? 'Create Backup' : 'Restore Backup' }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { PartitionInfo } from 'src/app/services/api/api-types'
|
||||
|
||||
@Component({
|
||||
selector: 'backup-confirmation',
|
||||
@@ -8,20 +7,15 @@ import { PartitionInfo } from 'src/app/services/api/api-types'
|
||||
styleUrls: ['./backup-confirmation.component.scss'],
|
||||
})
|
||||
export class BackupConfirmationComponent {
|
||||
@Input() name: string
|
||||
@Input() type: 'backup' | 'restore'
|
||||
unmasked = false
|
||||
password: string
|
||||
message: string
|
||||
password = ''
|
||||
error = ''
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.message = `Enter your master password to create an encrypted backup on "${this.name}".`
|
||||
}
|
||||
|
||||
toggleMask () {
|
||||
this.unmasked = !this.unmasked
|
||||
}
|
||||
|
||||
15
ui/src/app/modals/markdown/markdown.module.ts
Normal file
15
ui/src/app/modals/markdown/markdown.module.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { MarkdownPage } from './markdown.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [MarkdownPage],
|
||||
})
|
||||
export class MarkdownPageModule { }
|
||||
3
ui/src/app/modals/markdown/markdown.page.html
Normal file
3
ui/src/app/modals/markdown/markdown.page.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<ion-content>
|
||||
<div [innerHTML]="content | markdown"></div>
|
||||
</ion-content>
|
||||
19
ui/src/app/modals/markdown/markdown.page.ts
Normal file
19
ui/src/app/modals/markdown/markdown.page.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
|
||||
@Component({
|
||||
selector: 'markdown',
|
||||
templateUrl: './markdown.page.html',
|
||||
styleUrls: ['./markdown.page.scss'],
|
||||
})
|
||||
export class MarkdownPage {
|
||||
@Input() content: string
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
) { }
|
||||
|
||||
async dismiss () {
|
||||
return this.modalCtrl.dismiss(true)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export class StorageKeys {
|
||||
static APPS_CACHE_KEY = 'apps'
|
||||
static SERVER_CACHE_KEY = 'embassy'
|
||||
static LOGGED_IN_KEY = 'loggedInKey'
|
||||
static VIEWED_INSTRUCTIONS_KEY = 'viewedInstructions'
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { AlertController, IonContent, ModalController, NavController } from '@ionic/angular'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { HttpErrorResponse } from '@angular/common/http'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { Action, InstalledPackageDataEntry, Manifest, PackageMainStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Action, InstalledPackageDataEntry, Manifest, PackageMainStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@@ -10,8 +10,6 @@ import { AppConfigObjectPageModule } from 'src/app/modals/app-config-object/app-
|
||||
import { AppConfigUnionPageModule } from 'src/app/modals/app-config-union/app-config-union.module'
|
||||
import { AppConfigValuePageModule } from 'src/app/modals/app-config-value/app-config-value.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { RecommendationButtonComponentModule } from 'src/app/components/recommendation-button/recommendation-button.component.module'
|
||||
import { InformationPopoverComponentModule } from 'src/app/components/information-popover/information-popover.component.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -32,8 +30,6 @@ const routes: Routes = [
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
RecommendationButtonComponentModule,
|
||||
InformationPopoverComponentModule,
|
||||
],
|
||||
declarations: [AppConfigPage],
|
||||
})
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { NavController, AlertController, ModalController, PopoverController, IonContent } from '@ionic/angular'
|
||||
import { NavController, AlertController, ModalController, IonContent } from '@ionic/angular'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { isEmptyObject } from 'src/app/util/misc.util'
|
||||
import { isEmptyObject, Recommendation } from 'src/app/util/misc.util'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { TrackingModalController } from 'src/app/services/tracking-modal-controller.service'
|
||||
import { from, fromEvent, of, Subscription } from 'rxjs'
|
||||
import { catchError, concatMap, map, take, tap } from 'rxjs/operators'
|
||||
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { InformationPopoverComponent } from 'src/app/components/information-popover/information-popover.component'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import { ConfigCursor } from 'src/app/pkg-config/config-cursor'
|
||||
import { InstalledPackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { InstalledPackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-config',
|
||||
@@ -31,7 +29,7 @@ export class AppConfigPage {
|
||||
pkg: InstalledPackageDataEntry
|
||||
hasConfig = false
|
||||
|
||||
backButtonDefense = false
|
||||
mocalShowing = false
|
||||
packageState = PackageState
|
||||
|
||||
rec: Recommendation | null = null
|
||||
@@ -57,7 +55,6 @@ export class AppConfigPage {
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly modalController: ModalController,
|
||||
private readonly trackingModalCtrl: TrackingModalController,
|
||||
private readonly popoverController: PopoverController,
|
||||
private readonly patch: PatchDbModel,
|
||||
) { }
|
||||
|
||||
@@ -71,17 +68,17 @@ export class AppConfigPage {
|
||||
}
|
||||
}),
|
||||
fromEvent(window, 'popstate').subscribe(() => {
|
||||
this.backButtonDefense = false
|
||||
this.mocalShowing = false
|
||||
this.trackingModalCtrl.dismissAll()
|
||||
}),
|
||||
this.trackingModalCtrl.onCreateAny$().subscribe(() => {
|
||||
if (!this.backButtonDefense) {
|
||||
if (!this.mocalShowing) {
|
||||
window.history.pushState(null, null, window.location.href + '/edit')
|
||||
this.backButtonDefense = true
|
||||
this.mocalShowing = true
|
||||
}
|
||||
}),
|
||||
this.trackingModalCtrl.onDismissAny$().subscribe(() => {
|
||||
if (!this.trackingModalCtrl.anyModals && this.backButtonDefense === true) {
|
||||
if (!this.trackingModalCtrl.anyModals && this.mocalShowing === true) {
|
||||
this.navCtrl.back()
|
||||
}
|
||||
}),
|
||||
@@ -135,28 +132,6 @@ export class AppConfigPage {
|
||||
this.subs.forEach(sub => sub.unsubscribe())
|
||||
}
|
||||
|
||||
async presentPopover (title: string, description: string, ev: any) {
|
||||
const information = `
|
||||
<div style="font-size: medium; font-style: italic; margin: 5px 0px;">
|
||||
${title}
|
||||
</div>
|
||||
<div>
|
||||
${description}
|
||||
</div>
|
||||
`
|
||||
const popover = await this.popoverController.create({
|
||||
component: InformationPopoverComponent,
|
||||
event: ev,
|
||||
translucent: false,
|
||||
showBackdrop: true,
|
||||
backdropDismiss: true,
|
||||
componentProps: {
|
||||
information,
|
||||
},
|
||||
})
|
||||
return await popover.present()
|
||||
}
|
||||
|
||||
setConfig (spec: ConfigSpec, config: object, dependencyConfig?: object) {
|
||||
this.rootCursor = dependencyConfig ? new ConfigCursor(spec, config, null, dependencyConfig) : new ConfigCursor(spec, config)
|
||||
this.spec = this.rootCursor.spec().spec
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'
|
||||
import { IonContent } from '@ionic/angular'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { concatMap, take, tap } from 'rxjs/operators'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Component, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { IonContent, ToastController } from '@ionic/angular'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { InstalledPackageDataEntry, PackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { InstalledPackageDataEntry, PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { getManifest } from 'src/app/services/config.service'
|
||||
import * as JsonPointer from 'json-pointer'
|
||||
import { IonContent } from '@ionic/angular'
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Component, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { IonContent } from '@ionic/angular'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-metrics',
|
||||
|
||||
@@ -6,10 +6,10 @@ import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { AlertController, IonContent, NavController, PopoverController, ToastController } from '@ionic/angular'
|
||||
import { PackageProperties } from 'src/app/util/properties.util'
|
||||
import { QRComponent } from 'src/app/components/qr/qr.component'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import * as JsonPointer from 'json-pointer'
|
||||
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { PackageMainStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { PackageMainStatus } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-properties',
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key, partition.value)">
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
|
||||
import { DiskInfo, PartitionInfoEntry } from 'src/app/services/api/api-types'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
@@ -69,10 +69,10 @@ export class AppRestorePage {
|
||||
}
|
||||
}
|
||||
|
||||
async presentModal (logicalname: string, partition: PartitionInfoEntry): Promise<void> {
|
||||
async presentModal (logicalname: string): Promise<void> {
|
||||
const m = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
name: partition.label || logicalname,
|
||||
type: 'restore',
|
||||
},
|
||||
cssClass: 'alertlike-modal',
|
||||
component: BackupConfirmationComponent,
|
||||
|
||||
@@ -33,16 +33,16 @@
|
||||
|
||||
<div class="status-readout">
|
||||
<status *ngIf="connected" size="large" weight="500" [pkg]="pkg"></status>
|
||||
<ion-button *ngIf="pkg.status === FeStatus.NeedsConfig" expand="block" [routerLink]="['config']">
|
||||
<ion-button *ngIf="(pkg | status) === FeStatus.NeedsConfig" expand="block" [routerLink]="['config']">
|
||||
Configure
|
||||
</ion-button>
|
||||
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : pkg.status" expand="block" color="danger" (click)="stop()">
|
||||
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : (pkg | status)" expand="block" color="danger" (click)="stop()">
|
||||
Stop
|
||||
</ion-button>
|
||||
<ion-button *ngIf="pkg.status === FeStatus.DependencyIssue" expand="block" (click)="scrollToRequirements()">
|
||||
<ion-button *ngIf="(pkg | status) === FeStatus.DependencyIssue" expand="block" (click)="scrollToRequirements()">
|
||||
Fix
|
||||
</ion-button>
|
||||
<ion-button *ngIf="pkg.status === FeStatus.Stopped" expand="block" color="success" (click)="tryStart()">
|
||||
<ion-button *ngIf="(pkg | status) === FeStatus.Stopped" expand="block" color="success" (click)="tryStart()">
|
||||
Start
|
||||
</ion-button>
|
||||
</div>
|
||||
@@ -86,23 +86,23 @@
|
||||
<p><ion-text [color]="pkg.installed.status['dependency-errors'][dep.key] ? 'warning' : 'success'">{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}</ion-text></p>
|
||||
</ion-label>
|
||||
|
||||
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" color="dark" [routerLink]="['/services', dep.key]" fill="outline">
|
||||
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" [routerLink]="['/services', dep.key]">
|
||||
View
|
||||
</ion-button>
|
||||
|
||||
<ng-container *ngIf="pkg.installed.status['dependency-errors'][dep.key]">
|
||||
<ion-button *ngIf="!localDep" slot="end" size="small" color="dark" (click)="fixDep('install', dep.key)" fill="outline">
|
||||
<ion-button *ngIf="!localDep" slot="end" size="small" (click)="fixDep('install', dep.key)">
|
||||
Install
|
||||
</ion-button>
|
||||
|
||||
<ng-container *ngIf="localDep && localDep.state === PackageState.Installed">
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" color="dark" [routerLink]="['/services', dep.key]" fill="outline">
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" [routerLink]="['/services', dep.key]">
|
||||
Start
|
||||
</ion-button>
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.IncorrectVersion" slot="end" size="small" color="dark" (click)="fixDep('update', dep.key)" fill="outline">
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.IncorrectVersion" slot="end" size="small" (click)="fixDep('update', dep.key)">
|
||||
Update
|
||||
</ion-button>
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.ConfigUnsatisfied" slot="end" size="small" color="dark" (click)="fixDep('configure', dep.key)" fill="outline">
|
||||
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.ConfigUnsatisfied" slot="end" size="small" (click)="fixDep('configure', dep.key)">
|
||||
Configure
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
@@ -2,17 +2,16 @@ import { Component, ViewChild } from '@angular/core'
|
||||
import { AlertController, NavController, ModalController, IonContent } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { ActivatedRoute, NavigationExtras } from '@angular/router'
|
||||
import { chill, isEmptyObject } from 'src/app/util/misc.util'
|
||||
import { chill, isEmptyObject, Recommendation } from 'src/app/util/misc.util'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { Observable, of, Subscription } from 'rxjs'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { ConfigService, getManifest } from 'src/app/services/config.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, Manifest, PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, Manifest, PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show',
|
||||
@@ -80,7 +79,9 @@ export class AppShowPage {
|
||||
}).displayDuringAsync(async () => {
|
||||
const breakages = await this.apiService.dryStopPackage({ id })
|
||||
|
||||
if (isEmptyObject(breakages.length)) {
|
||||
console.log('BREAKAGES', breakages)
|
||||
|
||||
if (!isEmptyObject(breakages)) {
|
||||
const { cancelled } = await wizardModal(
|
||||
this.modalCtrl,
|
||||
this.wizardBaker.stop({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { ServerStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@Component({
|
||||
selector: 'Maintenance',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Embassy Marketplace</ion-title>
|
||||
<ion-title>Service Marketplace</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<badge-menu-button></badge-menu-button>
|
||||
</ion-buttons>
|
||||
|
||||
@@ -4,8 +4,8 @@ import { MarketplaceData, MarketplaceEOS, AvailablePreview } from 'src/app/servi
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -7,7 +7,6 @@ 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 { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { RecommendationButtonComponentModule } from 'src/app/components/recommendation-button/recommendation-button.component.module'
|
||||
import { InstallWizardComponentModule } from 'src/app/components/install-wizard/install-wizard.component.module'
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -25,7 +24,6 @@ const routes: Routes = [
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
PwaBackComponentModule,
|
||||
RecommendationButtonComponentModule,
|
||||
BadgeMenuComponentModule,
|
||||
InstallWizardComponentModule,
|
||||
],
|
||||
|
||||
@@ -155,72 +155,65 @@
|
||||
<p>{{ pkg.categories.join(', ') }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item detail="false">
|
||||
<ion-item button detail="false" (click)="presentAlertVersions()">
|
||||
<ion-label>
|
||||
<h2>Other Versions</h2>
|
||||
<p>Click to view other versions</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="presentAlertVersions()">
|
||||
<ion-icon slot="icon-only" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item detail="false">
|
||||
<ion-item button detail="false" (click)="presentModalMd(pkg.manifest.)">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ pkg.manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" (click)="presentModalLicense()">
|
||||
<ion-icon slot="icon-only" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item button detail="false" (click)="presentModalMd('instructions')">
|
||||
<ion-label>
|
||||
<h2>Instructions</h2>
|
||||
<p>Click to view instructions</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-col sizeSm="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item detail="false">
|
||||
<ion-item [href]="pkg.manifest['upstream-repo']" target="_blank" detail="false">
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ pkg.manifest['upstream-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" [href]="pkg.manifest['upstream-repo']" target="_blank">
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item detail="false">
|
||||
<ion-item [href]="pkg.manifest['wrapper-repo']" target="_blank" detail="false">
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ pkg.manifest['wrapper-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" [href]="pkg.manifest['wrapper-repo']" target="_blank">
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item detail="false">
|
||||
<ion-item [href]="pkg.manifest['support-site']" target="_blank" detail="false">
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ pkg.manifest['support-site'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" [href]="pkg.manifest['support-site']" target="_blank">
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item detail="false">
|
||||
<ion-item [href]="pkg.manifest['marketing-site']" target="_blank" detail="false">
|
||||
<ion-label>
|
||||
<h2>Marketing Site</h2>
|
||||
<p>{{ pkg.manifest['marketing-site'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" [href]="pkg.manifest['marketing-site']" target="_blank">
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="pkg.manifest['donation-url'] as donationUrl" detail="false">
|
||||
<ion-item *ngIf="pkg.manifest['donation-url'] as donationUrl" [href]="donationUrl" target="_blank" detail="false">
|
||||
<ion-label>
|
||||
<h2>Donation Site</h2>
|
||||
<p>{{ donationUrl }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" [href]="donationUrl" target="_blank">
|
||||
<ion-icon slot="icon-only" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { AlertController, ModalController, NavController } from '@ionic/angular'
|
||||
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { Emver } from 'src/app/services/emver.service'
|
||||
import { displayEmver } from 'src/app/pipes/emver.pipe'
|
||||
import { pauseFor } from 'src/app/util/misc.util'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
|
||||
import { pauseFor, Recommendation } from 'src/app/util/misc.util'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
||||
import { MarketplaceService } from '../marketplace.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-show',
|
||||
@@ -35,7 +35,7 @@ export class MarketplaceShowPage {
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly emver: Emver,
|
||||
private readonly patch: PatchDbModel,
|
||||
public marketplaceService: MarketplaceService,
|
||||
public readonly marketplaceService: MarketplaceService,
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
@@ -95,6 +95,17 @@ export class MarketplaceShowPage {
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async presentModalMd (content: string) {
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
content,
|
||||
},
|
||||
component: MarkdownPage,
|
||||
})
|
||||
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async install () {
|
||||
const { id, title, version, dependencies, alerts } = this.marketplaceService.pkgs[this.pkgId].manifest
|
||||
const { cancelled } = await wizardModal(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { ServerInfo } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ServerInfo } from 'src/app/services/patch-db/data-model'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
<ion-content class="ion-padding-top">
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item button (click)="presentModalValueEdit('name', ui['server-name'])">
|
||||
<ion-label>Embassy Name</ion-label>
|
||||
<ion-note slot="end">{{ ui['server-name'] }}</ion-note>
|
||||
</ion-item>
|
||||
<ion-item button (click)="presentModalValueEdit('autoCheckUpdates', ui['auto-check-updates'])">
|
||||
<ion-label>Auto Check for Updates</ion-label>
|
||||
<ion-note slot="end">{{ ui['auto-check-updates'] }}</ion-note>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { UIData } from 'src/app/models/patch-db/data-model'
|
||||
import { UIData } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'preferences',
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-item-group>
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key, partition.value)">
|
||||
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
|
||||
<ion-icon slot="start" name="save-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component } from '@angular/core'
|
||||
import { LoadingController, ModalController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
|
||||
import { DiskInfo, PartitionInfoEntry } from 'src/app/services/api/api-types'
|
||||
import { DiskInfo } from 'src/app/services/api/api-types'
|
||||
|
||||
@Component({
|
||||
selector: 'server-backup',
|
||||
@@ -42,10 +42,10 @@ export class ServerBackupPage {
|
||||
}
|
||||
}
|
||||
|
||||
async presentModal (logicalname: string, partition: PartitionInfoEntry): Promise<void> {
|
||||
async presentModal (logicalname: string): Promise<void> {
|
||||
const m = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
name: partition.label || logicalname,
|
||||
type: 'backup',
|
||||
},
|
||||
cssClass: 'alertlike-modal',
|
||||
component: BackupConfirmationComponent,
|
||||
|
||||
@@ -26,6 +26,10 @@ const routes: Routes = [
|
||||
path: 'preferences',
|
||||
loadChildren: () => import('./preferences/preferences.module').then(m => m.PreferencesPageModule),
|
||||
},
|
||||
{
|
||||
path: 'preferences/edit',
|
||||
loadChildren: () => import('./preferences/preferences.module').then(m => m.PreferencesPageModule),
|
||||
},
|
||||
{
|
||||
path: 'wifi',
|
||||
loadChildren: () => import('./wifi/wifi.module').then(m => m.WifiListPageModule),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ patch.watch$('ui', 'server-name') | async }}</ion-title>
|
||||
<ion-title>Embassy</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<badge-menu-button></badge-menu-button>
|
||||
</ion-buttons>
|
||||
|
||||
@@ -3,8 +3,6 @@ import { LoadingOptions } from '@ionic/core'
|
||||
import { AlertController, NavController } from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { ServerStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
@@ -13,7 +11,6 @@ import { ActivatedRoute } from '@angular/router'
|
||||
styleUrls: ['server-show.page.scss'],
|
||||
})
|
||||
export class ServerShowPage {
|
||||
ServerStatus = ServerStatus
|
||||
settings: ServerSettings = { }
|
||||
|
||||
constructor (
|
||||
@@ -22,7 +19,6 @@ export class ServerShowPage {
|
||||
private readonly apiService: ApiService,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly route: ActivatedRoute,
|
||||
public readonly patch: PatchDbModel,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
@@ -77,7 +73,6 @@ export class ServerShowPage {
|
||||
this.loader
|
||||
.of(LoadingSpinner(`Restarting...`))
|
||||
.displayDuringAsync( async () => {
|
||||
// this.serverModel.markUnreachable()
|
||||
await this.apiService.restartServer({ })
|
||||
})
|
||||
.catch(console.error)
|
||||
@@ -87,7 +82,6 @@ export class ServerShowPage {
|
||||
this.loader
|
||||
.of(LoadingSpinner(`Shutting down...`))
|
||||
.displayDuringAsync( async () => {
|
||||
// this.serverModel.markUnreachable()
|
||||
await this.apiService.shutdownServer({ })
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
<ion-content>
|
||||
|
||||
<ion-item-divider>Basic</ion-item-divider>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ToastController } from '@ionic/angular'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { ServerInfo } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ServerInfo } from 'src/app/services/patch-db/data-model'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<ng-container *ngIf="wifi">
|
||||
<ion-item button detail="false" *ngFor="let ssid of wifi.ssids" (click)="presentAction(ssid, wifi)">
|
||||
<ion-label>{{ ssid }}</ion-label>
|
||||
<ion-icon *ngIf="ssid === wifi.connected" name="wifi" color="success"></ion-icon>
|
||||
<ion-note slot="end" *ngIf="ssid === wifi.connected"><ion-text color="success">Connected</ion-text></ion-note>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
@@ -4,8 +4,8 @@ import { ApiService } from 'src/app/services/api/api.service'
|
||||
import { ActionSheetButton } from '@ionic/core'
|
||||
import { WifiService } from './wifi.service'
|
||||
import { LoaderService } from 'src/app/services/loader.service'
|
||||
import { WiFiInfo } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { WiFiInfo } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
|
||||
import { AlertController, ToastController } from '@ionic/angular'
|
||||
import { merge, Observable, timer } from 'rxjs'
|
||||
import { filter, map, take, tap } from 'rxjs/operators'
|
||||
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
|
||||
import { PatchDbModel } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry } from '../models/patch-db/data-model'
|
||||
import { PackageDataEntry } from '../services/patch-db/data-model'
|
||||
import { renderPkgStatus } from '../services/pkg-status-rendering.service'
|
||||
|
||||
@Pipe({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry } from '../models/patch-db/data-model'
|
||||
import { PackageDataEntry } from '../services/patch-db/data-model'
|
||||
import { FEStatus, renderPkgStatus } from '../services/pkg-status-rendering.service'
|
||||
|
||||
@Pipe({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry, Manifest } from '../models/patch-db/data-model'
|
||||
import { PackageDataEntry, Manifest } from '../services/patch-db/data-model'
|
||||
import { ConfigService, getManifest, hasUi } from '../services/config.service'
|
||||
|
||||
@Pipe({
|
||||
|
||||
@@ -2,7 +2,10 @@ import { ConfigCursor } from './config-cursor'
|
||||
import { TrackingModalController } from '../services/tracking-modal-controller.service'
|
||||
|
||||
export class ModalPresentable {
|
||||
constructor (private readonly trackingModalCtrl: TrackingModalController) { }
|
||||
|
||||
constructor (
|
||||
private readonly trackingModalCtrl: TrackingModalController,
|
||||
) { }
|
||||
|
||||
async presentModal (cursor: ConfigCursor<any>, callback: () => any) {
|
||||
const modal = await this.trackingModalCtrl.createConfigModal({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Dump, Revision } from 'patch-db-client'
|
||||
import { PackagePropertiesVersioned } from 'src/app/util/properties.util'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import { DataModel, DependencyError, Manifest, URL } from 'src/app/models/patch-db/data-model'
|
||||
import { DataModel, DependencyError, Manifest, URL } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
export module RR {
|
||||
|
||||
@@ -187,6 +187,8 @@ export interface AvailablePreview {
|
||||
|
||||
export interface AvailableShow {
|
||||
icon: URL
|
||||
license: URL
|
||||
instructions: URL
|
||||
manifest: Manifest
|
||||
categories: string[]
|
||||
versions: string[]
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
import { Subject, Observable } from 'rxjs'
|
||||
import { Http, Update, Operation, Revision } from 'patch-db-client'
|
||||
import { Http, Update, Operation, Revision, Source, Store } from 'patch-db-client'
|
||||
import { RR } from './api-types'
|
||||
import { DataModel } from 'src/app/models/patch-db/data-model'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
export abstract class ApiService implements Http<DataModel> {
|
||||
export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
||||
protected readonly sync = new Subject<Update<DataModel>>()
|
||||
private syncing = true
|
||||
|
||||
/** PatchDb Source interface. Post/Patch requests provide a source of patches to the db. */
|
||||
// sequenceStream '_' is not used by the live api, but is overridden by the mock
|
||||
watch$ (_?: Observable<number>): Observable<Update<DataModel>> {
|
||||
return this.sync.asObservable().pipe(filter(() => this.syncing))
|
||||
watch$ (_?: Store<DataModel>): Observable<Update<DataModel>> {
|
||||
return this.sync.asObservable()
|
||||
}
|
||||
|
||||
// used for determining internet connectivity
|
||||
abstract echo (): Promise<string>
|
||||
|
||||
// for getting static files: ex icons, instructions, licenses
|
||||
abstract getStatic (url: string): Promise<string>
|
||||
|
||||
@@ -189,18 +184,4 @@ export abstract class ApiService implements Http<DataModel> {
|
||||
}) as any
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO better types?
|
||||
// private async process<T, F extends (args: object) => Promise<{ response: T, revision?: Revision }>> (f: F, temps: Operation[] = []): Promise<T> {
|
||||
// let expireId = undefined
|
||||
// if (temps.length) {
|
||||
// expireId = uuid.v4()
|
||||
// this.sync.next({ patch: temps, expiredBy: expireId })
|
||||
// }
|
||||
// const { response, revision } = await f({ ...f.arguments, expireId })
|
||||
// if (revision) this.sync.next(revision)
|
||||
// return response
|
||||
// }
|
||||
}
|
||||
// used for type inference in syncResponse
|
||||
type ExtractResultPromise<T extends Promise<any>> = T extends Promise<infer R> ? Promise<R> : any
|
||||
|
||||
@@ -10,10 +10,6 @@ export class LiveApiService extends ApiService {
|
||||
private readonly http: HttpService,
|
||||
) { super() }
|
||||
|
||||
async echo (): Promise<string> {
|
||||
return this.http.rpcRequest({ method: 'echo', params: { } })
|
||||
}
|
||||
|
||||
async getStatic (url: string): Promise<string> {
|
||||
return this.http.httpRequest({ method: Method.GET, url })
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ import { Injectable } from '@angular/core'
|
||||
import { pauseFor } from '../../util/misc.util'
|
||||
import { ApiService } from './api.service'
|
||||
import { Observable } from 'rxjs'
|
||||
import { PatchOp, Update } from 'patch-db-client'
|
||||
import { DataModel, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { PatchOp, Store, Update } from 'patch-db-client'
|
||||
import { DataModel, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { RR } from './api-types'
|
||||
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
||||
import { Mock } from './mock-app-fixures'
|
||||
import { HttpService } from '../http.service'
|
||||
import { ConfigService } from '../config.service'
|
||||
import markdown from 'raw-loader!src/assets/markdown/md-sample.md'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
@Injectable()
|
||||
export class MockApiService extends ApiService {
|
||||
@@ -20,18 +21,20 @@ export class MockApiService extends ApiService {
|
||||
) { super() }
|
||||
|
||||
// every time a patch is returned from the mock, we override its sequence to be 1 more than the last sequence in the patch-db as provided by `o`.
|
||||
watch$ (sequenceStream: Observable<number>): Observable<Update<DataModel>> {
|
||||
sequenceStream.subscribe(i => this.sequence < i ? (this.sequence = i) : { })
|
||||
watch$ (store: Store<DataModel>): Observable<Update<DataModel>> {
|
||||
store.watchCache$().pipe(map(cache => cache.sequence)).subscribe(seq => {
|
||||
console.log('INCOMING: ', seq)
|
||||
if (this.sequence < seq) {
|
||||
console.log('hererereree')
|
||||
this.sequence = seq
|
||||
}
|
||||
})
|
||||
return super.watch$()
|
||||
}
|
||||
|
||||
async echo (): Promise<string> {
|
||||
console.log('echo...echo')
|
||||
return ''
|
||||
}
|
||||
|
||||
async getStatic (url: string): Promise<string> {
|
||||
return Mock.DbDump.value['package-data']['bitcoind']['static-files'].instructions
|
||||
await pauseFor(2000)
|
||||
return markdown
|
||||
}
|
||||
|
||||
// db
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { DependencyErrorType, DockerIoFormat, Manifest, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/models/patch-db/data-model'
|
||||
import { DependencyErrorType, DockerIoFormat, Manifest, PackageDataEntry, PackageMainStatus, PackageState, ServerStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { NotificationLevel, RR, ServerNotification, ServerNotifications } from './api-types'
|
||||
import markdown from 'raw-loader!./md-sample.md'
|
||||
|
||||
export module Mock {
|
||||
|
||||
export const MarketplaceEos: RR.GetMarketplaceEOSRes = {
|
||||
version: '1.0.0',
|
||||
headline: 'Our biggest release ever.',
|
||||
'release-notes': { '1.0.0': markdown },
|
||||
'release-notes': { '1.0.0': 'Some **Markdown** release _notes_' },
|
||||
}
|
||||
|
||||
export const AvailableList: RR.GetAvailableListRes = [
|
||||
@@ -402,6 +401,8 @@ export module Mock {
|
||||
'bitcoind': {
|
||||
'0.19.2': {
|
||||
icon: 'assets/img/service-icons/bitcoind.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.19.0',
|
||||
@@ -417,6 +418,8 @@ export module Mock {
|
||||
},
|
||||
'0.20.0': {
|
||||
icon: 'assets/img/service-icons/bitcoind.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.20.0',
|
||||
@@ -432,6 +435,8 @@ export module Mock {
|
||||
},
|
||||
'0.21.0': {
|
||||
icon: 'assets/img/service-icons/bitcoind.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
version: '0.21.0',
|
||||
@@ -448,6 +453,8 @@ export module Mock {
|
||||
},
|
||||
'latest': {
|
||||
icon: 'assets/img/service-icons/bitcoind.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestBitcoind,
|
||||
'release-notes': 'Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.',
|
||||
@@ -465,6 +472,8 @@ export module Mock {
|
||||
'lnd': {
|
||||
'0.11.0': {
|
||||
icon: 'assets/img/service-icons/lnd.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.0',
|
||||
@@ -490,6 +499,8 @@ export module Mock {
|
||||
},
|
||||
'0.11.1': {
|
||||
icon: 'assets/img/service-icons/lnd.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: {
|
||||
...Mock.MockManifestLnd,
|
||||
version: '0.11.1',
|
||||
@@ -515,6 +526,8 @@ export module Mock {
|
||||
},
|
||||
'latest': {
|
||||
icon: 'assets/img/service-icons/lnd.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestLnd,
|
||||
categories: ['bitcoin', 'lightning', 'cryptocurrency'],
|
||||
versions: ['0.11.0', '0.11.1'],
|
||||
@@ -538,6 +551,8 @@ export module Mock {
|
||||
'bitcoin-proxy': {
|
||||
'latest': {
|
||||
icon: 'assets/img/service-icons/bitcoin-proxy.png',
|
||||
license: 'licenseUrl',
|
||||
instructions: 'instructionsUrl',
|
||||
manifest: Mock.MockManifestBitcoinProxy,
|
||||
categories: ['bitcoin'],
|
||||
versions: ['0.2.2'],
|
||||
@@ -559,9 +574,9 @@ export module Mock {
|
||||
export const bitcoind: PackageDataEntry = {
|
||||
state: PackageState.Installed,
|
||||
'static-files': {
|
||||
license: 'licenseURL',
|
||||
license: 'licenseUrl', // /public/package-data/bitcoind/0.21.1/LICENSE.md,
|
||||
icon: 'assets/img/service-icons/bitcoind.png',
|
||||
instructions: markdown, // /public/package-data/bitcoind/0.21.1/INSTRUCTIONS.md
|
||||
instructions: 'instructionsUrl', // /public/package-data/bitcoind/0.21.1/INSTRUCTIONS.md
|
||||
},
|
||||
'temp-manifest': undefined,
|
||||
installed: {
|
||||
@@ -610,9 +625,9 @@ export module Mock {
|
||||
export const lnd: PackageDataEntry = {
|
||||
state: PackageState.Installed,
|
||||
'static-files': {
|
||||
license: 'licenseURL',
|
||||
license: 'licenseUrl', // /public/package-data/lnd/0.21.1/LICENSE.md,
|
||||
icon: 'assets/img/service-icons/lnd.png',
|
||||
instructions: markdown, // /public/package-data/bitcoind/0.21.1/INSTRUCTIONS.md
|
||||
instructions: 'instructionsUrl', // /public/package-data/lnd/0.21.1/INSTRUCTIONS.md
|
||||
},
|
||||
'temp-manifest': undefined,
|
||||
installed: {
|
||||
@@ -662,9 +677,9 @@ export module Mock {
|
||||
export const bitcoinproxy: PackageDataEntry = {
|
||||
state: PackageState.Installed,
|
||||
'static-files': {
|
||||
license: 'licenseURL',
|
||||
license: 'licenseUrl', // /public/package-data/bitcoinproxy/0.21.1/LICENSE.md,
|
||||
icon: 'assets/img/service-icons/bitcoin-proxy.png',
|
||||
instructions: '*These are some instructions.*', // /public/package-data/bitcoinproxy/0.2.2/INSTRUCTIONS.md
|
||||
instructions: 'instructionsUrl', // /public/package-data/bitcoinproxy/0.2.2/INSTRUCTIONS.md
|
||||
},
|
||||
'temp-manifest': undefined,
|
||||
installed: {
|
||||
@@ -737,7 +752,6 @@ export module Mock {
|
||||
'lnd': lnd,
|
||||
},
|
||||
ui: {
|
||||
'server-name': 'My Embassy',
|
||||
'welcome-ack': '1.0.0',
|
||||
'auto-check-updates': true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@ import { BehaviorSubject, Observable } from 'rxjs'
|
||||
import { distinctUntilChanged } from 'rxjs/operators'
|
||||
import { ApiService } from './api/api.service'
|
||||
import { Storage } from '@ionic/storage'
|
||||
import { StorageKeys } from '../models/storage-keys'
|
||||
|
||||
export enum AuthState {
|
||||
UNVERIFIED,
|
||||
@@ -14,6 +13,7 @@ export enum AuthState {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
private readonly LOGGED_IN_KEY = 'loggedInKey'
|
||||
private readonly authState$: BehaviorSubject<AuthState> = new BehaviorSubject(AuthState.INITIALIZING)
|
||||
|
||||
constructor (
|
||||
@@ -22,7 +22,7 @@ export class AuthService {
|
||||
) { }
|
||||
|
||||
async init (): Promise<void> {
|
||||
const loggedIn = await this.storage.get(StorageKeys.LOGGED_IN_KEY)
|
||||
const loggedIn = await this.storage.get(this.LOGGED_IN_KEY)
|
||||
this.authState$.next( loggedIn ? AuthState.VERIFIED : AuthState.UNVERIFIED)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class AuthService {
|
||||
|
||||
async login (password: string): Promise<void> {
|
||||
await this.api.login({ password })
|
||||
await this.storage.set(StorageKeys.LOGGED_IN_KEY, true)
|
||||
await this.storage.set(this.LOGGED_IN_KEY, true)
|
||||
this.authState$.next(AuthState.VERIFIED)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { InstalledPackageDataEntry, InterfaceDef, Manifest, PackageDataEntry, PackageMainStatus, PackageState } from '../models/patch-db/data-model'
|
||||
import { InstalledPackageDataEntry, InterfaceDef, Manifest, PackageDataEntry, PackageMainStatus, PackageState } from './patch-db/data-model'
|
||||
|
||||
const { patchDb, api, mocks } = require('../../../ui-config.json') as UiConfig
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, Subscription } from 'rxjs'
|
||||
import { ConnectionStatus } from '../../../../../patch-db/client/dist'
|
||||
import { DataModel } from '../models/patch-db/data-model'
|
||||
import { PatchDbModel } from '../models/patch-db/patch-db-model'
|
||||
import { BehaviorSubject, combineLatest, fromEvent, merge, Subscription } from 'rxjs'
|
||||
import { DataModel } from './patch-db/data-model'
|
||||
import { ConnectionStatus, PatchDbModel } from './patch-db/patch-db.service'
|
||||
import { HttpService, Method } from './http.service'
|
||||
import { distinctUntilChanged } from 'rxjs/operators'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -19,7 +19,7 @@ export class ConnectionService {
|
||||
private readonly patch: PatchDbModel,
|
||||
) { }
|
||||
|
||||
watch$ () {
|
||||
watchFailure$ () {
|
||||
return this.connectionFailure$.asObservable()
|
||||
}
|
||||
|
||||
@@ -39,22 +39,27 @@ export class ConnectionService {
|
||||
this.networkState$.next(event.type === 'online')
|
||||
}),
|
||||
|
||||
combineLatest([this.networkState$, this.patch.connectionStatus$()])
|
||||
combineLatest([this.networkState$.pipe(distinctUntilChanged()), this.patch.watchConnection$().pipe(distinctUntilChanged())])
|
||||
.subscribe(async ([network, connectionStatus]) => {
|
||||
console.log('CONNECTION STATUS', connectionStatus)
|
||||
if (connectionStatus !== ConnectionStatus.Disconnected) {
|
||||
this.connectionFailure$.next(ConnectionFailure.None)
|
||||
} else if (!network) {
|
||||
this.connectionFailure$.next(ConnectionFailure.Network)
|
||||
} else {
|
||||
console.log('diagnosing')
|
||||
this.connectionFailure$.next(ConnectionFailure.Diagnosing)
|
||||
const torSuccess = await this.testAddrs(this.addrs.tor)
|
||||
if (torSuccess) {
|
||||
console.log('TOR SUCCESS, EMBASSY IS PROBLEM')
|
||||
this.connectionFailure$.next(ConnectionFailure.Embassy)
|
||||
} else {
|
||||
const clearnetSuccess = await this.testAddrs(this.addrs.clearnet)
|
||||
if (clearnetSuccess) {
|
||||
console.log('CLEARNET SUCCESS, TOR IS PROBLEM')
|
||||
this.connectionFailure$.next(ConnectionFailure.Tor)
|
||||
} else {
|
||||
console.log('INTERNET IS PROBLEM')
|
||||
this.connectionFailure$.next(ConnectionFailure.Internet)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,6 @@ export interface InterfaceInfo {
|
||||
export type URL = string
|
||||
|
||||
export interface UIData {
|
||||
'server-name': string
|
||||
'welcome-ack': string
|
||||
'auto-check-updates': boolean
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import { PollSource, Source, WebsocketSource } from 'patch-db-client'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { DataModel } from './data-model'
|
||||
import { LocalStorageBootstrap } from './local-storage-bootstrap'
|
||||
import { PatchDbModel } from './patch-db-model'
|
||||
import { PatchDbModel } from './patch-db.service'
|
||||
import { ApiService } from 'src/app/services/api/api.service'
|
||||
|
||||
export function PatchDbModelFactory (
|
||||
config: ConfigService,
|
||||
bootstrapper: LocalStorageBootstrap,
|
||||
http: ApiService,
|
||||
apiService: ApiService,
|
||||
): PatchDbModel {
|
||||
|
||||
const { mocks, patchDb: { poll }, isConsulate } = config
|
||||
@@ -17,13 +17,13 @@ export function PatchDbModelFactory (
|
||||
|
||||
if (mocks.enabled) {
|
||||
if (mocks.connection === 'poll') {
|
||||
source = new PollSource({ ...poll }, http)
|
||||
source = new PollSource({ ...poll }, apiService)
|
||||
} else {
|
||||
source = new WebsocketSource(`ws://localhost:${config.mocks.wsPort}/db`)
|
||||
}
|
||||
} else {
|
||||
if (isConsulate) {
|
||||
source = new PollSource({ ...poll }, http)
|
||||
source = new PollSource({ ...poll }, apiService)
|
||||
} else {
|
||||
const protocol = window.location.protocol === 'http:' ? 'ws' : 'wss'
|
||||
const host = window.location.host
|
||||
@@ -31,5 +31,5 @@ export function PatchDbModelFactory (
|
||||
}
|
||||
}
|
||||
|
||||
return new PatchDbModel(bootstrapper, source)
|
||||
return new PatchDbModel(bootstrapper, [source, apiService])
|
||||
}
|
||||
@@ -1,27 +1,34 @@
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core'
|
||||
import { Bootstrapper, ConnectionStatus, PatchDB, Source, Store } from 'patch-db-client'
|
||||
import { Observable, of, Subscription } from 'rxjs'
|
||||
import { catchError, debounceTime, map } from 'rxjs/operators'
|
||||
import { Bootstrapper, PatchDB, Source, Store } from 'patch-db-client'
|
||||
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'
|
||||
import { catchError, debounceTime, map, tap } from 'rxjs/operators'
|
||||
import { DataModel } from './data-model'
|
||||
|
||||
export const BOOTSTRAPPER = new InjectionToken<Bootstrapper<DataModel>>('app.config')
|
||||
export const PATCH_SOURCE = new InjectionToken<Source<DataModel>>('app.config')
|
||||
|
||||
export enum ConnectionStatus {
|
||||
Initializing = 'initializing',
|
||||
Connected = 'connected',
|
||||
Disconnected = 'disconnected',
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PatchDbModel {
|
||||
patchDb: PatchDB<DataModel>
|
||||
connectionStatus$ = new BehaviorSubject(ConnectionStatus.Initializing)
|
||||
private patchDb: PatchDB<DataModel>
|
||||
private patchSub: Subscription
|
||||
|
||||
constructor (
|
||||
@Inject(BOOTSTRAPPER) private readonly bootstrapper: Bootstrapper<DataModel>,
|
||||
@Inject(PATCH_SOURCE) private readonly source: Source<DataModel>,
|
||||
@Inject(PATCH_SOURCE) private readonly sources: Source<DataModel>[],
|
||||
) { }
|
||||
|
||||
async init (): Promise<void> {
|
||||
const cache = await this.bootstrapper.init()
|
||||
this.patchDb = new PatchDB(this.source, cache)
|
||||
this.patchDb = new PatchDB(this.sources, cache)
|
||||
}
|
||||
|
||||
start (): void {
|
||||
@@ -32,12 +39,14 @@ export class PatchDbModel {
|
||||
.pipe(debounceTime(500))
|
||||
.subscribe({
|
||||
next: cache => {
|
||||
console.log('saving cache: ', cache.sequence)
|
||||
console.log('saving cacheee: ', cache)
|
||||
this.connectionStatus$.next(ConnectionStatus.Connected)
|
||||
this.bootstrapper.update(cache)
|
||||
},
|
||||
error: e => {
|
||||
console.error('patch-db-sync sub ERROR', e)
|
||||
this.start()
|
||||
this.connectionStatus$.next(ConnectionStatus.Disconnected)
|
||||
// this.start()
|
||||
},
|
||||
complete: () => {
|
||||
console.error('patch-db-sync sub COMPLETE')
|
||||
@@ -56,19 +65,20 @@ export class PatchDbModel {
|
||||
}
|
||||
|
||||
connected$ (): Observable<boolean> {
|
||||
return this.patchDb.connectionStatus$
|
||||
return this.connectionStatus$
|
||||
.pipe(
|
||||
map(status => status === ConnectionStatus.Connected),
|
||||
)
|
||||
}
|
||||
|
||||
connectionStatus$ (): Observable<ConnectionStatus> {
|
||||
return this.patchDb.connectionStatus$.asObservable()
|
||||
watchConnection$ (): Observable<ConnectionStatus> {
|
||||
return this.connectionStatus$.asObservable()
|
||||
}
|
||||
|
||||
watch$: Store<DataModel> ['watch$'] = (...args: (string | number)[]): Observable<DataModel> => {
|
||||
// console.log('WATCHING')
|
||||
return this.patchDb.store.watch$(...(args as [])).pipe(
|
||||
tap(cache => console.log('CHANGE IN STORE', cache)),
|
||||
catchError(e => {
|
||||
console.error(e)
|
||||
return of(e.message)
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HealthCheckResultLoading, MainStatusRunning, PackageDataEntry, PackageMainStatus, PackageState, Status } from '../models/patch-db/data-model'
|
||||
import { HealthCheckResultLoading, MainStatusRunning, PackageDataEntry, PackageMainStatus, PackageState, Status } from './patch-db/data-model'
|
||||
|
||||
export function renderPkgStatus (pkg: PackageDataEntry): PkgStatusRendering {
|
||||
switch (pkg.state) {
|
||||
@@ -15,7 +15,7 @@ function handleInstalledState (status: Status): PkgStatusRendering {
|
||||
}
|
||||
|
||||
if (Object.keys(status['dependency-errors']).length) {
|
||||
return { display: 'Needs Attention', color: 'warning', showDots: false, feStatus: FEStatus.DependencyIssue }
|
||||
return { display: 'Dependency Issue', color: 'warning', showDots: false, feStatus: FEStatus.DependencyIssue }
|
||||
}
|
||||
|
||||
switch (status.main.status) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { AppConfigValuePage } from '../modals/app-config-value/app-config-value.page'
|
||||
import { ApiService } from './api/api.service'
|
||||
import { ConfigSpec } from '../pkg-config/config-types'
|
||||
import { ConfigCursor } from '../pkg-config/config-cursor'
|
||||
import { SSHService } from '../pages/server-routes/developer-routes/dev-ssh-keys/ssh.service'
|
||||
import { TrackingModalController } from './tracking-modal-controller.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -12,7 +12,7 @@ import { SSHService } from '../pages/server-routes/developer-routes/dev-ssh-keys
|
||||
export class ServerConfigService {
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly trackingModalCtrl: TrackingModalController,
|
||||
private readonly apiService: ApiService,
|
||||
private readonly sshService: SSHService,
|
||||
) { }
|
||||
@@ -20,10 +20,10 @@ export class ServerConfigService {
|
||||
async presentModalValueEdit (key: string, current?: string) {
|
||||
const cursor = new ConfigCursor(serverConfig, { [key]: current }).seekNext(key)
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
const modal = await this.trackingModalCtrl.create({
|
||||
backdropDismiss: false,
|
||||
component: AppConfigValuePage,
|
||||
presentingElement: await this.modalCtrl.getTop(),
|
||||
presentingElement: await this.trackingModalCtrl.getTop(),
|
||||
componentProps: {
|
||||
cursor,
|
||||
saveFn: this.saveFns[key],
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Inject, Injectable } from '@angular/core'
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core'
|
||||
import { Observable, Subject } from 'rxjs'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { ModalOptions } from '@ionic/core'
|
||||
import { APP_CONFIG_COMPONENT_MAPPING } from '../modals/app-config-injectable/modal-injectable-token'
|
||||
import { AppConfigComponentMapping } from '../modals/app-config-injectable/modal-injectable-type'
|
||||
import { ValueSpec } from '../pkg-config/config-types'
|
||||
import { AppConfigComponentMapping } from '../modals/app-config-injectable'
|
||||
|
||||
export const APP_CONFIG_COMPONENT_MAPPING = new InjectionToken<string>('APP_CONFIG_COMPONENTS')
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
export type Omit<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>
|
||||
export type PromiseRes<T> = { result: 'resolve', value: T } | { result: 'reject', value: Error }
|
||||
|
||||
export type Recommendation = {
|
||||
dependentId: string
|
||||
dependentTitle: string
|
||||
dependentIcon: string,
|
||||
description: string
|
||||
version?: string
|
||||
}
|
||||
|
||||
import { OperatorFunction } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ ion-badge {
|
||||
ion-item-divider {
|
||||
--background: transparent;
|
||||
text-transform: uppercase;
|
||||
margin-top: 16px;
|
||||
margin-top: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@@ -161,9 +161,9 @@ ion-popover {
|
||||
|
||||
.modal-wrapper {
|
||||
position: absolute;
|
||||
height: 90%!important;
|
||||
height: 90% !important;
|
||||
top: 5%;
|
||||
width: 90%!important;
|
||||
width: 90% !important;
|
||||
left: 5%;
|
||||
display: block;
|
||||
}
|
||||
@@ -171,37 +171,30 @@ ion-popover {
|
||||
@media (min-width:1000px) {
|
||||
.modal-wrapper {
|
||||
position: absolute;
|
||||
height: 80% !important;
|
||||
top: 10%;
|
||||
width: 60% !important;
|
||||
left: 20%;
|
||||
height: 60% !important;
|
||||
top: 20%;
|
||||
width: 50% !important;
|
||||
left: 25%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (min-width:1000px) {
|
||||
.alertlike-modal {
|
||||
--backdrop-opacity: 0.7 !important;
|
||||
.modal-wrapper {
|
||||
height: 46% !important;
|
||||
width: 46% !important;
|
||||
left: 26% !important;
|
||||
top: 26% !important;
|
||||
--box-shadow: none !important;
|
||||
}
|
||||
.alertlike-modal {
|
||||
.modal-wrapper {
|
||||
height: 50% !important;
|
||||
top: 25% !important;
|
||||
width: 90% !important;
|
||||
left: 5% !important;
|
||||
--box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.alertlike-modal {
|
||||
--backdrop-opacity: 0.7 !important;
|
||||
.modal-wrapper {
|
||||
height: 60% !important;
|
||||
width: 80% !important;
|
||||
left: 10% !important;
|
||||
top: 20% !important;
|
||||
--box-shadow: none !important;
|
||||
@media (min-width:1000px) {
|
||||
.alertlike-modal {
|
||||
.modal-wrapper {
|
||||
width: 50% !important;
|
||||
left: 25% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,14 +227,9 @@ ion-loading {
|
||||
}
|
||||
|
||||
ion-modal {
|
||||
--box-shadow: 3px 4px 14px 3px rgba(255,255,255,0.3) !important;
|
||||
}
|
||||
|
||||
ion-modal:first-of-type {
|
||||
--backdrop-opacity: 0.75 !important;
|
||||
}
|
||||
|
||||
|
||||
.swiper-pagination {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"mocks": {
|
||||
"enabled": true,
|
||||
"connection": "ws",
|
||||
"connection": "poll",
|
||||
"rpcPort": "5959",
|
||||
"wsPort": "5960",
|
||||
"maskAs": "tor",
|
||||
|
||||
Reference in New Issue
Block a user