mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Feature/shared refactor (#1176)
* refactor: move most of the shared entities to @start8labs/shared library
This commit is contained in:
@@ -389,10 +389,10 @@
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "projects/shared/tsconfig.lib.prod.json"
|
||||
"tsConfig": "projects/shared/tsconfig.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "projects/shared/tsconfig.lib.json"
|
||||
"tsConfig": "projects/shared/tsconfig.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
|
||||
2582
frontend/package-lock.json
generated
2582
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"check": "npm run check:shared && npm run check:ui && npm run check:setup-wizard && npm run check:diagnostic-ui",
|
||||
"check:shared": "tsc --project projects/shared/tsconfig.lib.json --noEmit --skipLibCheck",
|
||||
"check:shared": "tsc --project projects/shared/tsconfig.json --noEmit --skipLibCheck",
|
||||
"check:diagnostic-ui": "tsc --project projects/diagnostic-ui/tsconfig.json --noEmit --skipLibCheck",
|
||||
"check:setup-wizard": "tsc --project projects/setup-wizard/tsconfig.json --noEmit --skipLibCheck",
|
||||
"check:ui": "tsc --project projects/ui/tsconfig.json --noEmit --skipLibCheck",
|
||||
|
||||
@@ -10,7 +10,7 @@ import { MockApiService } from './services/api/mock-api.service'
|
||||
import { LiveApiService } from './services/api/live-api.service'
|
||||
import { HttpService } from './services/http.service'
|
||||
import { GlobalErrorHandler } from './services/global-error-handler.service'
|
||||
import { WorkspaceConfig } from '@shared'
|
||||
import { WorkspaceConfig } from '@start9labs/shared'
|
||||
|
||||
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { Injectable } from "@angular/core"
|
||||
import { pauseFor } from "../../util/misc.util"
|
||||
import { ApiService, GetErrorRes, GetLogsReq, GetLogsRes, Log } from "./api.service"
|
||||
import { Injectable } from '@angular/core'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
import {
|
||||
ApiService,
|
||||
GetErrorRes,
|
||||
GetLogsReq,
|
||||
GetLogsRes,
|
||||
Log,
|
||||
} from './api.service'
|
||||
|
||||
@Injectable()
|
||||
export class MockApiService extends ApiService {
|
||||
|
||||
constructor () { super() }
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
async getError(): Promise<GetErrorRes> {
|
||||
await pauseFor(1000)
|
||||
return {
|
||||
code: 15,
|
||||
message: 'Unknown Embassy',
|
||||
data: { details: 'Some details about the error here' }
|
||||
data: { details: 'Some details about the error here' },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +36,15 @@ export class MockApiService extends ApiService {
|
||||
async getLogs(params: GetLogsReq): Promise<GetLogsRes> {
|
||||
await pauseFor(1000)
|
||||
let entries: Log[]
|
||||
if (Math.random() < .2) {
|
||||
if (Math.random() < 0.2) {
|
||||
entries = packageLogs
|
||||
} else {
|
||||
const arrLength = params.limit ? Math.ceil(params.limit / packageLogs.length) : 10
|
||||
entries = new Array(arrLength).fill(packageLogs).reduce((acc, val) => acc.concat(val), [])
|
||||
const arrLength = params.limit
|
||||
? Math.ceil(params.limit / packageLogs.length)
|
||||
: 10
|
||||
entries = new Array(arrLength)
|
||||
.fill(packageLogs)
|
||||
.reduce((acc, val) => acc.concat(val), [])
|
||||
}
|
||||
return {
|
||||
entries,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function pauseFor (ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import { LoadingPageModule } from './pages/loading/loading.module'
|
||||
import { ProdKeyModalModule } from './modals/prod-key-modal/prod-key-modal.module'
|
||||
import { ProductKeyPageModule } from './pages/product-key/product-key.module'
|
||||
import { RecoverPageModule } from './pages/recover/recover.module'
|
||||
import { WorkspaceConfig } from '@shared'
|
||||
import { WorkspaceConfig } from '@start9labs/shared'
|
||||
|
||||
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import { EmbassyPage } from './embassy.page'
|
||||
import { PasswordPageModule } from '../../modals/password/password.module'
|
||||
import { EmbassyPageRoutingModule } from './embassy-routing.module'
|
||||
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -14,7 +14,7 @@ import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
IonicModule,
|
||||
EmbassyPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
PipesModule,
|
||||
UnitConversionPipesModule,
|
||||
],
|
||||
declarations: [EmbassyPage],
|
||||
})
|
||||
|
||||
@@ -2,11 +2,11 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import { DriveStatusComponent, RecoverPage } from './recover.page'
|
||||
import { PasswordPageModule } from '../../modals/password/password.module'
|
||||
import { ProdKeyModalModule } from '../../modals/prod-key-modal/prod-key-modal.module'
|
||||
import { RecoverPageRoutingModule } from './recover-routing.module'
|
||||
import { PipesModule } from 'src/app/pipes/pipe.module'
|
||||
import { CifsModalModule } from 'src/app/modals/cifs-modal/cifs-modal.module'
|
||||
|
||||
@NgModule({
|
||||
@@ -18,7 +18,7 @@ import { CifsModalModule } from 'src/app/modals/cifs-modal/cifs-modal.module'
|
||||
RecoverPageRoutingModule,
|
||||
PasswordPageModule,
|
||||
ProdKeyModalModule,
|
||||
PipesModule,
|
||||
UnitConversionPipesModule,
|
||||
CifsModalModule,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
|
||||
// converts bytes to gigabytes
|
||||
@Pipe({
|
||||
name: 'convertBytes',
|
||||
})
|
||||
export class ConvertBytesPipe implements PipeTransform {
|
||||
transform (bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { ConvertBytesPipe } from './convert-bytes.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [ConvertBytesPipe],
|
||||
imports: [],
|
||||
exports: [ConvertBytesPipe],
|
||||
})
|
||||
|
||||
export class PipesModule { }
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { pauseFor } from 'src/app/util/misc.util'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
import { ApiService, CifsRecoverySource, SetupEmbassyReq } from './api.service'
|
||||
|
||||
let tries = 0
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { ApiService, CifsRecoverySource, DiskRecoverySource } from './api/api.service'
|
||||
import {
|
||||
ApiService,
|
||||
CifsRecoverySource,
|
||||
DiskRecoverySource,
|
||||
} from './api/api.service'
|
||||
import { ErrorToastService } from './error-toast.service'
|
||||
import { pauseFor } from '../util/misc.util'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -17,7 +21,11 @@ export class StateService {
|
||||
recoverySource: CifsRecoverySource | DiskRecoverySource
|
||||
recoveryPassword: string
|
||||
|
||||
dataTransferProgress: { bytesTransferred: number, totalBytes: number, complete: boolean } | null
|
||||
dataTransferProgress: {
|
||||
bytesTransferred: number
|
||||
totalBytes: number
|
||||
complete: boolean
|
||||
} | null
|
||||
dataProgress = 0
|
||||
dataCompletionSubject = new BehaviorSubject(false)
|
||||
|
||||
@@ -34,19 +42,18 @@ export class StateService {
|
||||
this.polling = true
|
||||
await pauseFor(500)
|
||||
|
||||
if (
|
||||
this.dataTransferProgress?.complete
|
||||
) {
|
||||
if (this.dataTransferProgress?.complete) {
|
||||
this.dataCompletionSubject.next(true)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let progress
|
||||
try {
|
||||
progress = await this.apiService.getRecoveryStatus()
|
||||
} catch (e) {
|
||||
this.errorToastService.present(`${e.message}: ${e.details}.\nRestart Embassy to try again.`)
|
||||
this.errorToastService.present(
|
||||
`${e.message}: ${e.details}.\nRestart Embassy to try again.`,
|
||||
)
|
||||
}
|
||||
if (progress) {
|
||||
this.dataTransferProgress = {
|
||||
@@ -55,7 +62,9 @@ export class StateService {
|
||||
complete: progress.complete,
|
||||
}
|
||||
if (this.dataTransferProgress.totalBytes) {
|
||||
this.dataProgress = this.dataTransferProgress.bytesTransferred / this.dataTransferProgress.totalBytes
|
||||
this.dataProgress =
|
||||
this.dataTransferProgress.bytesTransferred /
|
||||
this.dataTransferProgress.totalBytes
|
||||
}
|
||||
}
|
||||
setTimeout(() => this.pollDataTransferProgress(), 0) // prevent call stack from growing
|
||||
@@ -68,7 +77,10 @@ export class StateService {
|
||||
this.cert = ret['root-ca']
|
||||
}
|
||||
|
||||
async setupEmbassy (storageLogicalname: string, password: string): Promise<void> {
|
||||
async setupEmbassy(
|
||||
storageLogicalname: string,
|
||||
password: string,
|
||||
): Promise<void> {
|
||||
const ret = await this.apiService.setupEmbassy({
|
||||
'embassy-logicalname': storageLogicalname,
|
||||
'embassy-password': password,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export const pauseFor = (ms: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "shared",
|
||||
"name": "@start9labs/shared",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^13.2.0",
|
||||
"@angular/core": "^13.2.0"
|
||||
"@angular/core": "^13.2.0",
|
||||
"@start9labs/emver": "^0.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ion-grid style="height: 100%;">
|
||||
<ion-row class="ion-align-items-center ion-text-center" style="height: 100%;">
|
||||
<ion-grid class="full-height">
|
||||
<ion-row class="ion-align-items-center ion-text-center full-height">
|
||||
<ion-col>
|
||||
<ion-spinner name="lines" color="warning"></ion-spinner>
|
||||
<p>{{ text }}</p>
|
||||
@@ -1,18 +1,11 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { TextSpinnerComponent } from './text-spinner.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { TextSpinnerComponent } from './text-spinner.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
TextSpinnerComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
],
|
||||
declarations: [TextSpinnerComponent],
|
||||
imports: [CommonModule, IonicModule],
|
||||
exports: [TextSpinnerComponent],
|
||||
})
|
||||
export class TextSpinnerComponentModule {}
|
||||
@@ -0,0 +1,3 @@
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -6,5 +6,5 @@ import { Component, Input } from '@angular/core'
|
||||
styleUrls: ['./text-spinner.component.scss'],
|
||||
})
|
||||
export class TextSpinnerComponent {
|
||||
@Input() text: string
|
||||
@Input() text = ''
|
||||
}
|
||||
12
frontend/projects/shared/src/pipes/emver/emver.module.ts
Normal file
12
frontend/projects/shared/src/pipes/emver/emver.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import {
|
||||
EmverComparesPipe,
|
||||
EmverDisplayPipe,
|
||||
EmverSatisfiesPipe,
|
||||
} from './emver.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [EmverComparesPipe, EmverDisplayPipe, EmverSatisfiesPipe],
|
||||
exports: [EmverComparesPipe, EmverDisplayPipe, EmverSatisfiesPipe],
|
||||
})
|
||||
export class EmverPipesModule {}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { Emver } from '../services/emver.service'
|
||||
import { Emver } from '../../services/emver.service'
|
||||
|
||||
@Pipe({
|
||||
name: 'satisfiesEmver',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { MarkdownPipe } from './markdown.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [MarkdownPipe],
|
||||
exports: [MarkdownPipe],
|
||||
})
|
||||
export class MarkdownPipeModule {}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { isEmptyObject } from '../util/misc.util'
|
||||
import { isEmptyObject } from '../../util/misc.util'
|
||||
|
||||
@Pipe({
|
||||
name: 'empty',
|
||||
@@ -0,0 +1,9 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { IncludesPipe } from './includes.pipe'
|
||||
import { EmptyPipe } from './empty.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [IncludesPipe, EmptyPipe],
|
||||
exports: [IncludesPipe, EmptyPipe],
|
||||
})
|
||||
export class SharedPipesModule {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { ConvertBytesPipe, DurationToSecondsPipe } from './unit-conversion.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [ConvertBytesPipe, DurationToSecondsPipe],
|
||||
exports: [ConvertBytesPipe, DurationToSecondsPipe],
|
||||
})
|
||||
export class UnitConversionPipesModule {}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
|
||||
// converts bytes to gigabytes
|
||||
@Pipe({
|
||||
name: 'convertBytes',
|
||||
})
|
||||
export class ConvertBytesPipe implements PipeTransform {
|
||||
transform(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({
|
||||
name: 'durationToSeconds',
|
||||
})
|
||||
export class DurationToSecondsPipe implements PipeTransform {
|
||||
transform(duration: string | null): number {
|
||||
if (!duration) return 0
|
||||
const splitUnit = duration.match(/^([0-9]*(\.[0-9]+)?)(ns|µs|ms|s|m|d)$/)
|
||||
const unit = splitUnit[3]
|
||||
const num = splitUnit[1]
|
||||
return Number(num) * unitsToSeconds[unit]
|
||||
}
|
||||
}
|
||||
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
|
||||
const unitsToSeconds = {
|
||||
ns: 1e-9,
|
||||
µs: 1e-6,
|
||||
ms: 0.001,
|
||||
s: 1,
|
||||
m: 60,
|
||||
h: 3600,
|
||||
d: 86400,
|
||||
}
|
||||
@@ -1,5 +1,28 @@
|
||||
/*
|
||||
* Public API Surface of shared
|
||||
* Public API Surface of @start9labs/shared
|
||||
*/
|
||||
|
||||
export * from './components/text-spinner/text-spinner.component.module'
|
||||
export * from './components/text-spinner/text-spinner.component'
|
||||
|
||||
export * from './pipes/emver/emver.module'
|
||||
export * from './pipes/emver/emver.pipe'
|
||||
export * from './pipes/markdown/markdown.module'
|
||||
export * from './pipes/markdown/markdown.pipe'
|
||||
export * from './pipes/shared/shared.module'
|
||||
export * from './pipes/shared/empty.pipe'
|
||||
export * from './pipes/shared/includes.pipe'
|
||||
export * from './pipes/unit-conversion/unit-conversion.module'
|
||||
export * from './pipes/unit-conversion/unit-conversion.pipe'
|
||||
|
||||
export * from './services/destroy.service'
|
||||
export * from './services/emver.service'
|
||||
|
||||
export * from './types/dependent-info'
|
||||
export * from './types/install-progress'
|
||||
export * from './types/package-state'
|
||||
export * from './types/progress-data'
|
||||
export * from './types/workspace-config'
|
||||
|
||||
export * from './util/misc.util'
|
||||
export * from './util/package-loading-progress'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable, OnDestroy } from "@angular/core";
|
||||
import { ReplaySubject } from "rxjs";
|
||||
import { Injectable, OnDestroy } from '@angular/core'
|
||||
import { ReplaySubject } from 'rxjs'
|
||||
|
||||
/**
|
||||
* Observable abstraction over ngOnDestroy to use with takeUntil
|
||||
@@ -7,7 +7,7 @@ import { ReplaySubject } from "rxjs";
|
||||
@Injectable()
|
||||
export class DestroyService extends ReplaySubject<void> implements OnDestroy {
|
||||
ngOnDestroy() {
|
||||
this.next();
|
||||
this.complete();
|
||||
this.next()
|
||||
this.complete()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import * as emver from '@start9labs/emver'
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
5
frontend/projects/shared/src/types/dependent-info.ts
Normal file
5
frontend/projects/shared/src/types/dependent-info.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface DependentInfo {
|
||||
id: string
|
||||
title: string
|
||||
version?: string
|
||||
}
|
||||
9
frontend/projects/shared/src/types/install-progress.ts
Normal file
9
frontend/projects/shared/src/types/install-progress.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface InstallProgress {
|
||||
size: number | null
|
||||
downloaded: number
|
||||
'download-complete': boolean
|
||||
validated: number
|
||||
'validation-complete': boolean
|
||||
unpacked: number
|
||||
'unpack-complete': boolean
|
||||
}
|
||||
7
frontend/projects/shared/src/types/package-state.ts
Normal file
7
frontend/projects/shared/src/types/package-state.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum PackageState {
|
||||
Installing = 'installing',
|
||||
Installed = 'installed',
|
||||
Updating = 'updating',
|
||||
Removing = 'removing',
|
||||
Restoring = 'restoring',
|
||||
}
|
||||
7
frontend/projects/shared/src/types/progress-data.ts
Normal file
7
frontend/projects/shared/src/types/progress-data.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface ProgressData {
|
||||
totalProgress: number
|
||||
downloadProgress: number
|
||||
validateProgress: number
|
||||
unpackProgress: number
|
||||
isComplete: boolean
|
||||
}
|
||||
165
frontend/projects/shared/src/util/misc.util.ts
Normal file
165
frontend/projects/shared/src/util/misc.util.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { OperatorFunction } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
export function trace<T>(t: T): T {
|
||||
console.log(`TRACE`, t)
|
||||
return t
|
||||
}
|
||||
|
||||
// curried description. This allows e.g somePromise.thentraceDesc('my result'))
|
||||
export function traceDesc<T>(description: string): (t: T) => T {
|
||||
return t => {
|
||||
console.log(`TRACE`, description, t)
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
// for use in observables. This allows e.g. someObservable.pipe(traceM('my result'))
|
||||
// the practical equivalent of `tap(t => console.log(t, description))`
|
||||
export function traceWheel<T>(description?: string): OperatorFunction<T, T> {
|
||||
return description ? map(traceDesc(description)) : map(trace)
|
||||
}
|
||||
|
||||
export function traceThrowDesc<T>(description: string, t: T | undefined): T {
|
||||
if (!t) throw new Error(description)
|
||||
return t
|
||||
}
|
||||
|
||||
export function inMs(
|
||||
count: number,
|
||||
unit: 'days' | 'hours' | 'minutes' | 'seconds',
|
||||
) {
|
||||
switch (unit) {
|
||||
case 'seconds':
|
||||
return count * 1000
|
||||
case 'minutes':
|
||||
return inMs(count * 60, 'seconds')
|
||||
case 'hours':
|
||||
return inMs(count * 60, 'minutes')
|
||||
case 'days':
|
||||
return inMs(count * 24, 'hours')
|
||||
}
|
||||
}
|
||||
|
||||
// arr1 - arr2
|
||||
export function diff<T>(arr1: T[], arr2: T[]): T[] {
|
||||
return arr1.filter(x => !arr2.includes(x))
|
||||
}
|
||||
|
||||
// arr1 & arr2
|
||||
export function both<T>(arr1: T[], arr2: T[]): T[] {
|
||||
return arr1.filter(x => arr2.includes(x))
|
||||
}
|
||||
|
||||
export function isObject(val: any): boolean {
|
||||
return val && typeof val === 'object' && !Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj: object): boolean {
|
||||
if (obj === undefined) return true
|
||||
return !Object.keys(obj).length
|
||||
}
|
||||
|
||||
export function pauseFor(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export function toObject<T>(t: T[], map: (t0: T) => string): Record<string, T> {
|
||||
return t.reduce((acc, next) => {
|
||||
acc[map(next)] = next
|
||||
return acc
|
||||
}, {} as Record<string, T>)
|
||||
}
|
||||
|
||||
export function update<T>(
|
||||
t: Record<string, T>,
|
||||
u: Record<string, T>,
|
||||
): Record<string, T> {
|
||||
return { ...t, ...u }
|
||||
}
|
||||
|
||||
export function deepCloneUnknown<T>(value: T): T {
|
||||
if (typeof value !== 'object' || value === null) {
|
||||
return value
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return deepCloneArray(value)
|
||||
}
|
||||
return deepCloneObject(value)
|
||||
}
|
||||
|
||||
export function deepCloneObject<T>(source: T) {
|
||||
const result = {}
|
||||
Object.keys(source).forEach(key => {
|
||||
const value = source[key]
|
||||
result[key] = deepCloneUnknown(value)
|
||||
}, {})
|
||||
return result as T
|
||||
}
|
||||
|
||||
export function deepCloneArray(collection: any) {
|
||||
return collection.map(value => {
|
||||
return deepCloneUnknown(value)
|
||||
})
|
||||
}
|
||||
|
||||
export function partitionArray<T>(
|
||||
ts: T[],
|
||||
condition: (t: T) => boolean,
|
||||
): [T[], T[]] {
|
||||
const yes = [] as T[]
|
||||
const no = [] as T[]
|
||||
ts.forEach(t => {
|
||||
if (condition(t)) {
|
||||
yes.push(t)
|
||||
} else {
|
||||
no.push(t)
|
||||
}
|
||||
})
|
||||
return [yes, no]
|
||||
}
|
||||
|
||||
export function uniqueBy<T>(
|
||||
ts: T[],
|
||||
uniqueBy: (t: T) => string,
|
||||
prioritize: (t1: T, t2: T) => T,
|
||||
) {
|
||||
return Object.values(
|
||||
ts.reduce((acc, next) => {
|
||||
const previousValue = acc[uniqueBy(next)]
|
||||
if (previousValue) {
|
||||
acc[uniqueBy(next)] = prioritize(acc[uniqueBy(next)], previousValue)
|
||||
} else {
|
||||
acc[uniqueBy(next)] = previousValue
|
||||
}
|
||||
return acc
|
||||
}, {}),
|
||||
)
|
||||
}
|
||||
|
||||
export function capitalizeFirstLetter(string: string): string {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
|
||||
export const exists = (t: any) => {
|
||||
return t !== undefined
|
||||
}
|
||||
|
||||
export function debounce(delay: number = 300): MethodDecorator {
|
||||
return function (
|
||||
target: any,
|
||||
propertyKey: string,
|
||||
descriptor: PropertyDescriptor,
|
||||
) {
|
||||
const timeoutKey = Symbol()
|
||||
|
||||
const original = descriptor.value
|
||||
|
||||
descriptor.value = function (...args) {
|
||||
clearTimeout(this[timeoutKey])
|
||||
this[timeoutKey] = setTimeout(() => original.apply(this, args), delay)
|
||||
}
|
||||
|
||||
return descriptor
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { InstallProgress } from 'src/app/services/patch-db/data-model'
|
||||
import { isEmptyObject } from './misc.util'
|
||||
import { InstallProgress } from '../types/install-progress'
|
||||
import { ProgressData } from '../types/progress-data'
|
||||
|
||||
export function packageLoadingProgress(
|
||||
loadData: InstallProgress,
|
||||
@@ -46,11 +47,3 @@ export function packageLoadingProgress(
|
||||
isComplete: downloadComplete && validationComplete && unpackComplete,
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProgressData {
|
||||
totalProgress: number
|
||||
downloadProgress: number
|
||||
validateProgress: number
|
||||
unpackProgress: number
|
||||
isComplete: boolean
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
@@ -16,17 +16,16 @@ import {
|
||||
ModalController,
|
||||
ToastController,
|
||||
} from '@ionic/angular'
|
||||
import { Emver } from './services/emver.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { ToastButton } from '@ionic/core'
|
||||
import { PatchDbService } from './services/patch-db/patch-db.service'
|
||||
import { ServerStatus, UIData } from './services/patch-db/data-model'
|
||||
import {
|
||||
ConnectionFailure,
|
||||
ConnectionService,
|
||||
} from './services/connection.service'
|
||||
import { ConfigService } from './services/config.service'
|
||||
import { debounce, isEmptyObject } from './util/misc.util'
|
||||
import { debounce, isEmptyObject, Emver } from '@start9labs/shared'
|
||||
import { ServerStatus, UIData } from 'src/app/services/patch-db/data-model'
|
||||
import { ErrorToastService } from './services/error-toast.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { LocalStorageService } from './services/local-storage.service'
|
||||
|
||||
@@ -15,15 +15,14 @@ import { OSWelcomePageModule } from './modals/os-welcome/os-welcome.module'
|
||||
import { MarkdownPageModule } from './modals/markdown/markdown.module'
|
||||
import { PatchDbService } from './services/patch-db/patch-db.service'
|
||||
import { LocalStorageBootstrap } from './services/patch-db/local-storage-bootstrap'
|
||||
import { SharingModule } from './modules/sharing.module'
|
||||
import { FormBuilder } from '@angular/forms'
|
||||
import { GenericInputComponentModule } from './modals/generic-input/generic-input.component.module'
|
||||
import { AuthService } from './services/auth.service'
|
||||
import { GlobalErrorHandler } from './services/global-error-handler.service'
|
||||
import { MockApiService } from './services/api/embassy-mock-api.service'
|
||||
import { LiveApiService } from './services/api/embassy-live-api.service'
|
||||
import { WorkspaceConfig } from '@shared'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
import { SharedPipesModule, WorkspaceConfig } from '@start9labs/shared'
|
||||
|
||||
const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
|
||||
@@ -47,8 +46,8 @@ const { useMocks } = require('../../../../config.json') as WorkspaceConfig
|
||||
OSWelcomePageModule,
|
||||
MarkdownPageModule,
|
||||
GenericInputComponentModule,
|
||||
SharingModule,
|
||||
MonacoEditorModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
providers: [
|
||||
FormBuilder,
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { BackupDrivesComponent, BackupDrivesHeaderComponent, BackupDrivesStatusComponent } from './backup-drives.component'
|
||||
import { SharingModule } from '../../modules/sharing.module'
|
||||
import {
|
||||
BackupDrivesComponent,
|
||||
BackupDrivesHeaderComponent,
|
||||
BackupDrivesStatusComponent,
|
||||
} from './backup-drives.component'
|
||||
import {
|
||||
UnitConversionPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.module'
|
||||
|
||||
@NgModule({
|
||||
@@ -14,7 +21,8 @@ import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
UnitConversionPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
GenericFormPageModule,
|
||||
],
|
||||
exports: [
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
import { BackupService } from './backup.service'
|
||||
import { CifsBackupTarget, DiskBackupTarget, RR } from 'src/app/services/api/api.types'
|
||||
import { ActionSheetController, AlertController, LoadingController, ModalController } from '@ionic/angular'
|
||||
import {
|
||||
CifsBackupTarget,
|
||||
DiskBackupTarget,
|
||||
RR,
|
||||
} from 'src/app/services/api/api.types'
|
||||
import {
|
||||
ActionSheetController,
|
||||
AlertController,
|
||||
LoadingController,
|
||||
ModalController,
|
||||
} from '@ionic/angular'
|
||||
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { MappedBackupTarget } from 'src/app/util/misc.util'
|
||||
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
|
||||
|
||||
@Component({
|
||||
selector: 'backup-drives',
|
||||
@@ -15,7 +24,9 @@ import { MappedBackupTarget } from 'src/app/util/misc.util'
|
||||
})
|
||||
export class BackupDrivesComponent {
|
||||
@Input() type: 'create' | 'restore'
|
||||
@Output() onSelect: EventEmitter<MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>> = new EventEmitter()
|
||||
@Output() onSelect: EventEmitter<
|
||||
MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>
|
||||
> = new EventEmitter()
|
||||
loadingText: string
|
||||
|
||||
constructor(
|
||||
@@ -29,19 +40,27 @@ export class BackupDrivesComponent {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadingText = this.type === 'create' ? 'Fetching Backup Targets' : 'Fetching Backup Sources'
|
||||
this.loadingText =
|
||||
this.type === 'create'
|
||||
? 'Fetching Backup Targets'
|
||||
: 'Fetching Backup Sources'
|
||||
this.backupService.getBackupTargets()
|
||||
}
|
||||
|
||||
select (target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>): void {
|
||||
select(
|
||||
target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>,
|
||||
): void {
|
||||
if (target.entry.type === 'cifs' && !target.entry.mountable) {
|
||||
const message = 'Unable to connect to shared folder. Ensure (1) target computer is connected to LAN, (2) target folder is being shared, and (3) hostname, path, and credentials are accurate.'
|
||||
const message =
|
||||
'Unable to connect to shared folder. Ensure (1) target computer is connected to LAN, (2) target folder is being shared, and (3) hostname, path, and credentials are accurate.'
|
||||
this.presentAlertError(message)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.type === 'restore' && !target.hasValidBackup) {
|
||||
const message = `${target.entry.type === 'cifs' ? 'Shared folder' : 'Drive partition'} does not contain a valid Embassy backup.`
|
||||
const message = `${
|
||||
target.entry.type === 'cifs' ? 'Shared folder' : 'Drive partition'
|
||||
} does not contain a valid Embassy backup.`
|
||||
this.presentAlertError(message)
|
||||
return
|
||||
}
|
||||
@@ -69,7 +88,10 @@ export class BackupDrivesComponent {
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async presentActionCifs (target: MappedBackupTarget<CifsBackupTarget>, index: number): Promise<void> {
|
||||
async presentActionCifs(
|
||||
target: MappedBackupTarget<CifsBackupTarget>,
|
||||
index: number,
|
||||
): Promise<void> {
|
||||
const entry = target.entry as CifsBackupTarget
|
||||
|
||||
const action = await this.actionCtrl.create({
|
||||
@@ -93,8 +115,12 @@ export class BackupDrivesComponent {
|
||||
},
|
||||
},
|
||||
{
|
||||
text: this.type === 'create' ? 'Create Backup' : 'Restore From Backup',
|
||||
icon: this.type === 'create' ? 'cloud-upload-outline' : 'cloud-download-outline',
|
||||
text:
|
||||
this.type === 'create' ? 'Create Backup' : 'Restore From Backup',
|
||||
icon:
|
||||
this.type === 'create'
|
||||
? 'cloud-upload-outline'
|
||||
: 'cloud-download-outline',
|
||||
handler: () => {
|
||||
this.select(target)
|
||||
},
|
||||
@@ -139,7 +165,11 @@ export class BackupDrivesComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private async presentModalEditCifs (id: string, entry: CifsBackupTarget, index: number): Promise<void> {
|
||||
private async presentModalEditCifs(
|
||||
id: string,
|
||||
entry: CifsBackupTarget,
|
||||
index: number,
|
||||
): Promise<void> {
|
||||
const { hostname, path, username } = entry
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
@@ -166,7 +196,10 @@ export class BackupDrivesComponent {
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
private async editCifs (value: RR.UpdateBackupTargetReq, index: number): Promise<void> {
|
||||
private async editCifs(
|
||||
value: RR.UpdateBackupTargetReq,
|
||||
index: number,
|
||||
): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
message: 'Testing connectivity to shared folder...',
|
||||
@@ -204,7 +237,6 @@ export class BackupDrivesComponent {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'backup-drives-header',
|
||||
templateUrl: './backup-drives-header.component.html',
|
||||
@@ -214,16 +246,13 @@ export class BackupDrivesHeaderComponent {
|
||||
@Input() title: string
|
||||
@Output() onClose: EventEmitter<void> = new EventEmitter()
|
||||
|
||||
constructor (
|
||||
public readonly backupService: BackupService,
|
||||
) { }
|
||||
constructor(public readonly backupService: BackupService) {}
|
||||
|
||||
refresh() {
|
||||
this.backupService.getBackupTargets()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'backup-drives-status',
|
||||
templateUrl: './backup-drives-status.component.html',
|
||||
@@ -238,7 +267,8 @@ const CifsSpec: ConfigSpec = {
|
||||
hostname: {
|
||||
type: 'string',
|
||||
name: 'Hostname',
|
||||
description: 'The hostname of your target device on the Local Area Network.',
|
||||
description:
|
||||
'The hostname of your target device on the Local Area Network.',
|
||||
placeholder: `e.g. 'My Computer' OR 'my-computer.local'`,
|
||||
pattern: '^[a-zA-Z0-9._-]+( [a-zA-Z0-9]+)*$',
|
||||
'pattern-description': `Must be a valid hostname. e.g. 'My Computer' OR 'my-computer.local'`,
|
||||
@@ -249,7 +279,8 @@ const CifsSpec: ConfigSpec = {
|
||||
path: {
|
||||
type: 'string',
|
||||
name: 'Path',
|
||||
description: 'The directory path to the shared folder on your target device.',
|
||||
description:
|
||||
'The directory path to the shared folder on your target device.',
|
||||
placeholder: 'e.g. /Desktop/my-folder',
|
||||
nullable: false,
|
||||
masked: false,
|
||||
|
||||
@@ -2,9 +2,13 @@ import { Injectable } from '@angular/core'
|
||||
import { IonicSafeString } from '@ionic/core'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
import { BackupTarget, CifsBackupTarget, DiskBackupTarget } from 'src/app/services/api/api.types'
|
||||
import { Emver } from 'src/app/services/emver.service'
|
||||
import { MappedBackupTarget } from 'src/app/util/misc.util'
|
||||
import {
|
||||
BackupTarget,
|
||||
CifsBackupTarget,
|
||||
DiskBackupTarget,
|
||||
} from 'src/app/services/api/api.types'
|
||||
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -53,6 +57,8 @@ export class BackupService {
|
||||
}
|
||||
|
||||
hasValidBackup(target: BackupTarget): boolean {
|
||||
return [0, 1].includes(this.emver.compare(target['embassy-os']?.version, '0.3.0'))
|
||||
return [0, 1].includes(
|
||||
this.emver.compare(target['embassy-os']?.version, '0.3.0'),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<div style="position: relative; margin-right: 1vh;">
|
||||
<ion-badge mode="md" class="md-badge" *ngIf="unreadCount && !sidebarOpen" color="danger">{{ unreadCount }}</ion-badge>
|
||||
<div class="wrapper">
|
||||
<ion-badge
|
||||
*ngIf="unreadCount && !sidebarOpen"
|
||||
mode="md"
|
||||
class="md-badge"
|
||||
color="danger"
|
||||
>
|
||||
{{ unreadCount }}
|
||||
</ion-badge>
|
||||
<ion-menu-button color="dark"></ion-menu-button>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { BadgeMenuComponent } from './badge-menu.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { BadgeMenuComponent } from './badge-menu.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BadgeMenuComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [BadgeMenuComponent],
|
||||
imports: [CommonModule, IonicModule],
|
||||
exports: [BadgeMenuComponent],
|
||||
})
|
||||
export class BadgeMenuComponentModule {}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
margin-right: 1vh;
|
||||
}
|
||||
|
||||
.md-badge {
|
||||
background-color: var(--ion-color-danger);
|
||||
position: absolute;
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormObjectComponent, FormLabelComponent, FormErrorComponent } from './form-object.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { EnumListPageModule } from 'src/app/modals/enum-list/enum-list.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
import {
|
||||
FormObjectComponent,
|
||||
FormLabelComponent,
|
||||
FormErrorComponent,
|
||||
],
|
||||
} from './form-object.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { EnumListPageModule } from 'src/app/modals/enum-list/enum-list.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [FormObjectComponent, FormLabelComponent, FormErrorComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
EnumListPageModule,
|
||||
],
|
||||
exports: [
|
||||
FormObjectComponent,
|
||||
FormLabelComponent,
|
||||
FormErrorComponent,
|
||||
],
|
||||
exports: [FormObjectComponent, FormLabelComponent, FormErrorComponent],
|
||||
})
|
||||
export class FormObjectComponentModule {}
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
||||
import { AbstractFormGroupDirective, FormArray, FormGroup } from '@angular/forms'
|
||||
import { AlertButton, AlertController, IonicSafeString, ModalController } from '@ionic/angular'
|
||||
import { ConfigSpec, ListValueSpecOf, ValueSpec, ValueSpecBoolean, ValueSpecList, ValueSpecListOf, ValueSpecUnion } from 'src/app/pkg-config/config-types'
|
||||
import {
|
||||
AbstractFormGroupDirective,
|
||||
FormArray,
|
||||
FormGroup,
|
||||
} from '@angular/forms'
|
||||
import {
|
||||
AlertButton,
|
||||
AlertController,
|
||||
IonicSafeString,
|
||||
ModalController,
|
||||
} from '@ionic/angular'
|
||||
import {
|
||||
ConfigSpec,
|
||||
ListValueSpecOf,
|
||||
ValueSpec,
|
||||
ValueSpecBoolean,
|
||||
ValueSpecList,
|
||||
ValueSpecListOf,
|
||||
ValueSpecUnion,
|
||||
} from 'src/app/pkg-config/config-types'
|
||||
import { FormService } from 'src/app/services/form.service'
|
||||
import { Range } from 'src/app/pkg-config/config-utilities'
|
||||
import { EnumListPage } from 'src/app/modals/enum-list/enum-list.page'
|
||||
import { pauseFor } from 'src/app/util/misc.util'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
import { v4 } from 'uuid'
|
||||
const Mustache = require('mustache')
|
||||
|
||||
@@ -24,8 +41,10 @@ export class FormObjectComponent {
|
||||
@Output() onExpand = new EventEmitter<void>()
|
||||
warningAck: { [key: string]: boolean } = {}
|
||||
unmasked: { [key: string]: boolean } = {}
|
||||
objectDisplay: { [key: string]: { expanded: boolean, height: string } } = { }
|
||||
objectListDisplay: { [key: string]: { expanded: boolean, height: string, displayAs: string }[] } = { }
|
||||
objectDisplay: { [key: string]: { expanded: boolean; height: string } } = {}
|
||||
objectListDisplay: {
|
||||
[key: string]: { expanded: boolean; height: string; displayAs: string }[]
|
||||
} = {}
|
||||
private objectId = v4()
|
||||
|
||||
Object = Object
|
||||
@@ -39,14 +58,19 @@ export class FormObjectComponent {
|
||||
ngOnInit() {
|
||||
Object.keys(this.objectSpec).forEach(key => {
|
||||
const spec = this.objectSpec[key]
|
||||
|
||||
if (spec.type === 'list' && ['object', 'union'].includes(spec.subtype)) {
|
||||
this.objectListDisplay[key] = [];
|
||||
(this.formGroup.get(key).value as any[]).forEach((obj, index) => {
|
||||
const displayAs = (spec.spec as ListValueSpecOf<'object'>)['display-as']
|
||||
this.objectListDisplay[key] = []
|
||||
this.formGroup.get(key).value.forEach((obj, index) => {
|
||||
const displayAs = (spec.spec as ListValueSpecOf<'object'>)[
|
||||
'display-as'
|
||||
]
|
||||
this.objectListDisplay[key][index] = {
|
||||
expanded: false,
|
||||
height: '0px',
|
||||
displayAs: displayAs ? (Mustache as any).render(displayAs, obj) : '',
|
||||
displayAs: displayAs
|
||||
? (Mustache as any).render(displayAs, obj)
|
||||
: '',
|
||||
}
|
||||
})
|
||||
} else if (['object', 'union'].includes(spec.type)) {
|
||||
@@ -70,21 +94,26 @@ export class FormObjectComponent {
|
||||
this.formGroup.removeControl(control)
|
||||
})
|
||||
|
||||
const unionGroup = this.formService.getUnionObject(this.unionSpec as ValueSpecUnion, e.detail.value)
|
||||
const unionGroup = this.formService.getUnionObject(
|
||||
this.unionSpec as ValueSpecUnion,
|
||||
e.detail.value,
|
||||
)
|
||||
|
||||
Object.keys(unionGroup.controls).forEach(control => {
|
||||
if (control === primary) return
|
||||
this.formGroup.addControl(control, unionGroup.controls[control])
|
||||
})
|
||||
|
||||
Object.entries(this.unionSpec.variants[e.detail.value]).forEach(([key, value]) => {
|
||||
Object.entries(this.unionSpec.variants[e.detail.value]).forEach(
|
||||
([key, value]) => {
|
||||
if (['object', 'union'].includes(value.type)) {
|
||||
this.objectDisplay[key] = {
|
||||
expanded: false,
|
||||
height: '0px',
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
this.onExpand.emit()
|
||||
}
|
||||
@@ -112,7 +141,9 @@ export class FormObjectComponent {
|
||||
newItem.markAllAsTouched()
|
||||
arr.insert(0, newItem)
|
||||
if (['object', 'union'].includes(listSpec.subtype)) {
|
||||
const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)['display-as']
|
||||
const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)[
|
||||
'display-as'
|
||||
]
|
||||
this.objectListDisplay[key].unshift({
|
||||
height: '0px',
|
||||
expanded: true,
|
||||
@@ -127,21 +158,30 @@ export class FormObjectComponent {
|
||||
|
||||
toggleExpandObject(key: string) {
|
||||
this.objectDisplay[key].expanded = !this.objectDisplay[key].expanded
|
||||
this.objectDisplay[key].height = this.objectDisplay[key].expanded ? this.getDocSize(key) : '0px'
|
||||
this.objectDisplay[key].height = this.objectDisplay[key].expanded
|
||||
? this.getDocSize(key)
|
||||
: '0px'
|
||||
this.onExpand.emit()
|
||||
}
|
||||
|
||||
toggleExpandListObject(key: string, i: number) {
|
||||
this.objectListDisplay[key][i].expanded = !this.objectListDisplay[key][i].expanded
|
||||
this.objectListDisplay[key][i].height = this.objectListDisplay[key][i].expanded ? this.getDocSize(key, i) : '0px'
|
||||
this.objectListDisplay[key][i].expanded =
|
||||
!this.objectListDisplay[key][i].expanded
|
||||
this.objectListDisplay[key][i].height = this.objectListDisplay[key][i]
|
||||
.expanded
|
||||
? this.getDocSize(key, i)
|
||||
: '0px'
|
||||
}
|
||||
|
||||
updateLabel(key: string, i: number, displayAs: string) {
|
||||
this.objectListDisplay[key][i].displayAs = displayAs ? Mustache.render(displayAs, this.formGroup.get(key).value[i]) : ''
|
||||
this.objectListDisplay[key][i].displayAs = displayAs
|
||||
? Mustache.render(displayAs, this.formGroup.get(key).value[i])
|
||||
: ''
|
||||
}
|
||||
|
||||
getWarningText(text: string): IonicSafeString {
|
||||
if (text) return new IonicSafeString(`<ion-text color="warning">${text}</ion-text>`)
|
||||
if (text)
|
||||
return new IonicSafeString(`<ion-text color="warning">${text}</ion-text>`)
|
||||
}
|
||||
|
||||
handleInputChange() {
|
||||
@@ -156,7 +196,11 @@ export class FormObjectComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async presentModalEnumList (key: string, spec: ValueSpecListOf<'enum'>, current: string[]) {
|
||||
async presentModalEnumList(
|
||||
key: string,
|
||||
spec: ValueSpecListOf<'enum'>,
|
||||
current: string[],
|
||||
) {
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: {
|
||||
key,
|
||||
@@ -175,7 +219,12 @@ export class FormObjectComponent {
|
||||
await modal.present()
|
||||
}
|
||||
|
||||
async presentAlertChangeWarning (key: string, spec: ValueSpec, okFn?: Function, cancelFn?: Function) {
|
||||
async presentAlertChangeWarning(
|
||||
key: string,
|
||||
spec: ValueSpec,
|
||||
okFn?: Function,
|
||||
cancelFn?: Function,
|
||||
) {
|
||||
if (!spec.warning || this.warningAck[key]) return okFn ? okFn() : null
|
||||
this.warningAck[key] = true
|
||||
|
||||
@@ -229,11 +278,13 @@ export class FormObjectComponent {
|
||||
}
|
||||
|
||||
private deleteListItem(key: string, index: number, markDirty = true): void {
|
||||
if (this.objectListDisplay[key]) this.objectListDisplay[key][index].height = '0px'
|
||||
if (this.objectListDisplay[key])
|
||||
this.objectListDisplay[key][index].height = '0px'
|
||||
const arr = this.formGroup.get(key) as FormArray
|
||||
if (markDirty) arr.markAsDirty()
|
||||
pauseFor(250).then(() => {
|
||||
if (this.objectListDisplay[key]) this.objectListDisplay[key].splice(index, 1)
|
||||
if (this.objectListDisplay[key])
|
||||
this.objectListDisplay[key].splice(index, 1)
|
||||
arr.removeAt(index)
|
||||
})
|
||||
}
|
||||
@@ -264,7 +315,6 @@ export class FormObjectComponent {
|
||||
}
|
||||
|
||||
async presentUnionTagDescription(name: string, description: string) {
|
||||
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: name,
|
||||
message: description,
|
||||
@@ -293,9 +343,7 @@ export class FormLabelComponent {
|
||||
Range = Range
|
||||
@Input() data: HeaderData
|
||||
|
||||
constructor (
|
||||
private readonly alertCtrl: AlertController,
|
||||
) { }
|
||||
constructor(private readonly alertCtrl: AlertController) {}
|
||||
|
||||
async presentAlertDescription() {
|
||||
const { name, description } = this.data.spec
|
||||
@@ -308,7 +356,6 @@ export class FormLabelComponent {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'form-error',
|
||||
templateUrl: './form-error.component.html',
|
||||
|
||||
@@ -3,17 +3,15 @@ import { CommonModule } from '@angular/common'
|
||||
import { AlertComponent } from './alert.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { MarkdownPipeModule } from '@start9labs/shared'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AlertComponent,
|
||||
],
|
||||
declarations: [AlertComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
MarkdownPipeModule,
|
||||
],
|
||||
exports: [AlertComponent],
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { BehaviorSubject, from, Subject } from 'rxjs'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
import { capitalizeFirstLetter } from 'src/app/util/misc.util'
|
||||
import { capitalizeFirstLetter } from '@start9labs/shared'
|
||||
import { markAsLoadingDuring$ } from '../loadable'
|
||||
import { WizardAction } from '../wizard-types'
|
||||
|
||||
@@ -31,15 +31,20 @@ export class CompleteComponent {
|
||||
message: string
|
||||
|
||||
load() {
|
||||
markAsLoadingDuring$(this.loading$, from(this.params.executeAction())).pipe(takeUntil(this.cancel$)).subscribe(
|
||||
{
|
||||
error: e => this.transitions.error(new Error(`${this.params.action} failed: ${e.message || e}`)),
|
||||
markAsLoadingDuring$(this.loading$, from(this.params.executeAction()))
|
||||
.pipe(takeUntil(this.cancel$))
|
||||
.subscribe({
|
||||
error: e =>
|
||||
this.transitions.error(
|
||||
new Error(`${this.params.action} failed: ${e.message || e}`),
|
||||
),
|
||||
complete: () => this.transitions.final(),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.message = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title}...`
|
||||
this.message = `${capitalizeFirstLetter(this.params.verb)} ${
|
||||
this.params.title
|
||||
}...`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,15 @@ import { CommonModule } from '@angular/common'
|
||||
import { DependentsComponent } from './dependents.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DependentsComponent,
|
||||
],
|
||||
declarations: [DependentsComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
exports: [DependentsComponent],
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BehaviorSubject, from, Subject } from 'rxjs'
|
||||
import { takeUntil, tap } from 'rxjs/operators'
|
||||
import { Breakages } from 'src/app/services/api/api.types'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { capitalizeFirstLetter, isEmptyObject } from 'src/app/util/misc.util'
|
||||
import { capitalizeFirstLetter, isEmptyObject } from '@start9labs/shared'
|
||||
import { markAsLoadingDuring$ } from '../loadable'
|
||||
import { WizardAction } from '../wizard-types'
|
||||
|
||||
@@ -14,9 +14,9 @@ import { WizardAction } from '../wizard-types'
|
||||
})
|
||||
export class DependentsComponent {
|
||||
@Input() params: {
|
||||
title: string,
|
||||
action: WizardAction, //Are you sure you want to *uninstall*...,
|
||||
verb: string, // *Uninstalling* will cause problems...
|
||||
title: string
|
||||
action: WizardAction //Are you sure you want to *uninstall*...,
|
||||
verb: string // *Uninstalling* will cause problems...
|
||||
fetchBreakages: () => Promise<Breakages>
|
||||
}
|
||||
@Input() transitions: {
|
||||
@@ -32,27 +32,37 @@ export class DependentsComponent {
|
||||
loading$ = new BehaviorSubject(false)
|
||||
cancel$ = new Subject<void>()
|
||||
|
||||
constructor (
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
constructor(public readonly patch: PatchDbService) {}
|
||||
|
||||
load() {
|
||||
markAsLoadingDuring$(this.loading$, from(this.params.fetchBreakages()))
|
||||
.pipe(
|
||||
takeUntil(this.cancel$),
|
||||
tap(breakages => this.dependentBreakages = breakages),
|
||||
tap(breakages => (this.dependentBreakages = breakages)),
|
||||
)
|
||||
.subscribe(
|
||||
{
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
if (this.dependentBreakages && !isEmptyObject(this.dependentBreakages)) {
|
||||
this.dependentViolation = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title} will prohibit the following services from functioning properly and may cause them to stop if they are currently running.`
|
||||
if (
|
||||
this.dependentBreakages &&
|
||||
!isEmptyObject(this.dependentBreakages)
|
||||
) {
|
||||
this.dependentViolation = `${capitalizeFirstLetter(
|
||||
this.params.verb,
|
||||
)} ${
|
||||
this.params.title
|
||||
} will prohibit the following services from functioning properly and may cause them to stop if they are currently running.`
|
||||
} else {
|
||||
this.transitions.next()
|
||||
}
|
||||
},
|
||||
error: (e: Error) => this.transitions.error(new Error(`Fetching dependent service information failed: ${e.message || e}`)),
|
||||
},
|
||||
)
|
||||
error: (e: Error) =>
|
||||
this.transitions.error(
|
||||
new Error(
|
||||
`Fetching dependent service information failed: ${
|
||||
e.message || e
|
||||
}`,
|
||||
),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,19 @@ import { CommonModule } from '@angular/common'
|
||||
import { InstallWizardComponent } from './install-wizard.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
import { DependentsComponentModule } from './dependents/dependents.component.module'
|
||||
import { CompleteComponentModule } from './complete/complete.component.module'
|
||||
import { NotesComponentModule } from './notes/notes.component.module'
|
||||
import { AlertComponentModule } from './alert/alert.component.module'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
InstallWizardComponent,
|
||||
],
|
||||
declarations: [InstallWizardComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
EmverPipesModule,
|
||||
DependentsComponentModule,
|
||||
CompleteComponentModule,
|
||||
NotesComponentModule,
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Component, Input, NgZone, QueryList, ViewChild, ViewChildren } from '@angular/core'
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
NgZone,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
} from '@angular/core'
|
||||
import { IonContent, IonSlides, ModalController } from '@ionic/angular'
|
||||
import { capitalizeFirstLetter, pauseFor } from 'src/app/util/misc.util'
|
||||
import { capitalizeFirstLetter, pauseFor } from '@start9labs/shared'
|
||||
import { CompleteComponent } from './complete/complete.component'
|
||||
import { DependentsComponent } from './dependents/dependents.component'
|
||||
import { AlertComponent } from './alert/alert.component'
|
||||
@@ -30,7 +37,9 @@ export class InstallWizardComponent {
|
||||
//a slide component gives us hook into a slide. Allows us to call load when slide comes into view
|
||||
@ViewChildren('components')
|
||||
slideComponentsQL: QueryList<Loadable>
|
||||
get slideComponents (): Loadable[] { return this.slideComponentsQL.toArray() }
|
||||
get slideComponents(): Loadable[] {
|
||||
return this.slideComponentsQL.toArray()
|
||||
}
|
||||
|
||||
private slideIndex = 0
|
||||
get currentSlide(): Loadable {
|
||||
@@ -59,25 +68,39 @@ export class InstallWizardComponent {
|
||||
}
|
||||
|
||||
// process bottom bar buttons
|
||||
private transition = (info: { next: any } | { error: Error } | { cancelled: true } | { final: true }) => {
|
||||
const i = info as { next?: any, error?: Error, cancelled?: true, final?: true }
|
||||
private transition = (
|
||||
info:
|
||||
| { next: any }
|
||||
| { error: Error }
|
||||
| { cancelled: true }
|
||||
| { final: true },
|
||||
) => {
|
||||
const i = info as {
|
||||
next?: any
|
||||
error?: Error
|
||||
cancelled?: true
|
||||
final?: true
|
||||
}
|
||||
if (i.cancelled) this.currentSlide.cancel$.next()
|
||||
if (i.final || i.cancelled) return this.modalController.dismiss(i)
|
||||
if (i.error) return this.error = capitalizeFirstLetter(i.error.message)
|
||||
if (i.error) return (this.error = capitalizeFirstLetter(i.error.message))
|
||||
|
||||
this.moveToNextSlide(i.next)
|
||||
}
|
||||
|
||||
// bottom bar button callbacks. Pass this into components if they need to trigger slide transitions independent of the bottom bar clicks
|
||||
transitions = {
|
||||
next: (prevResult: any) => this.transition({ next: prevResult || this.currentSlide.result }),
|
||||
next: (prevResult: any) =>
|
||||
this.transition({ next: prevResult || this.currentSlide.result }),
|
||||
cancel: () => this.transition({ cancelled: true }),
|
||||
final: () => this.transition({ final: true }),
|
||||
error: (e: Error) => this.transition({ error: e }),
|
||||
}
|
||||
|
||||
private async moveToNextSlide(prevResult?: any) {
|
||||
if (this.slideComponents[this.slideIndex + 1] === undefined) { return this.transition({ final: true }) }
|
||||
if (this.slideComponents[this.slideIndex + 1] === undefined) {
|
||||
return this.transition({ final: true })
|
||||
}
|
||||
this.zone.run(async () => {
|
||||
this.slideComponents[this.slideIndex + 1].load(prevResult)
|
||||
await pauseFor(50) // give the load ^ opportunity to propogate into slide before sliding it into view
|
||||
@@ -98,14 +121,14 @@ export class InstallWizardComponent {
|
||||
|
||||
export interface SlideDefinition {
|
||||
slide:
|
||||
{ selector: 'dependents', params: DependentsComponent['params'] } |
|
||||
{ selector: 'complete', params: CompleteComponent['params'] } |
|
||||
{ selector: 'alert', params: AlertComponent['params'] } |
|
||||
{ selector: 'notes', params: NotesComponent['params'] }
|
||||
| { selector: 'dependents'; params: DependentsComponent['params'] }
|
||||
| { selector: 'complete'; params: CompleteComponent['params'] }
|
||||
| { selector: 'alert'; params: AlertComponent['params'] }
|
||||
| { selector: 'notes'; params: NotesComponent['params'] }
|
||||
bottomBar: {
|
||||
cancel: {
|
||||
// indicates the existence of a cancel button, and whether to have text or an icon 'x' by default.
|
||||
afterLoading?: { text?: string },
|
||||
afterLoading?: { text?: string }
|
||||
whileLoading?: { text?: string }
|
||||
}
|
||||
// indicates the existence of next or finish buttons (should only have one)
|
||||
@@ -114,11 +137,16 @@ export interface SlideDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
export type TopbarParams = { action: WizardAction, title: string, version: string }
|
||||
export type TopbarParams = {
|
||||
action: WizardAction
|
||||
title: string
|
||||
version: string
|
||||
}
|
||||
|
||||
export async function wizardModal(
|
||||
modalController: ModalController, params: InstallWizardComponent['params'],
|
||||
): Promise<{ cancelled?: true, final?: true, modal: HTMLIonModalElement }> {
|
||||
modalController: ModalController,
|
||||
params: InstallWizardComponent['params'],
|
||||
): Promise<{ cancelled?: true; final?: true; modal: HTMLIonModalElement }> {
|
||||
const modal = await modalController.create({
|
||||
backdropDismiss: false,
|
||||
cssClass: 'wizard-modal',
|
||||
|
||||
@@ -3,17 +3,15 @@ import { CommonModule } from '@angular/common'
|
||||
import { NotesComponent } from './notes.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { MarkdownPipeModule } from '@start9labs/shared'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
NotesComponent,
|
||||
],
|
||||
declarations: [NotesComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
MarkdownPipeModule,
|
||||
],
|
||||
exports: [NotesComponent],
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { PackageDataEntry } 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 { exists } from '@start9labs/shared'
|
||||
import { ApiService } from '../../services/api/embassy-api.service'
|
||||
import {
|
||||
InstallWizardComponent,
|
||||
|
||||
@@ -2,15 +2,11 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { LogsPage } from './logs.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { TextSpinnerComponentModule } from '@start9labs/shared'
|
||||
|
||||
@NgModule({
|
||||
declarations: [LogsPage],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
],
|
||||
imports: [CommonModule, IonicModule, TextSpinnerComponentModule],
|
||||
exports: [LogsPage],
|
||||
})
|
||||
export class LogsPageModule {}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<p
|
||||
[style.color]="disconnected ? 'gray' : 'var(--ion-color-' + rendering.color + ')'"
|
||||
[style.color]="
|
||||
disconnected ? 'gray' : 'var(--ion-color-' + rendering.color + ')'
|
||||
"
|
||||
[style.font-size]="size"
|
||||
[style.font-style]="style"
|
||||
[style.font-weight]="weight"
|
||||
@@ -7,7 +9,13 @@
|
||||
<span *ngIf="!installProgress">
|
||||
{{ disconnected ? 'Unknown' : rendering.display }}
|
||||
<span *ngIf="rendering.showDots" class="loading-dots"></span>
|
||||
<span *ngIf="rendering.display === PR[PS.Stopping].display && (sigtermTimeout | durationToSeconds) > 30">This may take a while.</span>
|
||||
<span
|
||||
*ngIf="
|
||||
rendering.display === PR[PS.Stopping].display &&
|
||||
(sigtermTimeout | durationToSeconds) > 30
|
||||
"
|
||||
>This may take a while.</span
|
||||
>
|
||||
</span>
|
||||
<span *ngIf="installProgress">
|
||||
<span *ngIf="installProgress < 99">
|
||||
@@ -19,5 +27,4 @@
|
||||
<span class="loading-dots"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</p>
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { StatusComponent } from './status.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
import { StatusComponent } from './status.component'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
StatusComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [StatusComponent],
|
||||
imports: [CommonModule, IonicModule, UnitConversionPipesModule],
|
||||
exports: [StatusComponent],
|
||||
})
|
||||
export class StatusComponentModule {}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { PrimaryRendering, PrimaryStatus, StatusRendering } from 'src/app/services/pkg-status-rendering.service'
|
||||
import {
|
||||
PrimaryRendering,
|
||||
PrimaryStatus,
|
||||
StatusRendering,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
selector: 'status',
|
||||
@@ -18,4 +22,3 @@ export class StatusComponent {
|
||||
@Input() installProgress?: number
|
||||
@Input() sigtermTimeout?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppConfigPage } from './app-config.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { TextSpinnerComponentModule } from '@start9labs/shared'
|
||||
import { FormObjectComponentModule } from 'src/app/components/form-object/form-object.component.module'
|
||||
|
||||
@NgModule({
|
||||
@@ -12,7 +12,7 @@ import { FormObjectComponentModule } from 'src/app/components/form-object/form-o
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
TextSpinnerComponentModule,
|
||||
FormObjectComponentModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
IonicSafeString,
|
||||
} from '@ionic/angular'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { DependentInfo, isEmptyObject, isObject } from 'src/app/util/misc.util'
|
||||
import { DependentInfo, isEmptyObject, isObject } from '@start9labs/shared'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { LoadingController, ModalController, IonicSafeString } from '@ionic/angular'
|
||||
import {
|
||||
LoadingController,
|
||||
ModalController,
|
||||
IonicSafeString,
|
||||
} from '@ionic/angular'
|
||||
import { BackupInfo, PackageBackupInfo } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { Emver } from 'src/app/services/emver.service'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
|
||||
@@ -42,7 +46,11 @@ export class AppRecoverSelectPage {
|
||||
id,
|
||||
checked: false,
|
||||
installed: !!this.patch.getData()['package-data'][id],
|
||||
'newer-eos': this.emver.compare(this.backupInfo['package-backups'][id]['os-version'], this.config.version) === 1,
|
||||
'newer-eos':
|
||||
this.emver.compare(
|
||||
this.backupInfo['package-backups'][id]['os-version'],
|
||||
this.config.version,
|
||||
) === 1,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ export class BackupReportPage {
|
||||
color: 'dark' | 'danger' | 'success'
|
||||
}
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
) { }
|
||||
constructor(private readonly modalCtrl: ModalController) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.report.server.attempted) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { ValueSpecListOf } from '../../pkg-config/config-types'
|
||||
import { ValueSpecListOf } from 'src/app/pkg-config/config-types'
|
||||
|
||||
@Component({
|
||||
selector: 'enum-list',
|
||||
@@ -14,9 +14,7 @@ export class EnumListPage {
|
||||
options: { [option: string]: boolean } = {}
|
||||
selectAll = true
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
) { }
|
||||
constructor(private readonly modalCtrl: ModalController) {}
|
||||
|
||||
ngOnInit() {
|
||||
for (let val of this.spec.spec.values) {
|
||||
@@ -29,11 +27,13 @@ export class EnumListPage {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.modalCtrl.dismiss(Object.keys(this.options).filter(key => this.options[key]))
|
||||
this.modalCtrl.dismiss(
|
||||
Object.keys(this.options).filter(key => this.options[key]),
|
||||
)
|
||||
}
|
||||
|
||||
toggleSelectAll() {
|
||||
Object.keys(this.options).forEach(k => this.options[k] = this.selectAll)
|
||||
Object.keys(this.options).forEach(k => (this.options[k] = this.selectAll))
|
||||
this.selectAll = !this.selectAll
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { FormGroup } from '@angular/forms'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { convertValuesRecursive, FormService } from 'src/app/services/form.service'
|
||||
import {
|
||||
convertValuesRecursive,
|
||||
FormService,
|
||||
} from 'src/app/services/form.service'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
|
||||
export interface ActionButton {
|
||||
@@ -45,7 +48,9 @@ export class GenericFormPage {
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
this.formGroup.markAllAsTouched()
|
||||
document.getElementsByClassName('validation-error')[0].parentElement.parentElement.scrollIntoView({ behavior: 'smooth' })
|
||||
document
|
||||
.getElementsByClassName('validation-error')[0]
|
||||
.parentElement.parentElement.scrollIntoView({ behavior: 'smooth' })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { GenericInputComponent } from './generic-input.component'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
|
||||
@NgModule({
|
||||
@@ -13,7 +13,7 @@ import { FormsModule } from '@angular/forms'
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
RouterModule.forChild([]),
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
exports: [GenericInputComponent],
|
||||
})
|
||||
|
||||
@@ -2,14 +2,18 @@ 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'
|
||||
import {
|
||||
MarkdownPipeModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
|
||||
@NgModule({
|
||||
declarations: [MarkdownPage],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharingModule,
|
||||
MarkdownPipeModule,
|
||||
TextSpinnerComponentModule,
|
||||
],
|
||||
exports: [MarkdownPage],
|
||||
})
|
||||
|
||||
@@ -2,17 +2,12 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { OSWelcomePage } from './os-welcome.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
|
||||
@NgModule({
|
||||
declarations: [OSWelcomePage],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
SharingModule,
|
||||
],
|
||||
imports: [CommonModule, IonicModule, FormsModule, SharedPipesModule],
|
||||
exports: [OSWelcomePage],
|
||||
})
|
||||
export class OSWelcomePageModule {}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import {
|
||||
EmverComparesPipe,
|
||||
EmverSatisfiesPipe,
|
||||
EmverDisplayPipe,
|
||||
} from '../pipes/emver.pipe'
|
||||
import { IncludesPipe } from '../pipes/includes.pipe'
|
||||
import { TypeofPipe } from '../pipes/typeof.pipe'
|
||||
import { MarkdownPipe } from '../pipes/markdown.pipe'
|
||||
import {
|
||||
TruncateCenterPipe,
|
||||
TruncateEndPipe,
|
||||
TruncateTailPipe,
|
||||
} from '../pipes/truncate.pipe'
|
||||
import { MaskPipe } from '../pipes/mask.pipe'
|
||||
import { HasUiPipe, LaunchablePipe, SanitizePipe } from '../pipes/ui.pipe'
|
||||
import { EmptyPipe } from '../pipes/empty.pipe'
|
||||
import { NotificationColorPipe } from '../pipes/notification-color.pipe'
|
||||
import { InstallState } from '../pipes/install-state.pipe'
|
||||
import { TextSpinnerComponentModule } from '../components/text-spinner/text-spinner.component.module'
|
||||
import { ConvertBytesPipe } from '../pipes/convert-bytes.pipe'
|
||||
import { DurationToSecondsPipe } from '../pipes/unit-conversion.pipe'
|
||||
import { InstallProgressPipe } from '../pipes/install-progress.pipe'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
EmverComparesPipe,
|
||||
EmverSatisfiesPipe,
|
||||
TypeofPipe,
|
||||
IncludesPipe,
|
||||
InstallProgressPipe,
|
||||
InstallState,
|
||||
MarkdownPipe,
|
||||
TruncateCenterPipe,
|
||||
TruncateEndPipe,
|
||||
TruncateTailPipe,
|
||||
MaskPipe,
|
||||
EmverDisplayPipe,
|
||||
HasUiPipe,
|
||||
LaunchablePipe,
|
||||
EmptyPipe,
|
||||
NotificationColorPipe,
|
||||
ConvertBytesPipe,
|
||||
DurationToSecondsPipe,
|
||||
SanitizePipe,
|
||||
],
|
||||
imports: [TextSpinnerComponentModule],
|
||||
exports: [
|
||||
EmverComparesPipe,
|
||||
EmverSatisfiesPipe,
|
||||
TypeofPipe,
|
||||
IncludesPipe,
|
||||
MarkdownPipe,
|
||||
TruncateEndPipe,
|
||||
TruncateCenterPipe,
|
||||
TruncateTailPipe,
|
||||
MaskPipe,
|
||||
EmverDisplayPipe,
|
||||
HasUiPipe,
|
||||
InstallProgressPipe,
|
||||
InstallState,
|
||||
LaunchablePipe,
|
||||
EmptyPipe,
|
||||
NotificationColorPipe,
|
||||
ConvertBytesPipe,
|
||||
DurationToSecondsPipe,
|
||||
// components
|
||||
TextSpinnerComponentModule,
|
||||
SanitizePipe,
|
||||
],
|
||||
})
|
||||
export class SharingModule {}
|
||||
@@ -4,7 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppActionsPage, AppActionsItemComponent } from './app-actions.page'
|
||||
import { QRComponentModule } from 'src/app/components/qr/qr.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.module'
|
||||
import { ActionSuccessPageModule } from 'src/app/modals/action-success/action-success.module'
|
||||
|
||||
@@ -21,13 +21,10 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
QRComponentModule,
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
GenericFormPageModule,
|
||||
ActionSuccessPageModule,
|
||||
],
|
||||
declarations: [
|
||||
AppActionsPage,
|
||||
AppActionsItemComponent,
|
||||
],
|
||||
declarations: [AppActionsPage, AppActionsItemComponent],
|
||||
})
|
||||
export class AppActionsPageModule {}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { isEmptyObject } from 'src/app/util/misc.util'
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page'
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -2,8 +2,11 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppInterfacesItemComponent, AppInterfacesPage } from './app-interfaces.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import {
|
||||
AppInterfacesItemComponent,
|
||||
AppInterfacesPage,
|
||||
} from './app-interfaces.page'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -17,11 +20,8 @@ const routes: Routes = [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
],
|
||||
declarations: [
|
||||
AppInterfacesPage,
|
||||
AppInterfacesItemComponent,
|
||||
SharedPipesModule,
|
||||
],
|
||||
declarations: [AppInterfacesPage, AppInterfacesItemComponent],
|
||||
})
|
||||
export class AppInterfacesPageModule {}
|
||||
|
||||
@@ -2,7 +2,10 @@ import { Component, Input, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { IonContent, ToastController } from '@ionic/angular'
|
||||
import { getUiInterfaceKey } from 'src/app/services/config.service'
|
||||
import { InstalledPackageDataEntry, InterfaceDef } from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
InstalledPackageDataEntry,
|
||||
InterfaceDef,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
|
||||
@@ -40,8 +43,12 @@ export class AppInterfacesPage {
|
||||
this.ui = {
|
||||
def: interfaces[uiKey],
|
||||
addresses: {
|
||||
'lan-address': uiAddresses['lan-address'] ? 'https://' + uiAddresses['lan-address'] : null,
|
||||
'tor-address': uiAddresses['tor-address'] ? 'http://' + uiAddresses['tor-address'] : null,
|
||||
'lan-address': uiAddresses['lan-address']
|
||||
? 'https://' + uiAddresses['lan-address']
|
||||
: null,
|
||||
'tor-address': uiAddresses['tor-address']
|
||||
? 'http://' + uiAddresses['tor-address']
|
||||
: null,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -53,8 +60,12 @@ export class AppInterfacesPage {
|
||||
return {
|
||||
def: interfaces[key],
|
||||
addresses: {
|
||||
'lan-address': addresses['lan-address'] ? 'https://' + addresses['lan-address'] : null,
|
||||
'tor-address': addresses['tor-address'] ? 'http://' + addresses['tor-address'] : null,
|
||||
'lan-address': addresses['lan-address']
|
||||
? 'https://' + addresses['lan-address']
|
||||
: null,
|
||||
'tor-address': addresses['tor-address']
|
||||
? 'http://' + addresses['tor-address']
|
||||
: null,
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -77,9 +88,7 @@ export class AppInterfacesPage {
|
||||
export class AppInterfacesItemComponent {
|
||||
@Input() interface: LocalInterface
|
||||
|
||||
constructor (
|
||||
private readonly toastCtrl: ToastController,
|
||||
) { }
|
||||
constructor(private readonly toastCtrl: ToastController) {}
|
||||
|
||||
launch(url: string): void {
|
||||
window.open(url, '_blank', 'noreferrer')
|
||||
@@ -87,8 +96,9 @@ export class AppInterfacesItemComponent {
|
||||
|
||||
async copy(address: string): Promise<void> {
|
||||
let message = ''
|
||||
await copyToClipboard(address || '')
|
||||
.then(success => { message = success ? 'copied to clipboard!' : 'failed to copy' })
|
||||
await copyToClipboard(address || '').then(success => {
|
||||
message = success ? 'copied to clipboard!' : 'failed to copy'
|
||||
})
|
||||
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: message,
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import {
|
||||
PackageMainStatus,
|
||||
PackageDataEntry,
|
||||
Manifest,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { PkgInfo } from 'src/app/util/get-package-info'
|
||||
|
||||
@@ -34,7 +34,7 @@ export class AppListReorderComponent {
|
||||
|
||||
readonly connectionFailure$ = this.connectionService
|
||||
.watchFailure$()
|
||||
.pipe(map((failure) => failure !== ConnectionFailure.None))
|
||||
.pipe(map(failure => failure !== ConnectionFailure.None))
|
||||
|
||||
constructor(private readonly connectionService: ConnectionService) {}
|
||||
|
||||
|
||||
@@ -3,9 +3,14 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppListPage } from './app-list.page'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module'
|
||||
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||
import { AppListIconComponent } from './app-list-icon/app-list-icon.component'
|
||||
import { AppListEmptyComponent } from './app-list-empty/app-list-empty.component'
|
||||
import { AppListPkgComponent } from './app-list-pkg/app-list-pkg.component'
|
||||
@@ -24,7 +29,10 @@ const routes: Routes = [
|
||||
imports: [
|
||||
CommonModule,
|
||||
StatusComponentModule,
|
||||
SharingModule,
|
||||
EmverPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
|
||||
@@ -3,10 +3,9 @@ import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { Observable } from 'rxjs'
|
||||
import { filter, map, switchMapTo, take, takeUntil, tap } from 'rxjs/operators'
|
||||
import { isEmptyObject, exists } from 'src/app/util/misc.util'
|
||||
import { isEmptyObject, exists, DestroyService } from '@start9labs/shared'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { parseDataModel, RecoveredInfo } from 'src/app/util/parse-data-model'
|
||||
import { DestroyService } from 'src/app/services/destroy.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-list',
|
||||
@@ -34,7 +33,7 @@ export class AppListPage {
|
||||
this.patch
|
||||
.watch$()
|
||||
.pipe(
|
||||
filter((data) => exists(data) && !isEmptyObject(data)),
|
||||
filter(data => exists(data) && !isEmptyObject(data)),
|
||||
take(1),
|
||||
map(parseDataModel),
|
||||
tap(({ order, pkgs, recoveredPkgs }) => {
|
||||
@@ -62,34 +61,32 @@ export class AppListPage {
|
||||
}
|
||||
|
||||
deleteRecovered(rec: RecoveredInfo): void {
|
||||
this.recoveredPkgs = this.recoveredPkgs.filter((item) => item !== rec)
|
||||
this.recoveredPkgs = this.recoveredPkgs.filter(item => item !== rec)
|
||||
}
|
||||
|
||||
private watchNewlyRecovered(): Observable<unknown> {
|
||||
return this.patch.watch$('package-data').pipe(
|
||||
filter((pkgs) => !!pkgs && Object.keys(pkgs).length !== this.pkgs.length),
|
||||
tap((pkgs) => {
|
||||
filter(pkgs => !!pkgs && Object.keys(pkgs).length !== this.pkgs.length),
|
||||
tap(pkgs => {
|
||||
const ids = Object.keys(pkgs)
|
||||
const newIds = ids.filter(
|
||||
(id) => !this.pkgs.find((pkg) => pkg.manifest.id === id),
|
||||
id => !this.pkgs.find(pkg => pkg.manifest.id === id),
|
||||
)
|
||||
|
||||
// remove uninstalled
|
||||
const filtered = this.pkgs.filter((pkg) =>
|
||||
ids.includes(pkg.manifest.id),
|
||||
)
|
||||
const filtered = this.pkgs.filter(pkg => ids.includes(pkg.manifest.id))
|
||||
|
||||
// add new entry to beginning of array
|
||||
const added = newIds.map((id) => pkgs[id])
|
||||
const added = newIds.map(id => pkgs[id])
|
||||
|
||||
this.pkgs = [...added, ...filtered]
|
||||
this.recoveredPkgs = this.recoveredPkgs.filter((rec) => !pkgs[rec.id])
|
||||
this.recoveredPkgs = this.recoveredPkgs.filter(rec => !pkgs[rec.id])
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private setOrder(): void {
|
||||
this.order = this.pkgs.map((pkg) => pkg.manifest.id)
|
||||
this.order = this.pkgs.map(pkg => pkg.manifest.id)
|
||||
this.api.setDbValue({ pointer: '/pkg-order', value: this.order })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
import { filter, map, startWith } from "rxjs/operators";
|
||||
import { PackageDataEntry } from "../../../services/patch-db/data-model";
|
||||
import { getPackageInfo, PkgInfo } from "../../../util/get-package-info";
|
||||
import { PatchDbService } from "../../../services/patch-db/patch-db.service";
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { Observable } from 'rxjs'
|
||||
import { filter, map, startWith } from 'rxjs/operators'
|
||||
import { PackageDataEntry } from '../../../services/patch-db/data-model'
|
||||
import { getPackageInfo, PkgInfo } from '../../../util/get-package-info'
|
||||
import { PatchDbService } from '../../../services/patch-db/patch-db.service'
|
||||
|
||||
@Pipe({
|
||||
name: "packageInfo",
|
||||
name: 'packageInfo',
|
||||
})
|
||||
export class PackageInfoPipe implements PipeTransform {
|
||||
constructor(private readonly patch: PatchDbService) {}
|
||||
|
||||
transform(pkg: PackageDataEntry): Observable<PkgInfo> {
|
||||
return this.patch.watch$("package-data", pkg.manifest.id).pipe(
|
||||
filter((v) => !!v),
|
||||
return this.patch.watch$('package-data', pkg.manifest.id).pipe(
|
||||
filter(v => !!v),
|
||||
map(getPackageInfo),
|
||||
startWith(getPackageInfo(pkg))
|
||||
);
|
||||
startWith(getPackageInfo(pkg)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppLogsPage } from './app-logs.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { LogsPageModule } from 'src/app/components/logs/logs.module'
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -18,7 +18,7 @@ const routes: Routes = [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
LogsPageModule,
|
||||
],
|
||||
declarations: [AppLogsPage],
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppMetricsPage } from './app-metrics.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { SkeletonListComponentModule } from 'src/app/components/skeleton-list/skeleton-list.component.module'
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -18,7 +18,7 @@ const routes: Routes = [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
SkeletonListComponentModule,
|
||||
],
|
||||
declarations: [AppMetricsPage],
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Metric } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { MainStatus } from 'src/app/services/patch-db/data-model'
|
||||
import { pauseFor } from 'src/app/util/misc.util'
|
||||
import { pauseFor } from '@start9labs/shared'
|
||||
|
||||
@Component({
|
||||
selector: 'app-metrics',
|
||||
|
||||
@@ -4,7 +4,11 @@ import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppPropertiesPage } from './app-properties.page'
|
||||
import { QRComponentModule } from 'src/app/components/qr/qr.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { MaskPipeModule } from 'src/app/pipes/mask/mask.module'
|
||||
import {
|
||||
SharedPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -19,7 +23,9 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
QRComponentModule,
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
MaskPipeModule,
|
||||
],
|
||||
declarations: [AppPropertiesPage],
|
||||
})
|
||||
|
||||
@@ -3,7 +3,13 @@ import { ActivatedRoute } from '@angular/router'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { copyToClipboard } from 'src/app/util/web.util'
|
||||
import { AlertController, IonContent, ModalController, NavController, ToastController } from '@ionic/angular'
|
||||
import {
|
||||
AlertController,
|
||||
IonContent,
|
||||
ModalController,
|
||||
NavController,
|
||||
ToastController,
|
||||
} from '@ionic/angular'
|
||||
import { PackageProperties } from 'src/app/util/properties.util'
|
||||
import { QRComponent } from 'src/app/components/qr/qr.component'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
@@ -45,13 +51,20 @@ export class AppPropertiesPage {
|
||||
await this.getProperties()
|
||||
|
||||
this.subs = [
|
||||
this.route.queryParams
|
||||
.subscribe(queryParams => {
|
||||
this.route.queryParams.subscribe(queryParams => {
|
||||
if (queryParams['pointer'] === this.pointer) return
|
||||
this.pointer = queryParams['pointer']
|
||||
this.node = getValueByPointer(this.properties, this.pointer || '')
|
||||
}),
|
||||
this.patch.watch$('package-data', this.pkgId, 'installed', 'status', 'main', 'status')
|
||||
this.patch
|
||||
.watch$(
|
||||
'package-data',
|
||||
this.pkgId,
|
||||
'installed',
|
||||
'status',
|
||||
'main',
|
||||
'status',
|
||||
)
|
||||
.subscribe(status => {
|
||||
this.running = status === PackageMainStatus.Running
|
||||
}),
|
||||
@@ -70,7 +83,10 @@ export class AppPropertiesPage {
|
||||
await this.getProperties()
|
||||
}
|
||||
|
||||
async presentDescription (property: { key: string, value: PackageProperties[''] }, e: Event) {
|
||||
async presentDescription(
|
||||
property: { key: string; value: PackageProperties[''] },
|
||||
e: Event,
|
||||
) {
|
||||
e.stopPropagation()
|
||||
|
||||
const alert = await this.alertCtrl.create({
|
||||
@@ -90,7 +106,9 @@ export class AppPropertiesPage {
|
||||
|
||||
async copy(text: string): Promise<void> {
|
||||
let message = ''
|
||||
await copyToClipboard(text).then(success => { message = success ? 'copied to clipboard!' : 'failed to copy'})
|
||||
await copyToClipboard(text).then(success => {
|
||||
message = success ? 'copied to clipboard!' : 'failed to copy'
|
||||
})
|
||||
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: message,
|
||||
@@ -118,7 +136,9 @@ export class AppPropertiesPage {
|
||||
private async getProperties(): Promise<void> {
|
||||
this.loading = true
|
||||
try {
|
||||
this.properties = await this.embassyApi.getPackageProperties({ id: this.pkgId })
|
||||
this.properties = await this.embassyApi.getPackageProperties({
|
||||
id: this.pkgId,
|
||||
})
|
||||
this.node = getValueByPointer(this.properties, this.pointer || '')
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
|
||||
@@ -3,10 +3,12 @@ import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { AppShowPage } from './app-show.page'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
import { InstallWizardComponentModule } from 'src/app/components/install-wizard/install-wizard.component.module'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { AppConfigPageModule } from 'src/app/modals/app-config/app-config.module'
|
||||
import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module'
|
||||
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||
import { AppShowHeaderComponent } from './components/app-show-header/app-show-header.component'
|
||||
import { AppShowProgressComponent } from './components/app-show-progress/app-show-progress.component'
|
||||
import { AppShowStatusComponent } from './components/app-show-status/app-show-status.component'
|
||||
@@ -18,6 +20,7 @@ import { ToHealthChecksPipe } from './pipes/to-health-checks.pipe'
|
||||
import { ToButtonsPipe } from './pipes/to-buttons.pipe'
|
||||
import { ToDependenciesPipe } from './pipes/to-dependencies.pipe'
|
||||
import { ToStatusPipe } from './pipes/to-status.pipe'
|
||||
import { InstallStatePipe } from './pipes/install-state.pipe'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -30,6 +33,7 @@ const routes: Routes = [
|
||||
declarations: [
|
||||
AppShowPage,
|
||||
HealthColorPipe,
|
||||
InstallStatePipe,
|
||||
ToHealthChecksPipe,
|
||||
ToButtonsPipe,
|
||||
ToDependenciesPipe,
|
||||
@@ -48,7 +52,9 @@ const routes: Routes = [
|
||||
RouterModule.forChild(routes),
|
||||
InstallWizardComponentModule,
|
||||
AppConfigPageModule,
|
||||
SharingModule,
|
||||
EmverPipesModule,
|
||||
LaunchablePipeModule,
|
||||
UiPipeModule,
|
||||
],
|
||||
})
|
||||
export class AppShowPageModule {}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import {
|
||||
PackageDataEntry,
|
||||
PackageState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PackageState } from '@start9labs/shared'
|
||||
import {
|
||||
PackageStatus,
|
||||
PrimaryStatus,
|
||||
@@ -70,4 +68,3 @@ export class AppShowPage {
|
||||
return STATES.includes(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import {
|
||||
InstallProgress,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { ProgressData } from 'src/app/util/package-loading-progress'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { InstallProgress, ProgressData } from '@start9labs/shared'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-progress',
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { UiLauncherService } from 'src/app/services/ui-launcher.service'
|
||||
import {
|
||||
InterfaceDef,
|
||||
PackageDataEntry,
|
||||
PackageState,
|
||||
Status,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
PackageStatus,
|
||||
PrimaryRendering,
|
||||
PrimaryStatus,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
import { isEmptyObject } from 'src/app/util/misc.util'
|
||||
import {
|
||||
InterfaceDef,
|
||||
PackageDataEntry,
|
||||
Status,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { isEmptyObject, PackageState } from '@start9labs/shared'
|
||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||
import {
|
||||
AlertController,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { ProgressData, packageLoadingProgress } from '@start9labs/shared'
|
||||
|
||||
@Pipe({
|
||||
name: 'installState',
|
||||
})
|
||||
export class InstallStatePipe implements PipeTransform {
|
||||
transform(pkg: PackageDataEntry): ProgressData | null {
|
||||
return packageLoadingProgress(pkg['install-progress'])
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,8 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
},
|
||||
// view in marketplace
|
||||
{
|
||||
action: () => this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`]),
|
||||
action: () =>
|
||||
this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`]),
|
||||
title: 'Marketplace',
|
||||
description: 'View service in marketplace',
|
||||
icon: 'storefront-outline',
|
||||
|
||||
@@ -3,12 +3,12 @@ import { NavigationExtras } from '@angular/router'
|
||||
import { NavController } from '@ionic/angular'
|
||||
import { combineLatest, Observable } from 'rxjs'
|
||||
import { filter, map, startWith } from 'rxjs/operators'
|
||||
import { DependentInfo, exists } from 'src/app/util/misc.util'
|
||||
import {
|
||||
DependencyError,
|
||||
DependencyErrorType,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { DependentInfo, exists } from '@start9labs/shared'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ModalService } from 'src/app/services/modal.service'
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
HealthCheckResult,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { exists, isEmptyObject } from 'src/app/util/misc.util'
|
||||
import { exists, isEmptyObject } from '@start9labs/shared'
|
||||
import { filter, map, startWith } from 'rxjs/operators'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { Observable } from 'rxjs'
|
||||
@@ -30,7 +30,8 @@ export class ToHealthChecksPipe implements PipeTransform {
|
||||
map(main => {
|
||||
// Question: is this ok or do we have to use Object.keys
|
||||
// to maintain order and the keys initially present in pkg?
|
||||
return main.status === PackageMainStatus.Running && !isEmptyObject(main.health)
|
||||
return main.status === PackageMainStatus.Running &&
|
||||
!isEmptyObject(main.health)
|
||||
? main.health
|
||||
: healthChecks
|
||||
}),
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevConfigPage } from './dev-config.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
@@ -22,7 +21,6 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
SharingModule,
|
||||
BackupReportPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DevInstructionsPage } from './dev-instructions.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
@@ -22,7 +21,6 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
SharingModule,
|
||||
BackupReportPageModule,
|
||||
FormsModule,
|
||||
MonacoEditorModule,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { DeveloperPage } from './developer-list.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -20,7 +19,6 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
SharingModule,
|
||||
BackupReportPageModule,
|
||||
],
|
||||
declarations: [DeveloperPage],
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { LoginPage } from './login.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -19,7 +19,7 @@ const routes: Routes = [
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
SharedPipesModule,
|
||||
],
|
||||
declarations: [LoginPage],
|
||||
})
|
||||
|
||||
@@ -2,8 +2,13 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
import { AppReleaseNotes } from './app-release-notes.page'
|
||||
import { SharingModule } from 'src/app/modules/sharing.module'
|
||||
import { MarketplacePipesModule } from '../pipes/marketplace-pipes.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -17,7 +22,10 @@ const routes: Routes = [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
SharingModule,
|
||||
TextSpinnerComponentModule,
|
||||
EmverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
MarketplacePipesModule,
|
||||
],
|
||||
declarations: [AppReleaseNotes],
|
||||
})
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { MarketplaceListPage } from './marketplace-list.page'
|
||||
import { SharingModule } from '../../../modules/sharing.module'
|
||||
import {
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MarketplacePipesModule } from '../pipes/marketplace-pipes.module'
|
||||
import { MarketplaceListPage } from './marketplace-list.page'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -21,8 +25,10 @@ const routes: Routes = [
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
RouterModule.forChild(routes),
|
||||
StatusComponentModule,
|
||||
SharingModule,
|
||||
TextSpinnerComponentModule,
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
MarketplacePipesModule,
|
||||
BadgeMenuComponentModule,
|
||||
],
|
||||
declarations: [MarketplaceListPage],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user