Merge branch 'next/minor' of github.com:Start9Labs/start-os into next/major

This commit is contained in:
Matt Hill
2024-11-25 19:02:07 -07:00
712 changed files with 83068 additions and 9240 deletions

View File

@@ -5,6 +5,7 @@ import { LoadingService } from '@start9labs/shared'
import { TuiDialogService } from '@taiga-ui/core'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
@Component({
selector: 'diagnostic-home',
@@ -25,6 +26,7 @@ export class HomePage {
private readonly api: ApiService,
private readonly dialogs: TuiDialogService,
@Inject(WA_WINDOW) private readonly window: Window,
readonly config: ConfigService,
) {}
async ngOnInit() {

View File

@@ -8,8 +8,7 @@ import {
} from '@angular/core'
import { FormGroup, ReactiveFormsModule } from '@angular/forms'
import { RouterModule } from '@angular/router'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import {
tuiMarkControlAsTouchedAndValidate,
TuiValueChanges,
@@ -29,10 +28,10 @@ export interface ActionButton<T> {
}
export interface FormContext<T> {
spec: CT.InputSpec
spec: IST.InputSpec
buttons: ActionButton<T>[]
value?: T
patch?: Operation[]
operations?: Operation[]
}
@Component({
@@ -110,7 +109,7 @@ export class FormComponent<T extends Record<string, any>> implements OnInit {
@Input() spec = this.context?.data.spec || {}
@Input() buttons = this.context?.data.buttons || []
@Input() patch = this.context?.data.patch || []
@Input() operations = this.context?.data.operations || []
@Input() value?: T = this.context?.data.value
form = new FormGroup({})
@@ -118,7 +117,7 @@ export class FormComponent<T extends Record<string, any>> implements OnInit {
ngOnInit() {
this.confirmService.markAsPristine()
this.form = this.formService.createForm(this.spec, this.value)
this.process(this.patch)
this.process(this.operations)
}
onReset() {
@@ -147,15 +146,16 @@ export class FormComponent<T extends Record<string, any>> implements OnInit {
this.context?.$implicit.complete()
}
private process(patch: Operation[]) {
patch.forEach(({ op, path }) => {
const control = this.form.get(path.substring(1).split('/'))
private process(operations: Operation[]) {
operations.forEach(operation => {
const control = this.form.get(operation.path.substring(1).split('/'))
if (!control || !control.parent) return
if (op !== 'remove') {
if (operation.op === 'add' || operation.op === 'replace') {
control.markAsDirty()
control.markAsTouched()
control.setValue(operation.value)
}
control.parent.markAsDirty()

View File

@@ -1,8 +1,8 @@
import { inject } from '@angular/core'
import { FormControlComponent } from './form-control/form-control.component'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
export abstract class Control<Spec extends CT.ValueSpec, Value> {
export abstract class Control<Spec extends Exclude<IST.ValueSpec, IST.ValueSpecHidden>, Value> {
private readonly control: FormControlComponent<Spec, Value> =
inject(FormControlComponent)

View File

@@ -0,0 +1,15 @@
import { Pipe, PipeTransform } from '@angular/core'
import { IST } from '@start9labs/start-sdk'
import { KeyValue } from '@angular/common'
@Pipe({
name: 'filterHidden',
})
export class FilterHiddenPipe implements PipeTransform {
transform(value: KeyValue<string, IST.ValueSpec>[]) {
return value.filter(x => x.value.type !== 'hidden') as KeyValue<
string,
Exclude<IST.ValueSpec, IST.ValueSpecHidden>
>[]
}
}

View File

@@ -18,6 +18,7 @@ import {
} from '@taiga-ui/core'
import { TUI_CONFIRM } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import { IST } from '@start9labs/start-sdk'
import { FormService } from 'src/app/services/form.service'
import { ERRORS } from '../form-group/form-group.component'
@@ -29,7 +30,7 @@ import { ERRORS } from '../form-group/form-group.component'
})
export class FormArrayComponent {
@Input({ required: true })
spec!: CT.ValueSpecList
spec!: IST.ValueSpecList
@HostBinding('@tuiParentStop')
readonly animation = tuiToAnimationOptions(inject(TUI_ANIMATIONS_SPEED))

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
import { MaskitoOptions } from '@maskito/core'
@@ -8,7 +8,7 @@ import { MaskitoOptions } from '@maskito/core'
templateUrl: './form-color.component.html',
styleUrls: ['./form-color.component.scss'],
})
export class FormColorComponent extends Control<CT.ValueSpecColor, string> {
export class FormColorComponent extends Control<IST.ValueSpecColor, string> {
readonly mask: MaskitoOptions = {
mask: ['#', ...Array(6).fill(/[0-9a-f]/i)],
}

View File

@@ -37,4 +37,4 @@
Accept
</button>
</div>
</ng-template>
</ng-template>

View File

@@ -7,10 +7,10 @@ import {
ViewChild,
} from '@angular/core'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { CT } from '@start9labs/start-sdk'
import { TuiAlertService, TuiDialogContext } from '@taiga-ui/core'
import { AbstractTuiNullableControl } from '@taiga-ui/legacy'
import { filter } from 'rxjs'
import { TuiAlertService, TuiDialogContext } from '@taiga-ui/core'
import { IST } from '@start9labs/start-sdk'
import { ERRORS } from '../form-group/form-group.component'
import { FORM_CONTROL_PROVIDERS } from './form-control.providers'
@@ -22,7 +22,7 @@ import { FORM_CONTROL_PROVIDERS } from './form-control.providers'
providers: FORM_CONTROL_PROVIDERS,
})
export class FormControlComponent<
T extends CT.ValueSpec,
T extends Exclude<IST.ValueSpec, IST.ValueSpecHidden>,
V,
> extends AbstractTuiNullableControl<V> {
@Input({ required: true })

View File

@@ -1,6 +1,6 @@
import { forwardRef, Provider } from '@angular/core'
import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { FormControlComponent } from './form-control.component'
interface ValidatorsPatternError {
@@ -12,7 +12,7 @@ export const FORM_CONTROL_PROVIDERS: Provider[] = [
{
provide: TUI_VALIDATION_ERRORS,
deps: [forwardRef(() => FormControlComponent)],
useFactory: (control: FormControlComponent<CT.ValueSpec, string>) => ({
useFactory: (control: FormControlComponent<Exclude<IST.ValueSpec, IST.ValueSpecHidden>, string>) => ({
required: 'Required',
pattern: ({ requiredPattern }: ValidatorsPatternError) =>
('patterns' in control.spec &&

View File

@@ -6,7 +6,7 @@ import {
tuiPure,
TuiTime,
} from '@taiga-ui/cdk'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
@Component({
@@ -14,7 +14,7 @@ import { Control } from '../control'
templateUrl: './form-datetime.component.html',
})
export class FormDatetimeComponent extends Control<
CT.ValueSpecDatetime,
IST.ValueSpecDatetime,
string
> {
readonly min = TUI_FIRST_DAY

View File

@@ -1,5 +1,5 @@
<ng-container
*ngFor="let entry of spec | keyvalue: asIsOrder"
*ngFor="let entry of spec | keyvalue: asIsOrder | filterHidden"
[ngSwitch]="entry.value.type"
[tuiTextfieldCleaner]="true"
>

View File

@@ -4,7 +4,7 @@ import {
Input,
ViewEncapsulation,
} from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { FORM_GROUP_PROVIDERS } from './form-group.providers'
export const ERRORS = [
@@ -27,7 +27,7 @@ export const ERRORS = [
viewProviders: [FORM_GROUP_PROVIDERS],
})
export class FormGroupComponent {
@Input() spec: CT.InputSpec = {}
@Input() spec: IST.InputSpec = {}
asIsOrder() {
return 0

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
import { tuiPure } from '@taiga-ui/cdk'
import { invert } from '@start9labs/shared'
@@ -9,7 +9,7 @@ import { invert } from '@start9labs/shared'
templateUrl: './form-multiselect.component.html',
})
export class FormMultiselectComponent extends Control<
CT.ValueSpecMultiselect,
IST.ValueSpecMultiselect,
readonly string[]
> {
private readonly inverted = invert(this.spec.values)

View File

@@ -1,11 +1,11 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
@Component({
selector: 'form-number',
templateUrl: './form-number.component.html',
})
export class FormNumberComponent extends Control<CT.ValueSpecNumber, number> {
export class FormNumberComponent extends Control<IST.ValueSpecNumber, number> {
protected readonly Infinity = Infinity
}

View File

@@ -7,7 +7,7 @@ import {
Output,
} from '@angular/core'
import { ControlContainer } from '@angular/forms'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
@Component({
selector: 'form-object',
@@ -17,7 +17,7 @@ import { CT } from '@start9labs/start-sdk'
})
export class FormObjectComponent {
@Input({ required: true })
spec!: CT.ValueSpecObject
spec!: IST.ValueSpecObject
@Input()
open = false

View File

@@ -2,13 +2,12 @@
[tuiHintContent]="spec | hint"
[disabled]="disabled"
[readOnly]="readOnly"
[tuiTextfieldCleaner]="!spec.required"
[tuiTextfieldCleaner]="false"
[pseudoInvalid]="invalid"
[(ngModel)]="selected"
(focusedChange)="onFocus($event)"
>
{{ spec.name }}
<span *ngIf="spec.required">*</span>
{{ spec.name }}*
<select
tuiSelect
[placeholder]="spec.name"

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { invert } from '@start9labs/shared'
import { Control } from '../control'
@@ -7,7 +7,7 @@ import { Control } from '../control'
selector: 'form-select',
templateUrl: './form-select.component.html',
})
export class FormSelectComponent extends Control<CT.ValueSpecSelect, string> {
export class FormSelectComponent extends Control<IST.ValueSpecSelect, string> {
private readonly inverted = invert(this.spec.values)
readonly items = Object.values(this.spec.values)

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT, utils } from '@start9labs/start-sdk'
import { IST, utils } from '@start9labs/start-sdk'
import { Control } from '../control'
@Component({
@@ -7,7 +7,7 @@ import { Control } from '../control'
templateUrl: './form-text.component.html',
styleUrls: ['./form-text.component.scss'],
})
export class FormTextComponent extends Control<CT.ValueSpecText, string> {
export class FormTextComponent extends Control<IST.ValueSpecText, string> {
masked = true
generate() {

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
@Component({
@@ -7,6 +7,6 @@ import { Control } from '../control'
templateUrl: './form-textarea.component.html',
})
export class FormTextareaComponent extends Control<
CT.ValueSpecTextarea,
IST.ValueSpecTextarea,
string
> {}

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { Control } from '../control'
@Component({
@@ -7,4 +7,7 @@ import { Control } from '../control'
templateUrl: './form-toggle.component.html',
host: { class: 'g-toggle' },
})
export class FormToggleComponent extends Control<CT.ValueSpecToggle, boolean> {}
export class FormToggleComponent extends Control<
IST.ValueSpecToggle,
boolean
> {}

View File

@@ -6,7 +6,7 @@ import {
OnChanges,
} from '@angular/core'
import { ControlContainer, FormGroupName } from '@angular/forms'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
import { FormService } from 'src/app/services/form.service'
import { tuiPure } from '@taiga-ui/cdk'
@@ -24,9 +24,9 @@ import { tuiPure } from '@taiga-ui/cdk'
})
export class FormUnionComponent implements OnChanges {
@Input({ required: true })
spec!: CT.ValueSpecUnion
spec!: IST.ValueSpecUnion
selectSpec!: CT.ValueSpecSelect
selectSpec!: IST.ValueSpecSelect
private readonly form = inject(FormGroupName)
private readonly formService = inject(FormService)

View File

@@ -49,6 +49,7 @@ import { FormToggleComponent } from './form-toggle/form-toggle.component'
import { FormUnionComponent } from './form-union/form-union.component'
import { HintPipe } from './hint.pipe'
import { MustachePipe } from './mustache.pipe'
import { FilterHiddenPipe } from './filter-hidden.pipe'
@NgModule({
imports: [
@@ -100,6 +101,7 @@ import { MustachePipe } from './mustache.pipe'
MustachePipe,
HintPipe,
ControlDirective,
FilterHiddenPipe,
],
exports: [FormGroupComponent],
})

View File

@@ -1,11 +1,11 @@
import { Pipe, PipeTransform } from '@angular/core'
import { CT } from '@start9labs/start-sdk'
import { IST } from '@start9labs/start-sdk'
@Pipe({
name: 'hint',
})
export class HintPipe implements PipeTransform {
transform(spec: CT.ValueSpec): string {
transform(spec: Exclude<IST.ValueSpec, IST.ValueSpecHidden>): string {
const hint = []
if (spec.description) {

View File

@@ -2,50 +2,46 @@ import {
ChangeDetectionStrategy,
Component,
Input,
OnChanges,
OnInit,
} from '@angular/core'
import { TuiNotification } from '@taiga-ui/core'
import { compare, getValueByPointer, Operation } from 'fast-json-patch'
import { getValueByPointer, Operation } from 'fast-json-patch'
import { isObject } from '@start9labs/shared'
import { tuiIsNumber } from '@taiga-ui/cdk'
import { CommonModule } from '@angular/common'
@Component({
selector: 'config-dep',
selector: 'action-request-info',
template: `
<tui-notification>
<h3 style="margin: 0 0 0.5rem; font-size: 1.25rem;">
{{ package }}
</h3>
The following modifications have been made to {{ package }} to satisfy
{{ dep }}:
<tui-notification *ngIf="diff.length">
The following modifications were made:
<ul>
<li *ngFor="let d of diff" [innerHTML]="d"></li>
</ul>
To accept these modifications, click "Save".
</tui-notification>
`,
standalone: true,
imports: [CommonModule, TuiNotification],
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [
`
tui-notification {
margin-bottom: 1.5rem;
}
`,
],
})
export class ConfigDepComponent implements OnChanges {
export class ActionRequestInfoComponent implements OnInit {
@Input()
package = ''
originalValue: object = {}
@Input()
dep = ''
@Input()
original: object = {}
@Input()
value: object = {}
operations: Operation[] = []
diff: string[] = []
ngOnChanges() {
this.diff = compare(this.original, this.value).map(
ngOnInit() {
this.diff = this.operations.map(
op => `${this.getPath(op)}: ${this.getMessage(op)}`,
)
}
@@ -69,20 +65,20 @@ export class ConfigDepComponent implements OnChanges {
private getMessage(operation: Operation): string {
switch (operation.op) {
case 'add':
return `Added ${this.getNewValue(operation.value)}`
return `added ${this.getNewValue(operation.value)}`
case 'remove':
return `Removed ${this.getOldValue(operation.path)}`
return `removed ${this.getOldValue(operation.path)}`
case 'replace':
return `Changed from ${this.getOldValue(
return `changed from ${this.getOldValue(
operation.path,
)} to ${this.getNewValue(operation.value)}`
default:
return `Unknown operation`
return `Unknown operation` // unreachable
}
}
private getOldValue(path: any): string {
const val = getValueByPointer(this.original, path)
const val = getValueByPointer(this.originalValue, path)
if (['string', 'number', 'boolean'].includes(typeof val)) {
return val
} else if (isObject(val)) {

View File

@@ -1,257 +0,0 @@
import { CommonModule } from '@angular/common'
import { Component, Inject, ViewChild } from '@angular/core'
import {
ErrorService,
getErrorMessage,
isEmptyObject,
LoadingService,
} from '@start9labs/shared'
import { CT, T } from '@start9labs/start-sdk'
import {
TuiDialogContext,
TuiDialogService,
TuiLoader,
TuiButton,
TuiNotification,
} from '@taiga-ui/core'
import { TuiConfirmData, TUI_CONFIRM } from '@taiga-ui/kit'
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
import { compare, Operation } from 'fast-json-patch'
import { PatchDB } from 'patch-db-client'
import { endWith, firstValueFrom, Subscription } from 'rxjs'
import { ConfigDepComponent } from 'src/app/routes/portal/modals/config-dep.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import {
DataModel,
PackageDataEntry,
} from 'src/app/services/patch-db/data-model'
import { hasCurrentDeps } from 'src/app/utils/has-deps'
import {
getAllPackages,
getManifest,
getPackage,
} from 'src/app/utils/get-package-data'
import { Breakages } from 'src/app/services/api/api.types'
import { InvalidService } from 'src/app/routes/portal/components/form/invalid.service'
import {
ActionButton,
FormComponent,
} from 'src/app/routes/portal/components/form.component'
import { DependentInfo } from 'src/app/types/dependent-info'
import { ToManifestPipe } from '../pipes/to-manifest'
export interface PackageConfigData {
readonly pkgId: string
readonly dependentInfo?: DependentInfo
}
@Component({
template: `
<tui-loader *ngIf="loadingText" size="l" [textContent]="loadingText" />
<tui-notification
*ngIf="!loadingText && (loadingError || !pkg)"
status="error"
>
<div [innerHTML]="loadingError"></div>
</tui-notification>
<ng-container
*ngIf="
!loadingText && !loadingError && pkg && (pkg | toManifest) as manifest
"
>
<tui-notification *ngIf="success" status="success">
{{ manifest.title }} has been automatically configured with recommended
defaults. Make whatever changes you want, then click "Save".
</tui-notification>
<config-dep
*ngIf="dependentInfo && value && original"
[package]="manifest.title"
[dep]="dependentInfo.title"
[original]="original"
[value]="value"
/>
<tui-notification *ngIf="!manifest.hasConfig" status="warning">
No config options for {{ manifest.title }} {{ manifest.version }}.
</tui-notification>
<app-form
[spec]="spec"
[value]="value || {}"
[buttons]="buttons"
[patch]="patch"
>
<button
tuiButton
appearance="flat"
type="reset"
[style.margin-right]="'auto'"
>
Reset Defaults
</button>
</app-form>
</ng-container>
`,
styles: [
`
tui-notification {
font-size: 1rem;
margin-bottom: 1rem;
}
`,
],
standalone: true,
imports: [
CommonModule,
FormComponent,
TuiLoader,
TuiNotification,
TuiButton,
ConfigDepComponent,
ToManifestPipe,
],
providers: [InvalidService],
})
export class ConfigModal {
@ViewChild(FormComponent)
private readonly form?: FormComponent<Record<string, any>>
readonly pkgId = this.context.data.pkgId
readonly dependentInfo = this.context.data.dependentInfo
loadingError = ''
loadingText = this.dependentInfo
? `Setting properties to accommodate ${this.dependentInfo.title}`
: 'Loading Config'
pkg?: PackageDataEntry
spec: CT.InputSpec = {}
patch: Operation[] = []
buttons: ActionButton<any>[] = [
{
text: 'Save',
handler: value => this.save(value),
},
]
original: object | null = null
value: object | null = null
constructor(
@Inject(POLYMORPHEUS_CONTEXT)
private readonly context: TuiDialogContext<void, PackageConfigData>,
private readonly dialogs: TuiDialogService,
private readonly errorService: ErrorService,
private readonly loader: LoadingService,
private readonly embassyApi: ApiService,
private readonly patchDb: PatchDB<DataModel>,
) {}
get success(): boolean {
return (
!!this.form &&
!this.form.form.dirty &&
!this.original &&
!this.pkg?.status?.configured
)
}
async ngOnInit() {
try {
this.pkg = await getPackage(this.patchDb, this.pkgId)
if (!this.pkg) {
this.loadingError = 'This service does not exist'
return
}
if (this.dependentInfo) {
const depConfig = await this.embassyApi.dryConfigureDependency({
dependencyId: this.pkgId,
dependentId: this.dependentInfo.id,
})
this.original = depConfig.oldConfig
this.value = depConfig.newConfig || this.original
this.spec = depConfig.spec
this.patch = compare(this.original, this.value)
} else {
const { config, spec } = await this.embassyApi.getPackageConfig({
id: this.pkgId,
})
this.original = config
this.value = config
this.spec = spec
}
} catch (e: any) {
this.loadingError = getErrorMessage(e)
} finally {
this.loadingText = ''
}
}
private async save(config: any) {
const loader = new Subscription()
try {
if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patchDb))) {
await this.configureDeps(config, loader)
} else {
await this.configure(config, loader)
}
} catch (e: any) {
this.errorService.handleError(e)
} finally {
loader.unsubscribe()
}
}
private async configureDeps(
config: Record<string, any>,
loader: Subscription,
) {
loader.unsubscribe()
loader.closed = false
loader.add(this.loader.open('Checking dependent services...').subscribe())
const breakages = await this.embassyApi.drySetPackageConfig({
id: this.pkgId,
config,
})
loader.unsubscribe()
loader.closed = false
if (isEmptyObject(breakages) || (await this.approveBreakages(breakages))) {
await this.configure(config, loader)
}
}
private async configure(config: Record<string, any>, loader: Subscription) {
loader.unsubscribe()
loader.closed = false
loader.add(this.loader.open('Saving...').subscribe())
await this.embassyApi.setPackageConfig({ id: this.pkgId, config })
this.context.$implicit.complete()
}
private async approveBreakages(breakages: T.PackageId[]): Promise<boolean> {
const packages = await getAllPackages(this.patchDb)
const message =
'As a result of this change, the following services will no longer work properly and may crash:<ul>'
const content = `${message}${breakages.map(
id => `<li><b>${getManifest(packages[id]).title}</b></li>`,
)}</ul>`
const data: TuiConfirmData = { content, yes: 'Continue', no: 'Cancel' }
return firstValueFrom(
this.dialogs.open<boolean>(TUI_CONFIRM, { data }).pipe(endWith(false)),
)
}
}

View File

@@ -5,6 +5,14 @@ import { PackageBackupInfo } from 'src/app/services/api/api.types'
import { ConfigService } from 'src/app/services/config.service'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { RecoverOption } from '../types/recover-option'
import { Version } from '@start9labs/start-sdk'
export interface AppRecoverOption extends PackageBackupInfo {
id: string
checked: boolean
installed: boolean
newerOS: boolean
}
@Pipe({
name: 'toOptions',
@@ -26,7 +34,10 @@ export class ToOptionsPipe implements PipeTransform {
id,
installed: !!packageData[id],
checked: false,
newerStartOs: this.compare(packageBackups[id].osVersion),
newerOS:
Version.parse(packageBackups[id].osVersion).compare(
Version.parse(this.config.version),
) === 'greater',
}))
.sort((a, b) =>
b.title.toLowerCase() > a.title.toLowerCase() ? -1 : 1,
@@ -34,11 +45,4 @@ export class ToOptionsPipe implements PipeTransform {
),
)
}
private compare(version: string): boolean {
// checks to see if backup was made on a newer version of startOS
return (
this.exver.compareOsVersion(version, this.config.version) === 'greater'
)
}
}

View File

@@ -4,5 +4,5 @@ export interface RecoverOption extends PackageBackupInfo {
id: string
checked: boolean
installed: boolean
newerStartOs: boolean
newerOs: boolean
}