chore: cleanup and small misc fixes

display success alert if on latest EOS after check for update

fix bug with loader dismiss after alert present

fix restart button on update complete alert and fix mocks to account for this state

fix make clean and adjust default registry names
This commit is contained in:
Lucy Cifferello
2022-06-14 18:38:36 -06:00
committed by Lucy C
parent 4ad9886517
commit 37304a9d92
28 changed files with 221 additions and 130 deletions

View File

@@ -58,7 +58,7 @@ npm run start:diagnostic-ui
This section enables you to run a local frontend with a remote backend (eg. hosted on a live Embassy). It assumes you have completed Step 1 and Step 2 in the [section above](#running-locally-with-mocks)
1. Set `useMocks: true` in `config.json`
1. Set `useMocks: false` in `config.json`
2. Create a proxy configuration file from the sample:

View File

@@ -1,6 +1,14 @@
import { Component } from '@angular/core'
import { AlertController, LoadingController, ModalController } from '@ionic/angular'
import { ApiService, CifsBackupTarget, EmbassyOSRecoveryInfo } from 'src/app/services/api/api.service'
import {
AlertController,
LoadingController,
ModalController,
} from '@ionic/angular'
import {
ApiService,
CifsBackupTarget,
EmbassyOSRecoveryInfo,
} from 'src/app/services/api/api.service'
import { PasswordPage } from '../password/password.page'
@Component({
@@ -17,18 +25,18 @@ export class CifsModal {
password: '',
}
constructor (
constructor(
private readonly modalController: ModalController,
private readonly apiService: ApiService,
private readonly loadingCtrl: LoadingController,
private readonly alertCtrl: AlertController,
) { }
) {}
cancel () {
cancel() {
this.modalController.dismiss()
}
async submit (): Promise<void> {
async submit(): Promise<void> {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Connecting to shared folder...',
@@ -38,23 +46,30 @@ export class CifsModal {
try {
const embassyOS = await this.apiService.verifyCifs(this.cifs)
await loader.dismiss()
const is02x = embassyOS.version.startsWith('0.2')
if (is02x) {
this.modalController.dismiss({
cifs: this.cifs,
}, 'success')
this.modalController.dismiss(
{
cifs: this.cifs,
},
'success',
)
} else {
this.presentModalPassword(embassyOS)
}
} catch (e) {
await loader.dismiss()
this.presentAlertFailed()
} finally {
loader.dismiss()
}
}
private async presentModalPassword (embassyOS: EmbassyOSRecoveryInfo): Promise<void> {
private async presentModalPassword(
embassyOS: EmbassyOSRecoveryInfo,
): Promise<void> {
const target: CifsBackupTarget = {
...this.cifs,
mountable: true,
@@ -68,19 +83,23 @@ export class CifsModal {
})
modal.onDidDismiss().then(res => {
if (res.role === 'success') {
this.modalController.dismiss({
cifs: this.cifs,
recoveryPassword: res.data.password,
}, 'success')
this.modalController.dismiss(
{
cifs: this.cifs,
recoveryPassword: res.data.password,
},
'success',
)
}
})
await modal.present()
}
private async presentAlertFailed (): Promise<void> {
private async presentAlertFailed(): Promise<void> {
const alert = await this.alertCtrl.create({
header: 'Connection Failed',
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.',
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.',
buttons: ['OK'],
})
alert.present()

View File

@@ -45,23 +45,43 @@ export class UpdateToastService extends Observable<unknown> {
super(subscriber => this.stream$.subscribe(subscriber))
}
LOADER: LoadingOptions = {
spinner: 'lines',
message: 'Restarting...',
}
TOAST: ToastOptions = {
header: 'EOS download complete!',
message:
'Restart your Embassy for these updates to take effect. It can take several minutes to come back online.',
position: 'bottom',
duration: 0,
cssClass: 'success-toast',
buttons: [
{
side: 'start',
icon: 'close',
handler: () => true,
},
{
side: 'end',
text: 'Restart',
handler: () => {
this.restart()
},
},
],
}
private async showToast() {
await this.updateToast?.dismiss()
this.updateToast = await this.toastCtrl.create(TOAST)
this.updateToast.buttons?.push({
side: 'end',
text: 'Restart',
handler: () => {
this.restart()
},
})
this.updateToast = await this.toastCtrl.create(this.TOAST)
await this.updateToast.present()
}
private async restart(): Promise<void> {
const loader = await this.loadingCtrl.create(LOADER)
const loader = await this.loadingCtrl.create(this.LOADER)
await loader.present()
@@ -74,24 +94,3 @@ export class UpdateToastService extends Observable<unknown> {
}
}
}
const LOADER: LoadingOptions = {
spinner: 'lines',
message: 'Restarting...',
}
const TOAST: ToastOptions = {
header: 'EOS download complete!',
message:
'Restart your Embassy for these updates to take effect. It can take several minutes to come back online.',
position: 'bottom',
duration: 0,
cssClass: 'success-toast',
buttons: [
{
side: 'start',
icon: 'close',
handler: () => true,
},
],
}

View File

@@ -32,8 +32,8 @@ export class SnekDirective {
if (data?.highScore > highScore) {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Saving High Score...',
message: 'Saving high score...',
backdropDismiss: true,
})
await loader.present()

View File

@@ -66,22 +66,30 @@
<ion-footer>
<ion-toolbar>
<ng-container *ngIf="!initializing && swiper">
<ion-buttons slot="end" style="padding-right: 8px">
<ion-buttons slot="end" class="ion-padding-end">
<ion-button
*ngIf="error; else noError"
fill="solid"
color="dark"
(click)="dismiss()"
class="enter-click"
class="enter-click btn-128"
>
<b>Dismiss</b>
Dismiss
</ion-button>
<ng-template #noError>
<ion-button
*ngIf="!currentSlide.loading && !swiper.isEnd"
fill="solid"
color="primary"
(click)="next()"
class="enter-click"
class="enter-click btn-128"
[class.no-click]="currentSlide.loading"
>
<b>Continue</b>
{{
currentIndex < swiper.slides.length - 2
? 'Continue'
: params.submitBtn
}}
</ion-button>
</ng-template>
</ion-buttons>

View File

@@ -27,6 +27,7 @@ export class AppWizardComponent {
action: WizardAction
title: string
slides: SlideDefinition[]
submitBtn: string
version?: string
}
@@ -76,7 +77,6 @@ export class AppWizardComponent {
}
setError(e: any) {
console.log(e)
this.error = e
}

View File

@@ -7,7 +7,7 @@
{{ v.version }}
</b>
</h4>
<hr style="height: 0; border-width: 1px" />
<div class="underline" style="margin: unset"></div>
<div [innerHTML]="v.notes | markdown"></div>
<br />
</div>

View File

@@ -63,6 +63,7 @@ export class WizardDefs {
title,
version,
slides: slides.filter(exists),
submitBtn: 'Begin Update',
}
}
@@ -110,6 +111,7 @@ export class WizardDefs {
title,
version,
slides: slides.filter(exists),
submitBtn: 'Begin Update',
}
}
@@ -160,6 +162,7 @@ export class WizardDefs {
title,
version,
slides: slides.filter(exists),
submitBtn: 'Begin Downgrade',
}
}
@@ -199,6 +202,7 @@ export class WizardDefs {
action: 'uninstall',
title,
slides: slides.filter(exists),
submitBtn: 'Uninstall Anyway',
}
}
@@ -228,6 +232,7 @@ export class WizardDefs {
action: 'stop',
title,
slides: slides.filter(exists),
submitBtn: 'Stop Anyway',
}
}
@@ -261,6 +266,7 @@ export class WizardDefs {
action: 'configure',
title,
slides: slides.filter(exists),
submitBtn: 'Configure Anyway',
}
}
}

View File

@@ -14,7 +14,7 @@
rendering.display === PR[PS.Stopping].display &&
(sigtermTimeout | durationToSeconds) > 30
"
>This may take a while.</span
>this may take a while</span
>
</span>
<span *ngIf="installProgress">

View File

@@ -107,19 +107,21 @@
>
<ion-button
*ngIf="hasConfig"
fill="outline"
fill="solid"
color="primary"
[disabled]="saving"
(click)="save()"
class="enter-click"
class="enter-click btn-128"
[class.no-click]="saving"
>
Save
</ion-button>
<ion-button
*ngIf="!hasConfig"
fill="outline"
fill="solid"
color="dark"
(click)="dismiss()"
class="enter-click"
class="enter-click btn-128"
>
Close
</ion-button>

View File

@@ -121,7 +121,7 @@ export class AppConfigPage {
async dismiss() {
if (this.configForm?.dirty) {
await this.presentAlertUnsaved()
this.presentAlertUnsaved()
} else {
this.modalCtrl.dismiss()
}

View File

@@ -20,13 +20,23 @@
<ion-text color="success">Ready to restore</ion-text>
</p>
<p *ngIf="option.installed">
<ion-text color="warning">Unavailable. {{ option.title }} is already installed.</ion-text>
<ion-text color="warning"
>Unavailable. {{ option.title }} is already installed.</ion-text
>
</p>
<p *ngIf="option['newer-eos']">
<ion-text color="danger">Unavailable. Backup was made on a newer version of EmbassyOS.</ion-text>
<ion-text color="danger"
>Unavailable. Backup was made on a newer version of
EmbassyOS.</ion-text
>
</p>
</ion-label>
<ion-checkbox slot="end" [(ngModel)]="option.checked" [disabled]="option.installed || option['newer-eos']" (ionChange)="handleChange()"></ion-checkbox>
<ion-checkbox
slot="end"
[(ngModel)]="option.checked"
[disabled]="option.installed || option['newer-eos']"
(ionChange)="handleChange()"
></ion-checkbox>
</ion-item>
</ion-item-group>
</ion-content>
@@ -34,7 +44,13 @@
<ion-footer>
<ion-toolbar>
<ion-buttons slot="end" class="ion-padding-end">
<ion-button [disabled]="!hasSelection" fill="outline" (click)="restore()" class="enter-click">
<ion-button
[disabled]="!hasSelection"
fill="solid"
color="primary"
(click)="restore()"
class="enter-click btn-128"
>
Restore Selected
</ion-button>
</ion-buttons>

View File

@@ -1,26 +1,30 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-title> {{ spec.name }} </ion-title>
<ion-buttons slot="end">
<ion-button (click)="dismiss()">
<ion-icon slot="icon-only" name="close"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>
{{ spec.name }}
</ion-title>
<ion-buttons slot="end">
<ion-button slot="end" fill="clear" (click)="toggleSelectAll()">
{{ selectAll ? 'All' : 'None' }}
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item-group>
<ion-item-divider style="padding-bottom: 4px">
<ion-buttons slot="end">
<ion-button fill="clear" (click)="toggleSelectAll()">
<b>{{ selectAll ? 'All' : 'None' }}</b>
</ion-button>
</ion-buttons>
</ion-item-divider>
<ion-item *ngFor="let option of options | keyvalue : asIsOrder">
<ion-label>{{ spec.spec['value-names'][option.key] }}</ion-label>
<ion-checkbox slot="end" [(ngModel)]="option.value" (click)="toggleSelected(option.key)"></ion-checkbox>
<ion-checkbox
slot="end"
[(ngModel)]="option.value"
(click)="toggleSelected(option.key)"
></ion-checkbox>
</ion-item>
</ion-item-group>
</ion-content>
@@ -28,7 +32,12 @@
<ion-footer>
<ion-toolbar>
<ion-buttons slot="end" class="ion-padding-end">
<ion-button fill="outline" (click)="save()" class="enter-click">
<ion-button
fill="solid"
color="primary"
(click)="save()"
class="enter-click btn-128"
>
Done
</ion-button>
</ion-buttons>

View File

@@ -90,7 +90,12 @@
</ul>
<div class="ion-text-center ion-padding">
<ion-button fill="outline" color="dark" (click)="dismiss()">
<ion-button
fill="solid"
color="primary"
(click)="dismiss()"
class="enter-click btn-128"
>
Begin
</ion-button>
</div>

View File

@@ -15,9 +15,14 @@
<ion-toolbar>
<ion-title slot="start">High Score: {{ highScore }}</ion-title>
<ion-buttons slot="end" class="ion-padding-end">
<ion-button (click)="dismiss()" class="enter-click"
>Byeeeeeee!</ion-button
<ion-button
fill="solid"
color="primary"
(click)="dismiss()"
class="enter-click btn-128"
>
Save and Quit
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-footer>

View File

@@ -1,7 +1,6 @@
.bulb {
position: absolute !important;
left: 9px !important;
top: 8px !important;
top: 6px !important;
height: 14px;
width: 14px;
border-radius: 100%;
@@ -10,7 +9,6 @@
.warning-icon {
position: absolute !important;
left: 6px !important;
top: 6px !important;
font-size: 12px;
border-radius: 100%;
@@ -21,7 +19,6 @@
.spinner {
position: absolute !important;
left: 6px !important;
top: 6px !important;
width: 18px;
}

View File

@@ -12,19 +12,10 @@ import { ValueSpecObject } from 'src/app/pkg-config/config-types'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
import { PatchDbService } from '../../../services/patch-db/patch-db.service'
import { v4 } from 'uuid'
import {
UIData,
UIMarketplaceData,
} from '../../../services/patch-db/data-model'
import { UIMarketplaceData } from '../../../services/patch-db/data-model'
import { ConfigService } from '../../../services/config.service'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import {
distinctUntilChanged,
finalize,
first,
map,
startWith,
} from 'rxjs/operators'
import { distinctUntilChanged, finalize, first } from 'rxjs/operators'
type Marketplaces = {
id: string | undefined
@@ -55,11 +46,8 @@ export class MarketplacesPage {
ngOnInit() {
this.patch
.watch$('ui')
.pipe(
map((ui: UIData) => ui.marketplace),
distinctUntilChanged(),
)
.watch$('ui', 'marketplace')
.pipe(distinctUntilChanged())
.subscribe((mp: UIMarketplaceData | undefined) => {
let marketplaces: Marketplaces = [
{
@@ -114,8 +102,7 @@ export class MarketplacesPage {
async presentAction(id: string = '') {
// no need to view actions if is selected marketplace
if (!id || id === this.patch.getData().ui.marketplace?.['selected-id'])
return
if (id === this.patch.getData().ui.marketplace?.['selected-id']) return
const buttons: ActionSheetButton[] = [
{

View File

@@ -16,6 +16,7 @@ import { WizardDefs } from 'src/app/components/app-wizard/wizard-defs'
import { exists, isEmptyObject, ErrorToastService } from '@start9labs/shared'
import { EOSService } from 'src/app/services/eos.service'
import { LocalStorageService } from 'src/app/services/local-storage.service'
import { RecoveredPackageDataEntry } from 'src/app/services/patch-db/data-model'
@Component({
selector: 'server-show',
@@ -47,7 +48,7 @@ export class ServerShowPage {
this.patch
.watch$('recovered-packages')
.pipe(filter(exists), take(1))
.subscribe(rps => {
.subscribe((rps: { [id: string]: RecoveredPackageDataEntry }) => {
this.hasRecoveredPackage = !isEmptyObject(rps)
})
}
@@ -233,16 +234,36 @@ export class ServerShowPage {
try {
const updateAvailable = await this.eosService.getEOS()
await loader.dismiss()
if (updateAvailable) {
this.updateEos()
} else {
this.presentAlertLatest()
}
} catch (e: any) {
await loader.dismiss()
this.errToast.present(e)
} finally {
loader.dismiss()
}
}
async presentAlertLatest() {
const alert = await this.alertCtrl.create({
header: 'Up to date!',
message: 'You are on the latest version of EmbassyOS.',
buttons: [
{
text: 'OK',
role: 'cancel',
cssClass: 'enter-click',
},
],
cssClass: 'alert-success-message',
})
alert.present()
}
settings: ServerSettings = {
Backups: [
{
@@ -332,8 +353,8 @@ export class ServerShowPage {
disabled: of(false),
},
{
title: 'Manually install a service',
description: `Install a service by drag n' drop`,
title: 'Manually Install A Service',
description: `Install a service by drag and drop`,
icon: 'push-outline',
action: () =>
this.navCtrl.navigateForward(['sideload'], {

View File

@@ -3,7 +3,7 @@
<ion-buttons slot="start">
<ion-back-button defaultHref="embassy"></ion-back-button>
</ion-buttons>
<ion-title>Manually install a service</ion-title>
<ion-title>Manually Install A Service</ion-title>
</ion-toolbar>
</ion-header>

View File

@@ -5,6 +5,7 @@ import {
PackageDataEntry,
PackageMainStatus,
PackageState,
ServerStatusInfo,
} from 'src/app/services/patch-db/data-model'
import {
Log,
@@ -18,6 +19,11 @@ import { BTC_ICON, LND_ICON, PROXY_ICON } from './api-icons'
import { MarketplacePkg } from '@start9labs/marketplace'
export module Mock {
export const ServerUpdated: ServerStatusInfo = {
'backing-up': false,
'update-progress': null,
updated: true,
}
export const MarketplaceEos: RR.GetMarketplaceEOSRes = {
version: '0.3.2',
headline: 'Our biggest release ever.',

View File

@@ -187,7 +187,6 @@ export class MockApiService extends ApiService {
value: initialProgress,
},
]
return this.withRevision(patch, 'updating')
}
@@ -860,6 +859,16 @@ export class MockApiService extends ApiService {
},
]
this.updateMock(patch4)
// set patch indicating update is complete
await pauseFor(100)
const patch6 = [
{
op: PatchOp.REPLACE,
path: '/server-info/status-info',
value: Mock.ServerUpdated,
},
]
this.updateMock(patch6)
}, 1000)
}

View File

@@ -444,7 +444,7 @@ export const mockPatchData: DataModel = {
'donation-url': null,
alerts: {
install: null,
uninstall: undefined,
uninstall: null,
restore:
'If this is a duplicate instance of the same LND node, you may loose your funds.',
start: 'Starting LND is good for your health.',

View File

@@ -4,6 +4,7 @@ import { MarketplaceEOS } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { Emver } from '@start9labs/shared'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { switchMap, take } from 'rxjs/operators'
@Injectable({
providedIn: 'root',

View File

@@ -12,13 +12,11 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
import {
ServerInfo,
UIData,
UIMarketplaceData,
} from 'src/app/services/patch-db/data-model'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import {
catchError,
distinctUntilChanged,
filter,
map,
shareReplay,
@@ -34,11 +32,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
private readonly altMarketplaceData$: Observable<
UIMarketplaceData | undefined
> = this.patch.watch$('ui').pipe(
map((ui: UIData) => ui.marketplace),
distinctUntilChanged(),
shareReplay({ bufferSize: 1, refCount: true }),
)
> = this.patch.watch$('ui', 'marketplace').pipe(shareReplay())
private readonly marketplace$ = this.altMarketplaceData$.pipe(
map(data => this.toMarketplace(data)),
@@ -46,7 +40,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
private readonly serverInfo$: Observable<ServerInfo> = this.patch
.watch$('server-info')
.pipe(take(1), shareReplay({ bufferSize: 1, refCount: true }))
.pipe(take(1), shareReplay())
private readonly categories$: Observable<string[]> = this.marketplace$.pipe(
switchMap(({ url }) =>
@@ -57,6 +51,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
),
),
map(({ categories }) => categories),
shareReplay(),
)
private readonly pkg$: Observable<MarketplacePkg[]> =
@@ -79,7 +74,7 @@ export class MarketplaceService extends AbstractMarketplaceService {
return of([])
}),
shareReplay({ bufferSize: 1, refCount: true }),
shareReplay(),
)
constructor(

View File

@@ -52,15 +52,17 @@ export interface ServerInfo {
'lan-address': Url
'tor-address': Url
'unread-notification-count': number
'status-info': {
'backing-up': boolean
updated: boolean
'update-progress': { size: number | null; downloaded: number } | null
}
'status-info': ServerStatusInfo
'eos-version-compat': string
'password-hash': string
}
export interface ServerStatusInfo {
'backing-up': boolean
updated: boolean
'update-progress': { size: number | null; downloaded: number } | null
}
export enum ServerStatus {
Running = 'running',
Updated = 'updated',

View File

@@ -60,6 +60,10 @@ $subheader-height: 48px;
}
}
.btn-128 {
min-width: 128px;
}
.subheader-padding {
--padding-top: #{$subheader-height} + 10px;
}

View File

@@ -5,10 +5,10 @@
},
"beta": {
"url": "https://beta-registry-0-3.start9labs.com",
"name": "Embassy Marketplace (Beta)"
"name": "Beta Marketplace"
},
"alpha": {
"url": "https://alpha-registry-0-3.start9labs.com",
"name": "Embassy Marketplace (Alpha)"
"name": "Alpha Marketplace"
}
}