mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
start9 marketplace config
shift not unshift move eos updates to embassy tab selected id sub roughly working keep name in sync in case of change delete commented code 64 img
This commit is contained in:
committed by
Aiden McClelland
parent
2d4ecd3096
commit
0c0cd9d0a0
@@ -18,7 +18,7 @@
|
|||||||
"index": "projects/ui/src/index.html",
|
"index": "projects/ui/src/index.html",
|
||||||
"main": "projects/ui/src/main.ts",
|
"main": "projects/ui/src/main.ts",
|
||||||
"polyfills": "projects/ui/src/polyfills.ts",
|
"polyfills": "projects/ui/src/polyfills.ts",
|
||||||
"tsConfig": "projects/ui/tsconfig.app.json",
|
"tsConfig": "projects/ui/tsconfig.json",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "scss",
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
"index": "projects/setup-wizard/src/index.html",
|
"index": "projects/setup-wizard/src/index.html",
|
||||||
"main": "projects/setup-wizard/src/main.ts",
|
"main": "projects/setup-wizard/src/main.ts",
|
||||||
"polyfills": "projects/setup-wizard/src/polyfills.ts",
|
"polyfills": "projects/setup-wizard/src/polyfills.ts",
|
||||||
"tsConfig": "projects/setup-wizard/tsconfig.app.json",
|
"tsConfig": "projects/setup-wizard/tsconfig.json",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "scss",
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
"index": "projects/diagnostic-ui/src/index.html",
|
"index": "projects/diagnostic-ui/src/index.html",
|
||||||
"main": "projects/diagnostic-ui/src/main.ts",
|
"main": "projects/diagnostic-ui/src/main.ts",
|
||||||
"polyfills": "projects/diagnostic-ui/src/polyfills.ts",
|
"polyfills": "projects/diagnostic-ui/src/polyfills.ts",
|
||||||
"tsConfig": "projects/diagnostic-ui/tsconfig.app.json",
|
"tsConfig": "projects/diagnostic-ui/tsconfig.json",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "scss",
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"mocks": {
|
"mocks": {
|
||||||
"maskAs": "tor",
|
"maskAs": "tor",
|
||||||
"skipStartupAlerts": true
|
"skipStartupAlerts": true
|
||||||
}
|
},
|
||||||
|
"eosMarketplaceURL": "https://beta-registry-0-3.start9labs.com "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"check": "npm run check:shared && npm run check:ui && npm run check:setup-wizard && npm run check:diagnostic-ui",
|
"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.lib.json --noEmit --skipLibCheck",
|
||||||
"check:diagnostic-ui": "tsc --project projects/diagnostic-ui/tsconfig.app.json --noEmit --skipLibCheck",
|
"check:diagnostic-ui": "tsc --project projects/diagnostic-ui/tsconfig.json --noEmit --skipLibCheck",
|
||||||
"check:setup-wizard": "tsc --project projects/setup-wizard/tsconfig.app.json --noEmit --skipLibCheck",
|
"check:setup-wizard": "tsc --project projects/setup-wizard/tsconfig.json --noEmit --skipLibCheck",
|
||||||
"check:ui": "tsc --project projects/ui/tsconfig.app.json --noEmit --skipLibCheck",
|
"check:ui": "tsc --project projects/ui/tsconfig.json --noEmit --skipLibCheck",
|
||||||
"build:deps": "cd ../patch-db/client && npm install && npm run build",
|
"build:deps": "cd ../patch-db/client && npm install && npm run build",
|
||||||
"build:diagnostic-ui": "ng run diagnostic-ui:build",
|
"build:diagnostic-ui": "ng run diagnostic-ui:build",
|
||||||
"build:setup-wizard": "ng run setup-wizard:build",
|
"build:setup-wizard": "ng run setup-wizard:build",
|
||||||
|
|||||||
@@ -16,5 +16,6 @@ export type WorkspaceConfig = {
|
|||||||
maskAs: 'tor' | 'lan'
|
maskAs: 'tor' | 'lan'
|
||||||
skipStartupAlerts: boolean
|
skipStartupAlerts: boolean
|
||||||
}
|
}
|
||||||
|
eosMarketplaceURL: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,11 +45,17 @@
|
|||||||
>
|
>
|
||||||
{{ page.title }}
|
{{ page.title }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
|
<ng-container *ngIf="page.url === '/embassy'">
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="eosService.updateAvailable$ | async"
|
||||||
|
color="success"
|
||||||
|
name="repeat"
|
||||||
|
></ion-icon>
|
||||||
|
</ng-container>
|
||||||
<ion-badge
|
<ion-badge
|
||||||
*ngIf="page.url === '/notifications' && unreadCount"
|
*ngIf="page.url === '/notifications' && unreadCount"
|
||||||
color="danger"
|
color="danger"
|
||||||
style="margin-right: 3%"
|
style="margin-right: 3%"
|
||||||
[class.selected-badge]="selectedIndex == i"
|
|
||||||
>{{ unreadCount }}</ion-badge
|
>{{ unreadCount }}</ion-badge
|
||||||
>
|
>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
@@ -117,6 +123,7 @@
|
|||||||
<ion-icon name="cloud-offline-outline"></ion-icon>
|
<ion-icon name="cloud-offline-outline"></ion-icon>
|
||||||
<ion-icon name="cloud-upload-outline"></ion-icon>
|
<ion-icon name="cloud-upload-outline"></ion-icon>
|
||||||
<ion-icon name="code-outline"></ion-icon>
|
<ion-icon name="code-outline"></ion-icon>
|
||||||
|
<ion-icon name="cog-outline"></ion-icon>
|
||||||
<ion-icon name="color-wand-outline"></ion-icon>
|
<ion-icon name="color-wand-outline"></ion-icon>
|
||||||
<ion-icon name="construct-outline"></ion-icon>
|
<ion-icon name="construct-outline"></ion-icon>
|
||||||
<ion-icon name="copy-outline"></ion-icon>
|
<ion-icon name="copy-outline"></ion-icon>
|
||||||
@@ -155,6 +162,7 @@
|
|||||||
<ion-icon name="reload"></ion-icon>
|
<ion-icon name="reload"></ion-icon>
|
||||||
<ion-icon name="remove"></ion-icon>
|
<ion-icon name="remove"></ion-icon>
|
||||||
<ion-icon name="remove-circle-outline"></ion-icon>
|
<ion-icon name="remove-circle-outline"></ion-icon>
|
||||||
|
<ion-icon name="repeat"></ion-icon>
|
||||||
<ion-icon name="save-outline"></ion-icon>
|
<ion-icon name="save-outline"></ion-icon>
|
||||||
<ion-icon name="shield-checkmark-outline"></ion-icon>
|
<ion-icon name="shield-checkmark-outline"></ion-icon>
|
||||||
<ion-icon name="storefront-outline"></ion-icon>
|
<ion-icon name="storefront-outline"></ion-icon>
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ import { Emver } from './services/emver.service'
|
|||||||
import { SplitPaneTracker } from './services/split-pane.service'
|
import { SplitPaneTracker } from './services/split-pane.service'
|
||||||
import { ToastButton } from '@ionic/core'
|
import { ToastButton } from '@ionic/core'
|
||||||
import { PatchDbService } from './services/patch-db/patch-db.service'
|
import { PatchDbService } from './services/patch-db/patch-db.service'
|
||||||
import { ServerStatus } from './services/patch-db/data-model'
|
import {
|
||||||
|
ServerStatus,
|
||||||
|
UIData,
|
||||||
|
UIMarketplaceData,
|
||||||
|
} from './services/patch-db/data-model'
|
||||||
import {
|
import {
|
||||||
ConnectionFailure,
|
ConnectionFailure,
|
||||||
ConnectionService,
|
ConnectionService,
|
||||||
@@ -30,6 +34,8 @@ import { debounce, isEmptyObject } from './util/misc.util'
|
|||||||
import { ErrorToastService } from './services/error-toast.service'
|
import { ErrorToastService } from './services/error-toast.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { LocalStorageService } from './services/local-storage.service'
|
import { LocalStorageService } from './services/local-storage.service'
|
||||||
|
import { EOSService } from './services/eos.service'
|
||||||
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -39,7 +45,7 @@ import { LocalStorageService } from './services/local-storage.service'
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
@HostListener('document:keydown.enter', ['$event'])
|
@HostListener('document:keydown.enter', ['$event'])
|
||||||
@debounce()
|
@debounce()
|
||||||
handleKeyboardEvent() {
|
handleKeyboardEvent () {
|
||||||
const elems = document.getElementsByClassName('enter-click')
|
const elems = document.getElementsByClassName('enter-click')
|
||||||
const elem = elems[elems.length - 1] as HTMLButtonElement
|
const elem = elems[elems.length - 1] as HTMLButtonElement
|
||||||
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
|
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
|
||||||
@@ -84,7 +90,7 @@ export class AppComponent {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
constructor(
|
constructor (
|
||||||
private readonly storage: Storage,
|
private readonly storage: Storage,
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly router: Router,
|
private readonly router: Router,
|
||||||
@@ -101,11 +107,12 @@ export class AppComponent {
|
|||||||
public readonly splitPane: SplitPaneTracker,
|
public readonly splitPane: SplitPaneTracker,
|
||||||
public readonly patch: PatchDbService,
|
public readonly patch: PatchDbService,
|
||||||
public readonly localStorageService: LocalStorageService,
|
public readonly localStorageService: LocalStorageService,
|
||||||
|
public readonly eosService: EOSService,
|
||||||
) {
|
) {
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init () {
|
||||||
await this.storage.create()
|
await this.storage.create()
|
||||||
await this.authService.init()
|
await this.authService.init()
|
||||||
await this.localStorageService.init()
|
await this.localStorageService.init()
|
||||||
@@ -140,7 +147,12 @@ export class AppComponent {
|
|||||||
filter(obj => !isEmptyObject(obj)),
|
filter(obj => !isEmptyObject(obj)),
|
||||||
take(1),
|
take(1),
|
||||||
)
|
)
|
||||||
.subscribe(_ => {
|
.subscribe(data => {
|
||||||
|
// check for updates to EOS
|
||||||
|
this.checkForEosUpdate(data.ui)
|
||||||
|
// seed EOS marketplace as default for services too
|
||||||
|
this.seedMarketplace(data.ui.marketplace)
|
||||||
|
|
||||||
this.subscriptions = this.subscriptions.concat([
|
this.subscriptions = this.subscriptions.concat([
|
||||||
// watch status to present toast for updated state
|
// watch status to present toast for updated state
|
||||||
this.watchStatus(),
|
this.watchStatus(),
|
||||||
@@ -172,7 +184,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToWebsite(): Promise<void> {
|
async goToWebsite (): Promise<void> {
|
||||||
let url: string
|
let url: string
|
||||||
if (this.config.isTor()) {
|
if (this.config.isTor()) {
|
||||||
url =
|
url =
|
||||||
@@ -183,7 +195,7 @@ export class AppComponent {
|
|||||||
window.open(url, '_blank', 'noreferrer')
|
window.open(url, '_blank', 'noreferrer')
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertLogout() {
|
async presentAlertLogout () {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Caution',
|
header: 'Caution',
|
||||||
message:
|
message:
|
||||||
@@ -206,13 +218,39 @@ export class AppComponent {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async checkForEosUpdate (ui: UIData): Promise<void> {
|
||||||
|
if (ui['auto-check-updates']) {
|
||||||
|
await this.eosService.getEOS()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async seedMarketplace(marketplace: UIMarketplaceData): Promise<void> {
|
||||||
|
if (
|
||||||
|
!marketplace ||
|
||||||
|
!marketplace['known-hosts'] ||
|
||||||
|
!marketplace['selected-id']
|
||||||
|
) {
|
||||||
|
const uuid = v4()
|
||||||
|
const value: UIMarketplaceData = {
|
||||||
|
'selected-id': uuid,
|
||||||
|
'known-hosts': {
|
||||||
|
[uuid]: {
|
||||||
|
url: this.config.eosMarketplaceUrl,
|
||||||
|
name: 'Start9 Embassy Marketplace',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await this.embassyApi.setDbValue({ pointer: '/marketplace', value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// should wipe cache independant of actual BE logout
|
// should wipe cache independant of actual BE logout
|
||||||
private async logout() {
|
private async logout () {
|
||||||
this.embassyApi.logout({})
|
this.embassyApi.logout({})
|
||||||
this.authService.setUnverified()
|
this.authService.setUnverified()
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchConnection(): Subscription {
|
private watchConnection (): Subscription {
|
||||||
return this.connectionService
|
return this.connectionService
|
||||||
.watchFailure$()
|
.watchFailure$()
|
||||||
.pipe(distinctUntilChanged(), debounceTime(500))
|
.pipe(distinctUntilChanged(), debounceTime(500))
|
||||||
@@ -245,7 +283,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchRouter(): Subscription {
|
private watchRouter (): Subscription {
|
||||||
return this.router.events
|
return this.router.events
|
||||||
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
|
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
|
||||||
.subscribe(e => {
|
.subscribe(e => {
|
||||||
@@ -256,7 +294,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchStatus(): Subscription {
|
private watchStatus (): Subscription {
|
||||||
return this.patch.watch$('server-info', 'status').subscribe(status => {
|
return this.patch.watch$('server-info', 'status').subscribe(status => {
|
||||||
if (status === ServerStatus.Updated && !this.updateToast) {
|
if (status === ServerStatus.Updated && !this.updateToast) {
|
||||||
this.presentToastUpdated()
|
this.presentToastUpdated()
|
||||||
@@ -264,7 +302,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchUpdateProgress(): Subscription {
|
private watchUpdateProgress (): Subscription {
|
||||||
return this.patch
|
return this.patch
|
||||||
.watch$('server-info', 'update-progress')
|
.watch$('server-info', 'update-progress')
|
||||||
.subscribe(progress => {
|
.subscribe(progress => {
|
||||||
@@ -272,7 +310,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchVersion(): Subscription {
|
private watchVersion (): Subscription {
|
||||||
return this.patch.watch$('server-info', 'version').subscribe(version => {
|
return this.patch.watch$('server-info', 'version').subscribe(version => {
|
||||||
if (this.emver.compare(this.config.version, version) !== 0) {
|
if (this.emver.compare(this.config.version, version) !== 0) {
|
||||||
this.presentAlertRefreshNeeded()
|
this.presentAlertRefreshNeeded()
|
||||||
@@ -280,7 +318,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchNotifications(): Subscription {
|
private watchNotifications (): Subscription {
|
||||||
let previous: number
|
let previous: number
|
||||||
return this.patch
|
return this.patch
|
||||||
.watch$('server-info', 'unread-notification-count')
|
.watch$('server-info', 'unread-notification-count')
|
||||||
@@ -292,7 +330,7 @@ export class AppComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentAlertRefreshNeeded() {
|
private async presentAlertRefreshNeeded () {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
backdropDismiss: false,
|
backdropDismiss: false,
|
||||||
header: 'Refresh Needed',
|
header: 'Refresh Needed',
|
||||||
@@ -311,7 +349,7 @@ export class AppComponent {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastUpdated() {
|
private async presentToastUpdated () {
|
||||||
if (this.updateToast) return
|
if (this.updateToast) return
|
||||||
|
|
||||||
this.updateToast = await this.toastCtrl.create({
|
this.updateToast = await this.toastCtrl.create({
|
||||||
@@ -341,7 +379,7 @@ export class AppComponent {
|
|||||||
await this.updateToast.present()
|
await this.updateToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastNotifications() {
|
private async presentToastNotifications () {
|
||||||
if (this.notificationToast) return
|
if (this.notificationToast) return
|
||||||
|
|
||||||
this.notificationToast = await this.toastCtrl.create({
|
this.notificationToast = await this.toastCtrl.create({
|
||||||
@@ -371,7 +409,7 @@ export class AppComponent {
|
|||||||
await this.notificationToast.present()
|
await this.notificationToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async presentToastOffline(
|
private async presentToastOffline (
|
||||||
message: string | IonicSafeString,
|
message: string | IonicSafeString,
|
||||||
link?: string,
|
link?: string,
|
||||||
) {
|
) {
|
||||||
@@ -412,7 +450,7 @@ export class AppComponent {
|
|||||||
await this.offlineToast.present()
|
await this.offlineToast.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restart(): Promise<void> {
|
private async restart (): Promise<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
spinner: 'lines',
|
spinner: 'lines',
|
||||||
message: 'Restarting...',
|
message: 'Restarting...',
|
||||||
@@ -429,7 +467,7 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
splitPaneVisible(e: any) {
|
splitPaneVisible (e: any) {
|
||||||
this.splitPane.sidebarOpen$.next(e.detail.visible)
|
this.splitPane.sidebarOpen$.next(e.detail.visible)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,14 @@
|
|||||||
|
|
||||||
<!-- not loading -->
|
<!-- not loading -->
|
||||||
<ng-template #data>
|
<ng-template #data>
|
||||||
<h1 style="font-family: 'Montserrat'; font-size: 42px; margin: 32px 0;" class="ion-text-center">Embassy Marketplace</h1>
|
<h1
|
||||||
|
style="font-family: 'Montserrat'; font-size: 42px; margin: 32px 0"
|
||||||
|
class="ion-text-center"
|
||||||
|
>
|
||||||
|
Embassy Marketplace
|
||||||
|
</h1>
|
||||||
|
|
||||||
<ion-grid style="padding-bottom: 32px;">
|
<ion-grid style="padding-bottom: 32px">
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col sizeSm="8" offset-sm="2">
|
<ion-col sizeSm="8" offset-sm="2">
|
||||||
<ion-toolbar color="transparent">
|
<ion-toolbar color="transparent">
|
||||||
@@ -36,12 +41,18 @@
|
|||||||
<!-- loading -->
|
<!-- loading -->
|
||||||
<ng-container *ngIf="loading; else pageLoaded">
|
<ng-container *ngIf="loading; else pageLoaded">
|
||||||
<div class="scrollable ion-text-center">
|
<div class="scrollable ion-text-center">
|
||||||
<ion-button *ngFor="let cat of ['', '', '', '', '', '', '']" fill="clear">
|
<ion-button
|
||||||
<ion-skeleton-text animated style="width: 80px; border-radius: 0;"></ion-skeleton-text>
|
*ngFor="let cat of ['', '', '', '', '', '', '']"
|
||||||
|
fill="clear"
|
||||||
|
>
|
||||||
|
<ion-skeleton-text
|
||||||
|
animated
|
||||||
|
style="width: 80px; border-radius: 0"
|
||||||
|
></ion-skeleton-text>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider" style="margin: 24px 0;"></div>
|
<div class="divider" style="margin: 24px 0"></div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- loaded -->
|
<!-- loaded -->
|
||||||
@@ -57,22 +68,39 @@
|
|||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider" style="margin: 24px;"></div>
|
<div class="divider" style="margin: 24px"></div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<!-- loading -->
|
<!-- loading -->
|
||||||
<ng-container *ngIf="loading; else pkgsLoaded">
|
<ng-container *ngIf="loading; else pkgsLoaded">
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col *ngFor="let pkg of ['', '', '', '']" sizeXs="12" sizeSm="12" sizeMd="6">
|
<ion-col
|
||||||
|
*ngFor="let pkg of ['', '', '', '']"
|
||||||
|
sizeXs="12"
|
||||||
|
sizeSm="12"
|
||||||
|
sizeMd="6"
|
||||||
|
>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-thumbnail slot="start">
|
<ion-thumbnail slot="start">
|
||||||
<ion-skeleton-text style="border-radius: 100%;" animated></ion-skeleton-text>
|
<ion-skeleton-text
|
||||||
|
style="border-radius: 100%"
|
||||||
|
animated
|
||||||
|
></ion-skeleton-text>
|
||||||
</ion-thumbnail>
|
</ion-thumbnail>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<ion-skeleton-text animated style="width: 150px; height: 18px; margin-bottom: 8px;"></ion-skeleton-text>
|
<ion-skeleton-text
|
||||||
<ion-skeleton-text animated style="width: 400px;"></ion-skeleton-text>
|
animated
|
||||||
<ion-skeleton-text animated style="width: 100px;"></ion-skeleton-text>
|
style="width: 150px; height: 18px; margin-bottom: 8px"
|
||||||
|
></ion-skeleton-text>
|
||||||
|
<ion-skeleton-text
|
||||||
|
animated
|
||||||
|
style="width: 400px"
|
||||||
|
></ion-skeleton-text>
|
||||||
|
<ion-skeleton-text
|
||||||
|
animated
|
||||||
|
style="width: 100px"
|
||||||
|
></ion-skeleton-text>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
@@ -85,39 +113,46 @@
|
|||||||
<div
|
<div
|
||||||
class="ion-padding"
|
class="ion-padding"
|
||||||
*ngIf="!pkgs.length && category ==='updates'"
|
*ngIf="!pkgs.length && category ==='updates'"
|
||||||
style="text-align: center;"
|
style="text-align: center"
|
||||||
>
|
>
|
||||||
<h1>All services are up to date!</h1>
|
<h1>All services are up to date!</h1>
|
||||||
</div>
|
</div>
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col *ngIf="marketplaceService.eosUpdateAvailable && category === 'featured'" sizeXs="12" sizeSm="12" sizeMd="6">
|
|
||||||
<ion-item button class="eos-item" (click)="updateEos()">
|
|
||||||
<ion-thumbnail slot="start">
|
|
||||||
<img src="assets/img/icon.png" />
|
|
||||||
</ion-thumbnail>
|
|
||||||
<ion-label>
|
|
||||||
<h3>Now Available...</h3>
|
|
||||||
<h2>Embassy OS {{ marketplaceService.eos.version }}</h2>
|
|
||||||
<p>{{ marketplaceService.eos.headline }}</p>
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-col>
|
|
||||||
<ion-col *ngFor="let pkg of pkgs" sizeXs="12" sizeSm="12" sizeMd="6">
|
<ion-col *ngFor="let pkg of pkgs" sizeXs="12" sizeSm="12" sizeMd="6">
|
||||||
<ion-item [routerLink]="['/marketplace', pkg.manifest.id]">
|
<ion-item [routerLink]="['/marketplace', pkg.manifest.id]">
|
||||||
<ion-thumbnail slot="start">
|
<ion-thumbnail slot="start">
|
||||||
<img [src]="'/marketplace' + pkg.icon" />
|
<img
|
||||||
|
[src]="sanitizer.bypassSecurityTrustResourceUrl('data:image/png;base64,' + pkg.icon)"
|
||||||
|
/>
|
||||||
</ion-thumbnail>
|
</ion-thumbnail>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2 style="font-family: 'Montserrat'; font-weight: bold;">{{ pkg.manifest.title }}</h2>
|
<h2 style="font-family: 'Montserrat'; font-weight: bold">
|
||||||
|
{{ pkg.manifest.title }}
|
||||||
|
</h2>
|
||||||
<h3>{{ pkg.manifest.description.short }}</h3>
|
<h3>{{ pkg.manifest.description.short }}</h3>
|
||||||
<ng-container *ngIf="localPkgs[pkg.manifest.id] as localPkg; else none">
|
<ng-container
|
||||||
|
*ngIf="localPkgs[pkg.manifest.id] as localPkg; else none"
|
||||||
|
>
|
||||||
<p *ngIf="localPkg.state === PackageState.Installed">
|
<p *ngIf="localPkg.state === PackageState.Installed">
|
||||||
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0" color="success">Installed</ion-text>
|
<ion-text
|
||||||
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1" color="warning">Update Available</ion-text>
|
*ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0"
|
||||||
|
color="success"
|
||||||
|
>Installed</ion-text
|
||||||
|
>
|
||||||
|
<ion-text
|
||||||
|
*ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1"
|
||||||
|
color="warning"
|
||||||
|
>Update Available</ion-text
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state">
|
<p
|
||||||
<ion-text color="primary" *ngIf="(localPkg['install-progress'] | installProgress) as progress">
|
*ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state"
|
||||||
|
>
|
||||||
|
<ion-text
|
||||||
|
color="primary"
|
||||||
|
*ngIf="(localPkg['install-progress'] | installProgress) as progress"
|
||||||
|
>
|
||||||
Installing
|
Installing
|
||||||
<span class="loading-dots"></span>{{ progress }}
|
<span class="loading-dots"></span>{{ progress }}
|
||||||
</ion-text>
|
</ion-text>
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { Component, ViewChild } from '@angular/core'
|
import { Component, ViewChild } from '@angular/core'
|
||||||
import { MarketplacePkg } from 'src/app/services/api/api.types'
|
import { MarketplacePkg } from 'src/app/services/api/api.types'
|
||||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
import { IonContent } from '@ionic/angular'
|
||||||
import { AlertController, IonContent, ModalController } from '@ionic/angular'
|
import {
|
||||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
PackageDataEntry,
|
||||||
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
PackageState,
|
||||||
|
} from 'src/app/services/patch-db/data-model'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||||
import { MarketplaceService } from '../marketplace.service'
|
import { MarketplaceService } from '../marketplace.service'
|
||||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
import Fuse from 'fuse.js/dist/fuse.min.js'
|
import Fuse from 'fuse.js/dist/fuse.min.js'
|
||||||
import { exists, isEmptyObject } from 'src/app/util/misc.util'
|
import { exists, isEmptyObject } from 'src/app/util/misc.util'
|
||||||
import { Router } from '@angular/router'
|
|
||||||
import { filter, first } from 'rxjs/operators'
|
import { filter, first } from 'rxjs/operators'
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser'
|
||||||
|
|
||||||
const defaultOps = {
|
const defaultOps = {
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
@@ -45,145 +46,106 @@ export class MarketplaceListPage {
|
|||||||
@ViewChild(IonContent) content: IonContent
|
@ViewChild(IonContent) content: IonContent
|
||||||
|
|
||||||
pkgs: MarketplacePkg[] = []
|
pkgs: MarketplacePkg[] = []
|
||||||
hasRecoveredPackage: boolean
|
|
||||||
categories: string[]
|
categories: string[]
|
||||||
localPkgs: { [id: string]: PackageDataEntry } = { }
|
localPkgs: { [id: string]: PackageDataEntry } = {}
|
||||||
category = 'featured'
|
category = 'featured'
|
||||||
query: string
|
query: string
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
subs: Subscription[] = []
|
subs: Subscription[] = []
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly modalCtrl: ModalController,
|
|
||||||
private readonly errToast: ErrorToastService,
|
private readonly errToast: ErrorToastService,
|
||||||
private readonly wizardBaker: WizardBaker,
|
|
||||||
private readonly alertCtrl: AlertController,
|
|
||||||
private readonly router: Router,
|
|
||||||
public readonly patch: PatchDbService,
|
public readonly patch: PatchDbService,
|
||||||
public readonly marketplaceService: MarketplaceService,
|
public readonly marketplaceService: MarketplaceService,
|
||||||
) { }
|
public readonly sanitizer: DomSanitizer,
|
||||||
|
) {}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit() {
|
||||||
this.subs = [
|
this.subs = [
|
||||||
this.patch.watch$('package-data')
|
this.patch
|
||||||
.pipe(
|
.watch$('package-data')
|
||||||
filter((data) => exists(data) && !isEmptyObject(data)),
|
.pipe(filter(data => exists(data) && !isEmptyObject(data)))
|
||||||
).subscribe(pkgs => {
|
.subscribe(pkgs => {
|
||||||
this.localPkgs = pkgs
|
this.localPkgs = pkgs
|
||||||
Object.values(this.localPkgs).forEach(pkg => {
|
Object.values(this.localPkgs).forEach(pkg => {
|
||||||
pkg['install-progress'] = { ...pkg['install-progress'] }
|
pkg['install-progress'] = { ...pkg['install-progress'] }
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
this.patch.watch$('recovered-packages').subscribe(rps => {
|
|
||||||
this.hasRecoveredPackage = !isEmptyObject(rps)
|
|
||||||
}),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
this.patch.watch$('server-info')
|
this.patch
|
||||||
.pipe(
|
.watch$('server-info')
|
||||||
filter((data) => exists(data) && !isEmptyObject(data)),
|
.pipe(
|
||||||
first(),
|
filter(data => exists(data) && !isEmptyObject(data)),
|
||||||
).subscribe(async _ => {
|
first(),
|
||||||
try {
|
)
|
||||||
if (!this.marketplaceService.pkgs.length) {
|
.subscribe(async _ => {
|
||||||
await this.marketplaceService.load()
|
try {
|
||||||
|
if (!this.marketplaceService.pkgs.length) {
|
||||||
|
await this.marketplaceService.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// category should start as first item in array
|
||||||
|
// remove here then add at beginning
|
||||||
|
const filterdCategories =
|
||||||
|
this.marketplaceService.data.categories.filter(
|
||||||
|
cat => this.category !== cat,
|
||||||
|
)
|
||||||
|
this.categories = [this.category, 'updates']
|
||||||
|
.concat(filterdCategories)
|
||||||
|
.concat(['all'])
|
||||||
|
|
||||||
|
this.filterPkgs()
|
||||||
|
} catch (e) {
|
||||||
|
this.errToast.present(e)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
})
|
||||||
// category should start as first item in array
|
|
||||||
// remove here then add at beginning
|
|
||||||
const filterdCategories = this.marketplaceService.data.categories.filter(cat => this.category !== cat)
|
|
||||||
this.categories = [this.category, 'updates'].concat(filterdCategories).concat(['all'])
|
|
||||||
|
|
||||||
this.filterPkgs()
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
this.errToast.present(e)
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit () {
|
ngAfterViewInit() {
|
||||||
this.content.scrollToPoint(undefined, 1)
|
this.content.scrollToPoint(undefined, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy() {
|
||||||
this.subs.forEach(sub => sub.unsubscribe())
|
this.subs.forEach(sub => sub.unsubscribe())
|
||||||
}
|
}
|
||||||
|
|
||||||
async search (): Promise<void> {
|
search(): void {
|
||||||
if (this.query) {
|
if (this.query) {
|
||||||
this.category = undefined
|
this.category = undefined
|
||||||
}
|
}
|
||||||
await this.filterPkgs()
|
this.filterPkgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
async switchCategory (category: string): Promise<void> {
|
switchCategory(category: string): void {
|
||||||
this.category = category
|
this.category = category
|
||||||
this.query = undefined
|
this.query = undefined
|
||||||
this.filterPkgs()
|
this.filterPkgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEos (): Promise<void> {
|
private filterPkgs(): void {
|
||||||
if (this.hasRecoveredPackage) {
|
|
||||||
const alert = await this.alertCtrl.create({
|
|
||||||
header: 'Cannot Update',
|
|
||||||
message: 'You cannot update EmbassyOS when you have unresolved recovered services.',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'OK',
|
|
||||||
role: 'cancel',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Resolve',
|
|
||||||
handler: () => {
|
|
||||||
this.router.navigate(['/services/list'], { replaceUrl: true })
|
|
||||||
},
|
|
||||||
cssClass: 'enter-click',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
await alert.present()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { version, headline, 'release-notes': releaseNotes } = this.marketplaceService.eos
|
|
||||||
|
|
||||||
await wizardModal(
|
|
||||||
this.modalCtrl,
|
|
||||||
this.wizardBaker.updateOS({
|
|
||||||
version,
|
|
||||||
headline,
|
|
||||||
releaseNotes,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async filterPkgs (): Promise<void> {
|
|
||||||
if (this.category === 'updates') {
|
if (this.category === 'updates') {
|
||||||
this.pkgs = this.marketplaceService.pkgs.filter(pkg => {
|
this.pkgs = this.marketplaceService.pkgs.filter(pkg => {
|
||||||
const { id, version } = pkg.manifest
|
const { id, version } = pkg.manifest
|
||||||
return this.localPkgs[id] && version !== this.localPkgs[id].manifest.version
|
return (
|
||||||
|
this.localPkgs[id] && version !== this.localPkgs[id].manifest.version
|
||||||
|
)
|
||||||
})
|
})
|
||||||
} else if (this.query) {
|
} else if (this.query) {
|
||||||
const fuse = new Fuse(this.marketplaceService.pkgs, defaultOps)
|
const fuse = new Fuse(this.marketplaceService.pkgs, defaultOps)
|
||||||
this.pkgs = fuse.search(this.query).map(p => p.item)
|
this.pkgs = fuse.search(this.query).map(p => p.item)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const pkgsToSort = this.marketplaceService.pkgs.filter(p => {
|
const pkgsToSort = this.marketplaceService.pkgs.filter(p => {
|
||||||
return this.category === 'all' || p.categories.includes(this.category)
|
return this.category === 'all' || p.categories.includes(this.category)
|
||||||
})
|
})
|
||||||
|
|
||||||
const opts = {
|
|
||||||
...defaultOps,
|
|
||||||
threshold: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
const fuse = new Fuse(pkgsToSort, { ...defaultOps, threshold: 1 })
|
const fuse = new Fuse(pkgsToSort, { ...defaultOps, threshold: 1 })
|
||||||
this.pkgs = fuse.search(this.category !== 'all' ? this.category || '' : 'bit').map(p => p.item)
|
this.pkgs = fuse
|
||||||
|
.search(this.category !== 'all' ? this.category || '' : 'bit')
|
||||||
|
.map(p => p.item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,24 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
|
<text-spinner
|
||||||
<text-spinner *ngIf="loading; else loaded" text="Loading Package"></text-spinner>
|
*ngIf="loading; else loaded"
|
||||||
|
text="Loading Package"
|
||||||
|
></text-spinner>
|
||||||
|
|
||||||
<ng-template #loaded>
|
<ng-template #loaded>
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col sizeXs="12" sizeSm="12" sizeMd="9" sizeLg="9" sizeXl="9">
|
<ion-col sizeXs="12" sizeSm="12" sizeMd="9" sizeLg="9" sizeXl="9">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<img [src]="'/marketplace' + pkg.icon" />
|
<img
|
||||||
|
[src]="sanitizer.bypassSecurityTrustResourceUrl('data:image/png;base64,' + pkg.icon)"
|
||||||
|
/>
|
||||||
<div class="header-text">
|
<div class="header-text">
|
||||||
<h1 class="header-title">{{ pkg.manifest.title }}</h1>
|
<h1 class="header-title">{{ pkg.manifest.title }}</h1>
|
||||||
<p class="header-version">{{ pkg.manifest.version | displayEmver }}</p>
|
<p class="header-version">
|
||||||
|
{{ pkg.manifest.version | displayEmver }}
|
||||||
|
</p>
|
||||||
<div class="header-status">
|
<div class="header-status">
|
||||||
<!-- no localPkg -->
|
<!-- no localPkg -->
|
||||||
<p *ngIf="!localPkg; else local">Not Installed</p>
|
<p *ngIf="!localPkg; else local">Not Installed</p>
|
||||||
@@ -27,12 +33,25 @@
|
|||||||
<ng-template #local>
|
<ng-template #local>
|
||||||
<!-- installed -->
|
<!-- installed -->
|
||||||
<p *ngIf="localPkg.state === PackageState.Installed">
|
<p *ngIf="localPkg.state === PackageState.Installed">
|
||||||
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0" color="success">Installed</ion-text>
|
<ion-text
|
||||||
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1" color="warning">Update Available</ion-text>
|
*ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0"
|
||||||
|
color="success"
|
||||||
|
>Installed</ion-text
|
||||||
|
>
|
||||||
|
<ion-text
|
||||||
|
*ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1"
|
||||||
|
color="warning"
|
||||||
|
>Update Available</ion-text
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<!-- installing, updating -->
|
<!-- installing, updating -->
|
||||||
<p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state">
|
<p
|
||||||
<ion-text color="primary" *ngIf="(localPkg['install-progress'] | installProgress) as progress">
|
*ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state"
|
||||||
|
>
|
||||||
|
<ion-text
|
||||||
|
color="primary"
|
||||||
|
*ngIf="(localPkg['install-progress'] | installProgress) as progress"
|
||||||
|
>
|
||||||
Installing
|
Installing
|
||||||
<span class="loading-dots"></span>{{ progress }}
|
<span class="loading-dots"></span>{{ progress }}
|
||||||
</ion-text>
|
</ion-text>
|
||||||
@@ -49,7 +68,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
<ion-col sizeXl="3" sizeLg="3" sizeMd="3" sizeSm="12" sizeXs="12" class="ion-align-self-center">
|
<ion-col
|
||||||
|
sizeXl="3"
|
||||||
|
sizeLg="3"
|
||||||
|
sizeMd="3"
|
||||||
|
sizeSm="12"
|
||||||
|
sizeXs="12"
|
||||||
|
class="ion-align-self-center"
|
||||||
|
>
|
||||||
<!-- no localPkg -->
|
<!-- no localPkg -->
|
||||||
<ion-button *ngIf="!localPkg" expand="block" (click)="tryInstall()">
|
<ion-button *ngIf="!localPkg" expand="block" (click)="tryInstall()">
|
||||||
Install
|
Install
|
||||||
@@ -58,10 +84,19 @@
|
|||||||
<ng-container *ngIf="localPkg">
|
<ng-container *ngIf="localPkg">
|
||||||
<!-- not installing, updating, or removing -->
|
<!-- not installing, updating, or removing -->
|
||||||
<ng-container *ngIf="localPkg.state === PackageState.Installed">
|
<ng-container *ngIf="localPkg.state === PackageState.Installed">
|
||||||
<ion-button *ngIf="(localPkg.manifest.version | compareEmver : pkg.manifest.version) === -1" expand="block" (click)="presentModal('update')">
|
<ion-button
|
||||||
|
*ngIf="(localPkg.manifest.version | compareEmver : pkg.manifest.version) === -1"
|
||||||
|
expand="block"
|
||||||
|
(click)="presentModal('update')"
|
||||||
|
>
|
||||||
Update
|
Update
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<ion-button *ngIf="(localPkg.manifest.version | compareEmver : pkg.manifest.version) === 1" expand="block" color="warning" (click)="presentModal('downgrade')">
|
<ion-button
|
||||||
|
*ngIf="(localPkg.manifest.version | compareEmver : pkg.manifest.version) === 1"
|
||||||
|
expand="block"
|
||||||
|
color="warning"
|
||||||
|
(click)="presentModal('downgrade')"
|
||||||
|
>
|
||||||
Downgrade
|
Downgrade
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -69,8 +104,20 @@
|
|||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
<ion-row *ngIf="localPkg">
|
<ion-row *ngIf="localPkg">
|
||||||
<ion-col sizeXl="3" sizeLg="3" sizeMd="3" sizeSm="12" sizeXs="12" class="ion-align-self-center">
|
<ion-col
|
||||||
<ion-button expand="block" fill="outline" color="primary" [routerLink]="['/services', pkg.manifest.id]">
|
sizeXl="3"
|
||||||
|
sizeLg="3"
|
||||||
|
sizeMd="3"
|
||||||
|
sizeSm="12"
|
||||||
|
sizeXs="12"
|
||||||
|
class="ion-align-self-center"
|
||||||
|
>
|
||||||
|
<ion-button
|
||||||
|
expand="block"
|
||||||
|
fill="outline"
|
||||||
|
color="primary"
|
||||||
|
[routerLink]="['/services', pkg.manifest.id]"
|
||||||
|
>
|
||||||
View Service
|
View Service
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
@@ -80,16 +127,30 @@
|
|||||||
<!-- auto-config -->
|
<!-- auto-config -->
|
||||||
<ion-item lines="none" *ngIf="dependentInfo" class="rec-item">
|
<ion-item lines="none" *ngIf="dependentInfo" class="rec-item">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2 style="display: flex; align-items: center;">
|
<h2 style="display: flex; align-items: center">
|
||||||
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: 18px;">{{ pkg.manifest.title }}</ion-text>
|
<ion-text
|
||||||
|
style="margin: 5px; font-family: 'Montserrat'; font-size: 18px"
|
||||||
|
>{{ pkg.manifest.title }}</ion-text
|
||||||
|
>
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
<ion-text color="dark">
|
<ion-text color="dark">
|
||||||
{{ dependentInfo.title }} requires an install of {{ pkg.manifest.title }} satisfying {{ dependentInfo.version }}.
|
{{ dependentInfo.title }} requires an install of {{
|
||||||
|
pkg.manifest.title }} satisfying {{ dependentInfo.version }}.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<span *ngIf="pkg.manifest.version | satisfiesEmver: dependentInfo.version" class="recommendation-text">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is compatible.</span>
|
<span
|
||||||
<span *ngIf="!(pkg.manifest.version | satisfiesEmver: dependentInfo.version)" class="recommendation-text recommendation-error">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is NOT compatible.</span>
|
*ngIf="pkg.manifest.version | satisfiesEmver: dependentInfo.version"
|
||||||
|
class="recommendation-text"
|
||||||
|
>{{ pkg.manifest.title }} version {{ pkg.manifest.version |
|
||||||
|
displayEmver }} is compatible.</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
*ngIf="!(pkg.manifest.version | satisfiesEmver: dependentInfo.version)"
|
||||||
|
class="recommendation-text recommendation-error"
|
||||||
|
>{{ pkg.manifest.title }} version {{ pkg.manifest.version |
|
||||||
|
displayEmver }} is NOT compatible.</span
|
||||||
|
>
|
||||||
</ion-text>
|
</ion-text>
|
||||||
</p>
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
@@ -99,47 +160,71 @@
|
|||||||
<!-- release notes -->
|
<!-- release notes -->
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
New in {{ pkg.manifest.version | displayEmver }}
|
New in {{ pkg.manifest.version | displayEmver }}
|
||||||
<ion-button [routerLink]="['notes']" style="position: absolute; right: 10px;" fill="clear" color="dark">
|
<ion-button
|
||||||
|
[routerLink]="['notes']"
|
||||||
|
style="position: absolute; right: 10px"
|
||||||
|
fill="clear"
|
||||||
|
color="dark"
|
||||||
|
>
|
||||||
All Release Notes
|
All Release Notes
|
||||||
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
|
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<ion-item lines="none" color="transparent">
|
<ion-item lines="none" color="transparent">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<div id='release-notes' [innerHTML]="pkg.manifest['release-notes'] | markdown"></div>
|
<div
|
||||||
|
id="release-notes"
|
||||||
|
[innerHTML]="pkg.manifest['release-notes'] | markdown"
|
||||||
|
></div>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<!-- description -->
|
<!-- description -->
|
||||||
<ion-item-divider>Description</ion-item-divider>
|
<ion-item-divider>Description</ion-item-divider>
|
||||||
<ion-item lines="none" color="transparent">
|
<ion-item lines="none" color="transparent">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<div id="release-notes" class="release-notes">{{ pkg.manifest.description.long }}</div>
|
<div id="release-notes" class="release-notes">
|
||||||
|
{{ pkg.manifest.description.long }}
|
||||||
|
</div>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<!-- dependencies -->
|
<!-- dependencies -->
|
||||||
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
|
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
|
||||||
<ion-item-divider>Dependencies</ion-item-divider>
|
<ion-item-divider>Dependencies</ion-item-divider>
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row>
|
<ion-row>
|
||||||
<ion-col *ngFor="let dep of pkg.manifest.dependencies | keyvalue" sizeSm="12" sizeMd="6">
|
<ion-col
|
||||||
<ion-item [routerLink]="['/marketplace', dep.key]">
|
*ngFor="let dep of pkg.manifest.dependencies | keyvalue"
|
||||||
<ion-thumbnail slot="start">
|
sizeSm="12"
|
||||||
<img [src]="'/marketplace' + pkg['dependency-metadata'][dep.key].icon" />
|
sizeMd="6"
|
||||||
</ion-thumbnail>
|
>
|
||||||
<ion-label>
|
<ion-item [routerLink]="['/marketplace', dep.key]">
|
||||||
<h2>
|
<ion-thumbnail slot="start">
|
||||||
{{ pkg['dependency-metadata'][dep.key].title }}
|
<img
|
||||||
<span *ngIf="dep.value.requirement.type === 'required'"> (required)</span>
|
[src]="'/marketplace' + pkg['dependency-metadata'][dep.key].icon"
|
||||||
<span *ngIf="dep.value.requirement.type === 'opt-out'"> (required by default)</span>
|
/>
|
||||||
<span *ngIf="dep.value.requirement.type === 'opt-in'"> (optional)</span>
|
</ion-thumbnail>
|
||||||
</h2>
|
<ion-label>
|
||||||
<p style="font-size: small">{{ dep.value.version | displayEmver }}</p>
|
<h2>
|
||||||
<p>{{ dep.value.description }}</p>
|
{{ pkg['dependency-metadata'][dep.key].title }}
|
||||||
</ion-label>
|
<span *ngIf="dep.value.requirement.type === 'required'">
|
||||||
</ion-item>
|
(required)</span
|
||||||
</ion-col>
|
>
|
||||||
</ion-row>
|
<span *ngIf="dep.value.requirement.type === 'opt-out'">
|
||||||
</ion-grid>
|
(required by default)</span
|
||||||
|
>
|
||||||
|
<span *ngIf="dep.value.requirement.type === 'opt-in'">
|
||||||
|
(optional)</span
|
||||||
|
>
|
||||||
|
</h2>
|
||||||
|
<p style="font-size: small">
|
||||||
|
{{ dep.value.version | displayEmver }}
|
||||||
|
</p>
|
||||||
|
<p>{{ dep.value.description }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
@@ -156,14 +241,22 @@
|
|||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button detail="false" (click)="presentModalMd('license')">
|
<ion-item
|
||||||
|
button
|
||||||
|
detail="false"
|
||||||
|
(click)="presentModalMd('license')"
|
||||||
|
>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>License</h2>
|
<h2>License</h2>
|
||||||
<p>{{ pkg.manifest.license }}</p>
|
<p>{{ pkg.manifest.license }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
<ion-icon slot="end" name="chevron-forward-outline"></ion-icon>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button detail="false" (click)="presentModalMd('instructions')">
|
<ion-item
|
||||||
|
button
|
||||||
|
detail="false"
|
||||||
|
(click)="presentModalMd('instructions')"
|
||||||
|
>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>Instructions</h2>
|
<h2>Instructions</h2>
|
||||||
<p>Click to view instructions</p>
|
<p>Click to view instructions</p>
|
||||||
@@ -174,21 +267,36 @@
|
|||||||
</ion-col>
|
</ion-col>
|
||||||
<ion-col sizeSm="12" sizeMd="6">
|
<ion-col sizeSm="12" sizeMd="6">
|
||||||
<ion-item-group>
|
<ion-item-group>
|
||||||
<ion-item [href]="pkg.manifest['upstream-repo']" target="_blank" rel="noreferrer" detail="false">
|
<ion-item
|
||||||
|
[href]="pkg.manifest['upstream-repo']"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
detail="false"
|
||||||
|
>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>Source Repository</h2>
|
<h2>Source Repository</h2>
|
||||||
<p>{{ pkg.manifest['upstream-repo'] }}</p>
|
<p>{{ pkg.manifest['upstream-repo'] }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item [href]="pkg.manifest['wrapper-repo']" target="_blank" rel="noreferrer" detail="false">
|
<ion-item
|
||||||
|
[href]="pkg.manifest['wrapper-repo']"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
detail="false"
|
||||||
|
>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>Wrapper Repository</h2>
|
<h2>Wrapper Repository</h2>
|
||||||
<p>{{ pkg.manifest['wrapper-repo'] }}</p>
|
<p>{{ pkg.manifest['wrapper-repo'] }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item [href]="pkg.manifest['support-site']" target="_blank" rel="noreferrer" detail="false">
|
<ion-item
|
||||||
|
[href]="pkg.manifest['support-site']"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
detail="false"
|
||||||
|
>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>Support Site</h2>
|
<h2>Support Site</h2>
|
||||||
<p>{{ pkg.manifest['support-site'] }}</p>
|
<p>{{ pkg.manifest['support-site'] }}</p>
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { Component, ViewChild } from '@angular/core'
|
import { Component, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { AlertController, IonContent, LoadingController, ModalController, NavController } from '@ionic/angular'
|
import {
|
||||||
|
AlertController,
|
||||||
|
IonContent,
|
||||||
|
LoadingController,
|
||||||
|
ModalController,
|
||||||
|
NavController,
|
||||||
|
} from '@ionic/angular'
|
||||||
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||||
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||||
import { Emver } from 'src/app/services/emver.service'
|
import { Emver } from 'src/app/services/emver.service'
|
||||||
@@ -8,12 +14,16 @@ import { displayEmver } from 'src/app/pipes/emver.pipe'
|
|||||||
import { DependentInfo, pauseFor } from 'src/app/util/misc.util'
|
import { DependentInfo, pauseFor } from 'src/app/util/misc.util'
|
||||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||||
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
|
import {
|
||||||
|
PackageDataEntry,
|
||||||
|
PackageState,
|
||||||
|
} from 'src/app/services/patch-db/data-model'
|
||||||
import { MarketplaceService } from '../marketplace.service'
|
import { MarketplaceService } from '../marketplace.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
|
import { MarkdownPage } from 'src/app/modals/markdown/markdown.page'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { MarketplacePkg } from 'src/app/services/api/api.types'
|
import { MarketplacePkg } from 'src/app/services/api/api.types'
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'marketplace-show',
|
selector: 'marketplace-show',
|
||||||
@@ -30,7 +40,7 @@ export class MarketplaceShowPage {
|
|||||||
dependentInfo: DependentInfo
|
dependentInfo: DependentInfo
|
||||||
subs: Subscription[] = []
|
subs: Subscription[] = []
|
||||||
|
|
||||||
constructor (
|
constructor(
|
||||||
private readonly route: ActivatedRoute,
|
private readonly route: ActivatedRoute,
|
||||||
private readonly alertCtrl: AlertController,
|
private readonly alertCtrl: AlertController,
|
||||||
private readonly modalCtrl: ModalController,
|
private readonly modalCtrl: ModalController,
|
||||||
@@ -42,18 +52,21 @@ export class MarketplaceShowPage {
|
|||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
private readonly embassyApi: ApiService,
|
private readonly embassyApi: ApiService,
|
||||||
private readonly marketplaceService: MarketplaceService,
|
private readonly marketplaceService: MarketplaceService,
|
||||||
) { }
|
public readonly sanitizer: DomSanitizer,
|
||||||
|
) {}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit() {
|
||||||
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
|
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
|
||||||
this.dependentInfo = history.state && history.state.dependentInfo as DependentInfo
|
this.dependentInfo =
|
||||||
|
history.state && (history.state.dependentInfo as DependentInfo)
|
||||||
|
|
||||||
this.subs = [
|
this.subs = [
|
||||||
this.patch.watch$('package-data', this.pkgId)
|
this.patch.watch$('package-data', this.pkgId).subscribe(pkg => {
|
||||||
.subscribe(pkg => {
|
|
||||||
if (!pkg) return
|
if (!pkg) return
|
||||||
this.localPkg = pkg
|
this.localPkg = pkg
|
||||||
this.localPkg['install-progress'] = { ...this.localPkg['install-progress'] }
|
this.localPkg['install-progress'] = {
|
||||||
|
...this.localPkg['install-progress'],
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -61,7 +74,9 @@ export class MarketplaceShowPage {
|
|||||||
if (!this.marketplaceService.pkgs.length) {
|
if (!this.marketplaceService.pkgs.length) {
|
||||||
await this.marketplaceService.load()
|
await this.marketplaceService.load()
|
||||||
}
|
}
|
||||||
this.pkg = this.marketplaceService.pkgs.find(pkg => pkg.manifest.id === this.pkgId)
|
this.pkg = this.marketplaceService.pkgs.find(
|
||||||
|
pkg => pkg.manifest.id === this.pkgId,
|
||||||
|
)
|
||||||
if (!this.pkg) {
|
if (!this.pkg) {
|
||||||
throw new Error(`Service with ID "${this.pkgId}" not found.`)
|
throw new Error(`Service with ID "${this.pkgId}" not found.`)
|
||||||
}
|
}
|
||||||
@@ -72,31 +87,34 @@ export class MarketplaceShowPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit () {
|
ngAfterViewInit() {
|
||||||
this.content.scrollToPoint(undefined, 1)
|
this.content.scrollToPoint(undefined, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy() {
|
||||||
this.subs.forEach(sub => sub.unsubscribe())
|
this.subs.forEach(sub => sub.unsubscribe())
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertVersions () {
|
async presentAlertVersions() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Versions',
|
header: 'Versions',
|
||||||
inputs: this.pkg.versions.sort((a, b) => -1 * this.emver.compare(a, b)).map(v => {
|
inputs: this.pkg.versions
|
||||||
return {
|
.sort((a, b) => -1 * this.emver.compare(a, b))
|
||||||
name: v, // for CSS
|
.map(v => {
|
||||||
type: 'radio',
|
return {
|
||||||
label: displayEmver(v), // appearance on screen
|
name: v, // for CSS
|
||||||
value: v, // literal SEM version value
|
type: 'radio',
|
||||||
checked: this.pkg.manifest.version === v,
|
label: displayEmver(v), // appearance on screen
|
||||||
}
|
value: v, // literal SEM version value
|
||||||
}),
|
checked: this.pkg.manifest.version === v,
|
||||||
|
}
|
||||||
|
}),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
role: 'cancel',
|
role: 'cancel',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
text: 'Ok',
|
text: 'Ok',
|
||||||
handler: (version: string) => {
|
handler: (version: string) => {
|
||||||
this.getPkg(version)
|
this.getPkg(version)
|
||||||
@@ -109,7 +127,7 @@ export class MarketplaceShowPage {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentModalMd (title: string) {
|
async presentModalMd(title: string) {
|
||||||
const modal = await this.modalCtrl.create({
|
const modal = await this.modalCtrl.create({
|
||||||
componentProps: {
|
componentProps: {
|
||||||
title,
|
title,
|
||||||
@@ -121,7 +139,7 @@ export class MarketplaceShowPage {
|
|||||||
await modal.present()
|
await modal.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryInstall () {
|
async tryInstall() {
|
||||||
const { id, title, version, alerts } = this.pkg.manifest
|
const { id, title, version, alerts } = this.pkg.manifest
|
||||||
|
|
||||||
if (!alerts.install) {
|
if (!alerts.install) {
|
||||||
@@ -148,7 +166,7 @@ export class MarketplaceShowPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentModal (action: 'update' | 'downgrade') {
|
async presentModal(action: 'update' | 'downgrade') {
|
||||||
const { id, title, version, dependencies, alerts } = this.pkg.manifest
|
const { id, title, version, dependencies, alerts } = this.pkg.manifest
|
||||||
const value = {
|
const value = {
|
||||||
id,
|
id,
|
||||||
@@ -160,9 +178,9 @@ export class MarketplaceShowPage {
|
|||||||
|
|
||||||
const { cancelled } = await wizardModal(
|
const { cancelled } = await wizardModal(
|
||||||
this.modalCtrl,
|
this.modalCtrl,
|
||||||
action === 'update' ?
|
action === 'update'
|
||||||
this.wizardBaker.update(value) :
|
? this.wizardBaker.update(value)
|
||||||
this.wizardBaker.downgrade(value),
|
: this.wizardBaker.downgrade(value),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
@@ -170,7 +188,7 @@ export class MarketplaceShowPage {
|
|||||||
this.navCtrl.back()
|
this.navCtrl.back()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPkg (version?: string): Promise<void> {
|
private async getPkg(version?: string): Promise<void> {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
this.pkg = await this.marketplaceService.getPkg(this.pkgId, version)
|
this.pkg = await this.marketplaceService.getPkg(this.pkgId, version)
|
||||||
@@ -182,7 +200,7 @@ export class MarketplaceShowPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async install (id: string, version?: string): Promise<void> {
|
private async install(id: string, version?: string): Promise<void> {
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
spinner: 'lines',
|
spinner: 'lines',
|
||||||
message: 'Beginning Installation',
|
message: 'Beginning Installation',
|
||||||
@@ -191,7 +209,10 @@ export class MarketplaceShowPage {
|
|||||||
loader.present()
|
loader.present()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.embassyApi.installPackage({ id, 'version-spec': version ? `=${version}` : undefined })
|
await this.embassyApi.installPackage({
|
||||||
|
id,
|
||||||
|
'version-spec': version ? `=${version}` : undefined,
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errToast.present(e)
|
this.errToast.present(e)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import {
|
import { MarketplaceData, MarketplacePkg } from 'src/app/services/api/api.types'
|
||||||
MarketplaceData,
|
|
||||||
MarketplaceEOS,
|
|
||||||
MarketplacePkg,
|
|
||||||
} from 'src/app/services/api/api.types'
|
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { Emver } from 'src/app/services/emver.service'
|
import { Emver } from 'src/app/services/emver.service'
|
||||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||||
@@ -14,7 +10,6 @@ import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
|||||||
})
|
})
|
||||||
export class MarketplaceService {
|
export class MarketplaceService {
|
||||||
data: MarketplaceData
|
data: MarketplaceData
|
||||||
eos: MarketplaceEOS
|
|
||||||
pkgs: MarketplacePkg[] = []
|
pkgs: MarketplacePkg[] = []
|
||||||
releaseNotes: {
|
releaseNotes: {
|
||||||
[id: string]: {
|
[id: string]: {
|
||||||
@@ -28,31 +23,24 @@ export class MarketplaceService {
|
|||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get eosUpdateAvailable() {
|
|
||||||
return (
|
|
||||||
this.emver.compare(
|
|
||||||
this.eos.version,
|
|
||||||
this.patch.data['server-info'].version,
|
|
||||||
) === 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async load(): Promise<void> {
|
async load(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const [data, eos, pkgs] = await Promise.all([
|
const [data, pkgs] = await Promise.all([
|
||||||
this.api.getMarketplaceData({}),
|
this.api.getMarketplaceData({}),
|
||||||
this.api.getEos({
|
|
||||||
'eos-version-compat':
|
|
||||||
this.patch.getData()['server-info']['eos-version-compat'],
|
|
||||||
}),
|
|
||||||
this.getPkgs(1, 100),
|
this.getPkgs(1, 100),
|
||||||
])
|
])
|
||||||
this.data = data
|
this.data = data
|
||||||
this.eos = eos
|
|
||||||
this.pkgs = pkgs
|
this.pkgs = pkgs
|
||||||
|
const { 'selected-id': selectedId, 'known-hosts': knownHosts } =
|
||||||
|
this.patch.getData().ui.marketplace
|
||||||
|
if (knownHosts[selectedId].name !== this.data.name) {
|
||||||
|
this.api.setDbValue({
|
||||||
|
pointer: `/marketplace/known-hosts/${selectedId}/name`,
|
||||||
|
value: this.data.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.data = undefined
|
this.data = undefined
|
||||||
this.eos = undefined
|
|
||||||
this.pkgs = []
|
this.pkgs = []
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@@ -61,10 +49,18 @@ export class MarketplaceService {
|
|||||||
async getUpdates(localPkgs: {
|
async getUpdates(localPkgs: {
|
||||||
[id: string]: PackageDataEntry
|
[id: string]: PackageDataEntry
|
||||||
}): Promise<MarketplacePkg[]> {
|
}): Promise<MarketplacePkg[]> {
|
||||||
const idAndCurrentVersions = Object.keys(localPkgs).map(key => ({
|
const idAndCurrentVersions = Object.keys(localPkgs)
|
||||||
id: key,
|
.map(key => ({
|
||||||
version: localPkgs[key].manifest.version,
|
id: key,
|
||||||
}))
|
version: localPkgs[key].manifest.version,
|
||||||
|
marketplaceUrl: localPkgs[key].installed['marketplace-url'],
|
||||||
|
}))
|
||||||
|
.filter(pkg => {
|
||||||
|
return (
|
||||||
|
pkg.marketplaceUrl ===
|
||||||
|
this.patch.getData().ui.marketplace['known-hosts']['selected-id'].url
|
||||||
|
)
|
||||||
|
})
|
||||||
const latestPkgs = await this.api.getMarketplacePkgs({
|
const latestPkgs = await this.api.getMarketplacePkgs({
|
||||||
ids: idAndCurrentVersions,
|
ids: idAndCurrentVersions,
|
||||||
'eos-version-compat':
|
'eos-version-compat':
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<ion-item
|
<ion-item
|
||||||
[button]="mp.key !== patch.data.ui.marketplace['selected-id']"
|
[button]="mp.key !== patch.data.ui.marketplace['selected-id']"
|
||||||
detail="false"
|
detail="false"
|
||||||
*ngFor="let mp of patch.data.ui.marketplace.options | keyvalue"
|
*ngFor="let mp of patch.data.ui.marketplace['known-hosts'] | keyvalue"
|
||||||
(click)="presentAction(mp.key)"
|
(click)="presentAction(mp.key)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
|
|||||||
import { PatchDbService } from '../../../services/patch-db/patch-db.service'
|
import { PatchDbService } from '../../../services/patch-db/patch-db.service'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
import { MarketplaceService } from '../../marketplace-routes/marketplace.service'
|
import { MarketplaceService } from '../../marketplace-routes/marketplace.service'
|
||||||
import {
|
import { UIMarketplaceData } from '../../../services/patch-db/data-model'
|
||||||
DataModel,
|
import { ConfigService } from '../../../services/config.service'
|
||||||
UIData,
|
|
||||||
UIMarketplaceData,
|
|
||||||
} from '../../../services/patch-db/data-model'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'marketplaces',
|
selector: 'marketplaces',
|
||||||
@@ -31,6 +28,7 @@ export class MarketplacesPage {
|
|||||||
private readonly errToast: ErrorToastService,
|
private readonly errToast: ErrorToastService,
|
||||||
private readonly actionCtrl: ActionSheetController,
|
private readonly actionCtrl: ActionSheetController,
|
||||||
private readonly marketplaceService: MarketplaceService,
|
private readonly marketplaceService: MarketplaceService,
|
||||||
|
private readonly config: ConfigService,
|
||||||
public readonly patch: PatchDbService,
|
public readonly patch: PatchDbService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -95,10 +93,10 @@ export class MarketplacesPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async connect(id: string): Promise<void> {
|
private async connect(id: string): Promise<void> {
|
||||||
const marketplace = JSON.parse(
|
const marketplace: UIMarketplaceData = JSON.parse(
|
||||||
JSON.stringify(this.patch.data.ui.marketplace),
|
JSON.stringify(this.patch.data.ui.marketplace),
|
||||||
)
|
)
|
||||||
const newMarketplace = marketplace.options[id]
|
const newMarketplace = marketplace['known-hosts'][id]
|
||||||
|
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = await this.loadingCtrl.create({
|
||||||
spinner: 'lines',
|
spinner: 'lines',
|
||||||
@@ -141,7 +139,7 @@ export class MarketplacesPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async delete(id: string): Promise<void> {
|
private async delete(id: string): Promise<void> {
|
||||||
const marketplace = JSON.parse(
|
const marketplace: UIMarketplaceData = JSON.parse(
|
||||||
JSON.stringify(this.patch.data.ui.marketplace),
|
JSON.stringify(this.patch.data.ui.marketplace),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,7 +151,7 @@ export class MarketplacesPage {
|
|||||||
await loader.present()
|
await loader.present()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
delete marketplace.options[id]
|
delete marketplace['known-hosts'][id]
|
||||||
await this.api.setDbValue({ pointer: `/marketplace`, value: marketplace })
|
await this.api.setDbValue({ pointer: `/marketplace`, value: marketplace })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errToast.present(e)
|
this.errToast.present(e)
|
||||||
@@ -168,7 +166,7 @@ export class MarketplacesPage {
|
|||||||
) as UIMarketplaceData
|
) as UIMarketplaceData
|
||||||
|
|
||||||
// no-op on duplicates
|
// no-op on duplicates
|
||||||
const currentUrls = Object.values(marketplace.options).map(
|
const currentUrls = Object.values(marketplace['known-hosts']).map(
|
||||||
u => new URL(u.url).hostname,
|
u => new URL(u.url).hostname,
|
||||||
)
|
)
|
||||||
if (currentUrls.includes(new URL(url).hostname)) return
|
if (currentUrls.includes(new URL(url).hostname)) return
|
||||||
@@ -184,7 +182,7 @@ export class MarketplacesPage {
|
|||||||
try {
|
try {
|
||||||
const id = v4()
|
const id = v4()
|
||||||
const { name } = await this.api.getMarketplaceData({}, url)
|
const { name } = await this.api.getMarketplaceData({}, url)
|
||||||
marketplace.options[id] = { name, url }
|
marketplace['known-hosts'][id] = { name, url }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errToast.present({ message: `Could not connect to ${url}` } as any)
|
this.errToast.present({ message: `Could not connect to ${url}` } as any)
|
||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
@@ -208,7 +206,7 @@ export class MarketplacesPage {
|
|||||||
) as UIMarketplaceData
|
) as UIMarketplaceData
|
||||||
|
|
||||||
// no-op on duplicates
|
// no-op on duplicates
|
||||||
const currentUrls = Object.values(marketplace.options).map(
|
const currentUrls = Object.values(marketplace['known-hosts']).map(
|
||||||
u => new URL(u.url).hostname,
|
u => new URL(u.url).hostname,
|
||||||
)
|
)
|
||||||
if (currentUrls.includes(new URL(url).hostname)) return
|
if (currentUrls.includes(new URL(url).hostname)) return
|
||||||
@@ -223,7 +221,7 @@ export class MarketplacesPage {
|
|||||||
try {
|
try {
|
||||||
const id = v4()
|
const id = v4()
|
||||||
const { name } = await this.api.getMarketplaceData({}, url)
|
const { name } = await this.api.getMarketplaceData({}, url)
|
||||||
marketplace.options[id] = { name, url }
|
marketplace['known-hosts'][id] = { name, url }
|
||||||
marketplace['selected-id'] = id
|
marketplace['selected-id'] = id
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errToast.present({ message: `Could not connect to ${url}` } as any)
|
this.errToast.present({ message: `Could not connect to ${url}` } as any)
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-title *ngIf="!patch.loaded">Loading<span class="loading-dots"></span></ion-title>
|
<ion-title *ngIf="!patch.loaded"
|
||||||
<ion-title *ngIf="patch.loaded">{{ patch.data.ui.name || "Embassy-" + patch.data['server-info'].id }}</ion-title>
|
>Loading<span class="loading-dots"></span
|
||||||
|
></ion-title>
|
||||||
|
<ion-title *ngIf="patch.loaded"
|
||||||
|
>{{ patch.data.ui.name || "Embassy-" + patch.data['server-info'].id
|
||||||
|
}}</ion-title
|
||||||
|
>
|
||||||
<ion-buttons slot="end">
|
<ion-buttons slot="end">
|
||||||
<badge-menu-button></badge-menu-button>
|
<badge-menu-button></badge-menu-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
@@ -19,28 +24,59 @@
|
|||||||
<ng-template #data>
|
<ng-template #data>
|
||||||
<ion-item-group>
|
<ion-item-group>
|
||||||
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
|
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
|
||||||
<ion-item-divider><ion-text color="dark">{{ cat.key }}</ion-text></ion-item-divider>
|
<ion-item-divider
|
||||||
<ion-item button *ngFor="let button of cat.value" [detail]="button.detail" [disabled]="button.disabled | async" (click)="button.action()">
|
><ion-text color="dark">{{ cat.key }}</ion-text></ion-item-divider
|
||||||
|
>
|
||||||
|
<ion-item
|
||||||
|
button
|
||||||
|
*ngFor="let button of cat.value"
|
||||||
|
[detail]="button.detail"
|
||||||
|
[disabled]="button.disabled | async"
|
||||||
|
(click)="button.action()"
|
||||||
|
>
|
||||||
<ion-icon slot="start" [name]="button.icon"></ion-icon>
|
<ion-icon slot="start" [name]="button.icon"></ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>{{ button.title }}</h2>
|
<h2>{{ button.title }}</h2>
|
||||||
<p *ngIf="button.description">{{ button.description }}</p>
|
<p *ngIf="button.description">{{ button.description }}</p>
|
||||||
|
|
||||||
|
<!-- "Create Backup" button only -->
|
||||||
<p *ngIf="button.title === 'Create Backup'">
|
<p *ngIf="button.title === 'Create Backup'">
|
||||||
<ng-container *ngIf="patch.data['server-info'].status as status">
|
<ng-container *ngIf="patch.data['server-info'].status as status">
|
||||||
<ion-text color="warning" *ngIf="status === ServerStatus.Running">
|
<ion-text
|
||||||
Last Backup: {{ patch.data['server-info']['last-backup'] ? (patch.data['server-info']['last-backup'] | date: 'short') : 'never' }}
|
color="warning"
|
||||||
|
*ngIf="status === ServerStatus.Running"
|
||||||
|
>
|
||||||
|
Last Backup: {{ patch.data['server-info']['last-backup'] ?
|
||||||
|
(patch.data['server-info']['last-backup'] | date: 'short') :
|
||||||
|
'never' }}
|
||||||
</ion-text>
|
</ion-text>
|
||||||
<span *ngIf="status === ServerStatus.BackingUp" class="inline">
|
<span *ngIf="status === ServerStatus.BackingUp" class="inline">
|
||||||
<ion-spinner color="success" style="height: 12px; width: 12px; margin-right: 6px;"></ion-spinner>
|
<ion-spinner
|
||||||
<ion-text color="success">
|
color="success"
|
||||||
Backing up
|
style="height: 12px; width: 12px; margin-right: 6px"
|
||||||
</ion-text>
|
></ion-spinner>
|
||||||
|
<ion-text color="success"> Backing up </ion-text>
|
||||||
</span>
|
</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!-- "Software Update" button only -->
|
||||||
|
<p *ngIf="button.title === 'Software Update'">
|
||||||
|
<ng-container
|
||||||
|
*ngIf="eosService.updateAvailable$ | async; else check"
|
||||||
|
>
|
||||||
|
<ion-text class="inline" color="success">
|
||||||
|
<ion-icon name="repeat"></ion-icon>
|
||||||
|
Update Available
|
||||||
|
</ion-text>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #check>
|
||||||
|
<i>Check for updates</i>
|
||||||
|
</ng-template>
|
||||||
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
LoadingController,
|
LoadingController,
|
||||||
NavController,
|
NavController,
|
||||||
IonicSafeString,
|
IonicSafeString,
|
||||||
|
ModalController,
|
||||||
} from '@ionic/angular'
|
} from '@ionic/angular'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
@@ -11,7 +12,11 @@ import { ErrorToastService } from 'src/app/services/error-toast.service'
|
|||||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
import { ServerStatus } from 'src/app/services/patch-db/data-model'
|
import { ServerStatus } from 'src/app/services/patch-db/data-model'
|
||||||
import { Observable, of } from 'rxjs'
|
import { Observable, of } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { filter, map, take } from 'rxjs/operators'
|
||||||
|
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
|
||||||
|
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
|
||||||
|
import { exists, isEmptyObject } from 'src/app/util/misc.util'
|
||||||
|
import { EOSService } from 'src/app/services/eos.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'server-show',
|
selector: 'server-show',
|
||||||
@@ -20,17 +25,57 @@ import { map } from 'rxjs/operators'
|
|||||||
})
|
})
|
||||||
export class ServerShowPage {
|
export class ServerShowPage {
|
||||||
ServerStatus = ServerStatus
|
ServerStatus = ServerStatus
|
||||||
|
hasRecoveredPackage: boolean
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly alertCtrl: AlertController,
|
private readonly alertCtrl: AlertController,
|
||||||
|
private readonly modalCtrl: ModalController,
|
||||||
|
private readonly wizardBaker: WizardBaker,
|
||||||
private readonly loadingCtrl: LoadingController,
|
private readonly loadingCtrl: LoadingController,
|
||||||
private readonly errToast: ErrorToastService,
|
private readonly errToast: ErrorToastService,
|
||||||
private readonly embassyApi: ApiService,
|
private readonly embassyApi: ApiService,
|
||||||
private readonly navCtrl: NavController,
|
private readonly navCtrl: NavController,
|
||||||
private readonly route: ActivatedRoute,
|
private readonly route: ActivatedRoute,
|
||||||
|
public readonly eosService: EOSService,
|
||||||
public readonly patch: PatchDbService,
|
public readonly patch: PatchDbService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.patch
|
||||||
|
.watch$('recovered-packages')
|
||||||
|
.pipe(filter(exists), take(1))
|
||||||
|
.subscribe(rps => {
|
||||||
|
this.hasRecoveredPackage = !isEmptyObject(rps)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateEos(): Promise<void> {
|
||||||
|
if (this.hasRecoveredPackage) {
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: 'Cannot Update',
|
||||||
|
message:
|
||||||
|
'You cannot update EmbassyOS when you have unresolved recovered services.',
|
||||||
|
buttons: ['OK'],
|
||||||
|
})
|
||||||
|
await alert.present()
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
version,
|
||||||
|
headline,
|
||||||
|
'release-notes': releaseNotes,
|
||||||
|
} = this.eosService.eos
|
||||||
|
|
||||||
|
await wizardModal(
|
||||||
|
this.modalCtrl,
|
||||||
|
this.wizardBaker.updateOS({
|
||||||
|
version,
|
||||||
|
headline,
|
||||||
|
releaseNotes,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async presentAlertRestart() {
|
async presentAlertRestart() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Confirm',
|
header: 'Confirm',
|
||||||
@@ -54,7 +99,6 @@ export class ServerShowPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertShutdown() {
|
async presentAlertShutdown() {
|
||||||
const sts = this.patch.data['server-info'].status
|
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Warning',
|
header: 'Warning',
|
||||||
message:
|
message:
|
||||||
@@ -81,7 +125,7 @@ export class ServerShowPage {
|
|||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'System Rebuild',
|
header: 'System Rebuild',
|
||||||
message: new IonicSafeString(
|
message: new IonicSafeString(
|
||||||
`<ion-text color="warning">Important:</ion-text> This will tear down all service containers and rebuild them from scratch. This may take up to ${minutes} minutes to complete. During this time, you will lose all connectivity to your Embassy.`,
|
`<ion-text color="warning">Warning:</ion-text> This action will tear down all service containers and rebuild them from scratch. No data will be deleted. This action is useful if your system gets into a bad state, and it should only be performed if you are experiencing general performance or reliability issues. It may take up to ${minutes} minutes to complete. During this time, you will lose all connectivity to your Embassy.`,
|
||||||
),
|
),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
@@ -151,6 +195,23 @@ export class ServerShowPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async checkForEosUpdate(): Promise<void> {
|
||||||
|
const loader = await this.loadingCtrl.create({
|
||||||
|
spinner: 'lines',
|
||||||
|
message: 'Checking for updates',
|
||||||
|
cssClass: 'loader',
|
||||||
|
})
|
||||||
|
await loader.present()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.eosService.getEOS()
|
||||||
|
} catch (e) {
|
||||||
|
this.errToast.present(e)
|
||||||
|
} finally {
|
||||||
|
loader.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
settings: ServerSettings = {
|
settings: ServerSettings = {
|
||||||
Backups: [
|
Backups: [
|
||||||
{
|
{
|
||||||
@@ -179,6 +240,17 @@ export class ServerShowPage {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
Insights: [
|
Insights: [
|
||||||
|
{
|
||||||
|
title: 'Software Update',
|
||||||
|
description: 'Get the latest version of EmbassyOS',
|
||||||
|
icon: 'cog-outline',
|
||||||
|
action: () =>
|
||||||
|
this.eosService.updateAvailable$.getValue()
|
||||||
|
? this.updateEos()
|
||||||
|
: this.checkForEosUpdate(),
|
||||||
|
detail: false,
|
||||||
|
disabled: of(false),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'About',
|
title: 'About',
|
||||||
description: 'Basic information about your Embassy',
|
description: 'Basic information about your Embassy',
|
||||||
@@ -303,12 +375,14 @@ export class ServerShowPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ServerSettings {
|
interface ServerSettings {
|
||||||
[key: string]: {
|
[key: string]: SettingBtn[]
|
||||||
title: string
|
}
|
||||||
description: string
|
|
||||||
icon: string
|
interface SettingBtn {
|
||||||
action: Function
|
title: string
|
||||||
detail: boolean
|
description: string
|
||||||
disabled: Observable<boolean>
|
icon: string
|
||||||
}[]
|
action: Function
|
||||||
|
detail: boolean
|
||||||
|
disabled: Observable<boolean>
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -64,14 +64,6 @@ export module RR {
|
|||||||
export type KillSessionsReq = WithExpire<{ ids: string[] }> // sessions.kill
|
export type KillSessionsReq = WithExpire<{ ids: string[] }> // sessions.kill
|
||||||
export type KillSessionsRes = WithRevision<null>
|
export type KillSessionsRes = WithRevision<null>
|
||||||
|
|
||||||
// marketplace URLs
|
|
||||||
|
|
||||||
export type SetEosMarketplaceReq = WithExpire<{ url: string }> // marketplace.eos.set
|
|
||||||
export type SetEosMarketplaceRes = WithRevision<null>
|
|
||||||
|
|
||||||
export type SetPackageMarketplaceReq = WithExpire<{ url: string }> // marketplace.package.set
|
|
||||||
export type SetPackageMarketplaceRes = WithRevision<null>
|
|
||||||
|
|
||||||
// password
|
// password
|
||||||
|
|
||||||
export type UpdatePasswordReq = { password: string } // password.set
|
export type UpdatePasswordReq = { password: string } // password.set
|
||||||
@@ -267,15 +259,11 @@ export module RR {
|
|||||||
query?: string
|
query?: string
|
||||||
page?: string
|
page?: string
|
||||||
'per-page'?: string
|
'per-page'?: string
|
||||||
url?: string
|
|
||||||
}
|
}
|
||||||
export type GetMarketplacePackagesRes = MarketplacePkg[]
|
export type GetMarketplacePackagesRes = MarketplacePkg[]
|
||||||
|
|
||||||
export type GetReleaseNotesReq = { id: string }
|
export type GetReleaseNotesReq = { id: string }
|
||||||
export type GetReleaseNotesRes = { [version: string]: string }
|
export type GetReleaseNotesRes = { [version: string]: string }
|
||||||
|
|
||||||
export type GetLatestVersionReq = { ids: string[] }
|
|
||||||
export type GetLatestVersionRes = { [id: string]: string }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WithExpire<T> = { 'expire-id'?: string } & T
|
export type WithExpire<T> = { 'expire-id'?: string } & T
|
||||||
|
|||||||
@@ -113,15 +113,6 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
|||||||
params: RR.GetReleaseNotesReq,
|
params: RR.GetReleaseNotesReq,
|
||||||
): Promise<RR.GetReleaseNotesRes>
|
): Promise<RR.GetReleaseNotesRes>
|
||||||
|
|
||||||
abstract getLatestVersion(
|
|
||||||
params: RR.GetLatestVersionReq,
|
|
||||||
): Promise<RR.GetLatestVersionRes>
|
|
||||||
|
|
||||||
// protected abstract setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise<RR.SetPackageMarketplaceRes>
|
|
||||||
// setPackageMarketplace = (params: RR.SetPackageMarketplaceReq) => this.syncResponse(
|
|
||||||
// () => this.setPackageMarketplaceRaw(params),
|
|
||||||
// )()
|
|
||||||
|
|
||||||
// password
|
// password
|
||||||
// abstract updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes>
|
// abstract updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes>
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,22 @@ import { ApiService } from './embassy-api.service'
|
|||||||
import { RR } from './api.types'
|
import { RR } from './api.types'
|
||||||
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
||||||
import { PatchDbService } from '../patch-db/patch-db.service'
|
import { PatchDbService } from '../patch-db/patch-db.service'
|
||||||
|
import { ConfigService } from '../config.service'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LiveApiService extends ApiService {
|
export class LiveApiService extends ApiService {
|
||||||
|
private marketplaceUrl: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly http: HttpService,
|
private readonly http: HttpService,
|
||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
|
private readonly config: ConfigService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
;(window as any).rpcClient = this
|
;(window as any).rpcClient = this
|
||||||
|
this.patch.watch$('ui', 'marketplace', 'selected-id').subscribe(id => {
|
||||||
|
this.marketplaceUrl = id
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStatic(url: string): Promise<string> {
|
async getStatic(url: string): Promise<string> {
|
||||||
@@ -106,10 +113,7 @@ export class LiveApiService extends ApiService {
|
|||||||
params: {},
|
params: {},
|
||||||
url?: string,
|
url?: string,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
if (!url) {
|
url = url || this.marketplaceUrl
|
||||||
const id = this.patch.data.ui.marketplace['selected-id']
|
|
||||||
url = this.patch.data.ui.marketplace.options[id].url
|
|
||||||
}
|
|
||||||
const fullURL = `${url}${path}?${new URLSearchParams(params).toString()}`
|
const fullURL = `${url}${path}?${new URLSearchParams(params).toString()}`
|
||||||
return this.http.rpcRequest({
|
return this.http.rpcRequest({
|
||||||
method: 'marketplace.get',
|
method: 'marketplace.get',
|
||||||
@@ -120,25 +124,25 @@ export class LiveApiService extends ApiService {
|
|||||||
async getEos(
|
async getEos(
|
||||||
params: RR.GetMarketplaceEOSReq,
|
params: RR.GetMarketplaceEOSReq,
|
||||||
): Promise<RR.GetMarketplaceEOSRes> {
|
): Promise<RR.GetMarketplaceEOSRes> {
|
||||||
return this.http.httpRequest({
|
return this.marketplaceProxy(
|
||||||
method: Method.GET,
|
'/eos/latest',
|
||||||
url: '/marketplace/eos/latest',
|
|
||||||
params,
|
params,
|
||||||
})
|
this.config.eosMarketplaceUrl,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMarketplaceData(
|
async getMarketplaceData(
|
||||||
params: RR.GetMarketplaceDataReq,
|
params: RR.GetMarketplaceDataReq,
|
||||||
url?: string,
|
url?: string,
|
||||||
): Promise<RR.GetMarketplaceDataRes> {
|
): Promise<RR.GetMarketplaceDataRes> {
|
||||||
return this.marketplaceProxy('/marketplace/package/data', params, url)
|
return this.marketplaceProxy('/package/data', params, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMarketplacePkgs(
|
async getMarketplacePkgs(
|
||||||
params: RR.GetMarketplacePackagesReq,
|
params: RR.GetMarketplacePackagesReq,
|
||||||
): Promise<RR.GetMarketplacePackagesRes> {
|
): Promise<RR.GetMarketplacePackagesRes> {
|
||||||
if (params.query) params.category = undefined
|
if (params.query) params.category = undefined
|
||||||
return this.marketplaceProxy('/marketplace/package/index', {
|
return this.marketplaceProxy('/package/index', {
|
||||||
...params,
|
...params,
|
||||||
ids: JSON.stringify(params.ids),
|
ids: JSON.stringify(params.ids),
|
||||||
})
|
})
|
||||||
@@ -147,27 +151,9 @@ export class LiveApiService extends ApiService {
|
|||||||
async getReleaseNotes(
|
async getReleaseNotes(
|
||||||
params: RR.GetReleaseNotesReq,
|
params: RR.GetReleaseNotesReq,
|
||||||
): Promise<RR.GetReleaseNotesRes> {
|
): Promise<RR.GetReleaseNotesRes> {
|
||||||
return this.http.httpRequest({
|
return this.marketplaceProxy('/package/release-notes', params)
|
||||||
method: Method.GET,
|
|
||||||
url: '/marketplace/package/release-notes',
|
|
||||||
params,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestVersion(
|
|
||||||
params: RR.GetLatestVersionReq,
|
|
||||||
): Promise<RR.GetLatestVersionRes> {
|
|
||||||
return this.http.httpRequest({
|
|
||||||
method: Method.GET,
|
|
||||||
url: '/marketplace/latest-version',
|
|
||||||
params,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise<RR.SetPackageMarketplaceRes> {
|
|
||||||
// return this.http.rpcRequest({ method: 'marketplace.package.set', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// password
|
// password
|
||||||
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
||||||
// return this.http.rpcRequest({ method: 'password.set', params })
|
// return this.http.rpcRequest({ method: 'password.set', params })
|
||||||
|
|||||||
@@ -228,16 +228,6 @@ export class MockApiService extends ApiService {
|
|||||||
return Mock.ReleaseNotes
|
return Mock.ReleaseNotes
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestVersion (
|
|
||||||
params: RR.GetLatestVersionReq,
|
|
||||||
): Promise<RR.GetLatestVersionRes> {
|
|
||||||
await pauseFor(2000)
|
|
||||||
return params.ids.reduce((obj, id) => {
|
|
||||||
obj[id] = '1.3.0'
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// password
|
// password
|
||||||
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
|
||||||
// await pauseFor(2000)
|
// await pauseFor(2000)
|
||||||
|
|||||||
@@ -16,15 +16,7 @@ export const mockPatchData: DataModel = {
|
|||||||
'pkg-order': [],
|
'pkg-order': [],
|
||||||
'ack-welcome': '1.0.0',
|
'ack-welcome': '1.0.0',
|
||||||
'ack-share-stats': false,
|
'ack-share-stats': false,
|
||||||
marketplace: {
|
marketplace: undefined,
|
||||||
'selected-id': 'asdfasdf',
|
|
||||||
options: {
|
|
||||||
asdfasdf: {
|
|
||||||
name: 'Start9',
|
|
||||||
url: 'start9marketplace.com',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'server-info': {
|
'server-info': {
|
||||||
id: 'embassy-abcdefgh',
|
id: 'embassy-abcdefgh',
|
||||||
@@ -439,6 +431,8 @@ export const mockPatchData: DataModel = {
|
|||||||
},
|
},
|
||||||
'current-dependencies': {},
|
'current-dependencies': {},
|
||||||
'dependency-info': {},
|
'dependency-info': {},
|
||||||
|
'marketplace-url': 'marketplace-url.com',
|
||||||
|
'developer-key': 'developer-key',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lnd: {
|
lnd: {
|
||||||
@@ -644,6 +638,8 @@ export const mockPatchData: DataModel = {
|
|||||||
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
icon: 'assets/img/service-icons/btc-rpc-proxy.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'marketplace-url': 'marketplace-url.com',
|
||||||
|
'developer-key': 'developer-key',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { WorkspaceConfig } from '@shared'
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
useMocks,
|
useMocks,
|
||||||
ui: { gitHash, patchDb, api, mocks },
|
ui: { gitHash, patchDb, api, mocks, eosMarketplaceURL },
|
||||||
} = require('../../../../../config.json') as WorkspaceConfig
|
} = require('../../../../../config.json') as WorkspaceConfig
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -25,6 +25,7 @@ export class ConfigService {
|
|||||||
gitHash = gitHash
|
gitHash = gitHash
|
||||||
patchDb = patchDb
|
patchDb = patchDb
|
||||||
api = api
|
api = api
|
||||||
|
eosMarketplaceUrl = eosMarketplaceURL
|
||||||
|
|
||||||
skipStartupAlerts = useMocks && mocks.skipStartupAlerts
|
skipStartupAlerts = useMocks && mocks.skipStartupAlerts
|
||||||
isConsulate = window['platform'] === 'ios'
|
isConsulate = window['platform'] === 'ios'
|
||||||
|
|||||||
33
frontend/projects/ui/src/app/services/eos.service.ts
Normal file
33
frontend/projects/ui/src/app/services/eos.service.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { BehaviorSubject } from 'rxjs'
|
||||||
|
import { MarketplaceEOS } from 'src/app/services/api/api.types'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
import { Emver } from 'src/app/services/emver.service'
|
||||||
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class EOSService {
|
||||||
|
eos: MarketplaceEOS
|
||||||
|
updateAvailable$ = new BehaviorSubject<boolean>(false)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly api: ApiService,
|
||||||
|
private readonly emver: Emver,
|
||||||
|
private readonly patch: PatchDbService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getEOS(): Promise<void> {
|
||||||
|
this.eos = await this.api.getEos({
|
||||||
|
'eos-version-compat':
|
||||||
|
this.patch.getData()['server-info']['eos-version-compat'],
|
||||||
|
})
|
||||||
|
const updateAvailable =
|
||||||
|
this.emver.compare(
|
||||||
|
this.eos.version,
|
||||||
|
this.patch.data['server-info'].version,
|
||||||
|
) === 1
|
||||||
|
this.updateAvailable$.next(updateAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,8 @@ export interface UIData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UIMarketplaceData {
|
export interface UIMarketplaceData {
|
||||||
'selected-id': string
|
'selected-id': string | null
|
||||||
options: {
|
'known-hosts': {
|
||||||
[id: string]: {
|
[id: string]: {
|
||||||
url: string
|
url: string
|
||||||
name: string
|
name: string
|
||||||
@@ -94,6 +94,8 @@ export interface InstalledPackageDataEntry {
|
|||||||
'interface-addresses': {
|
'interface-addresses': {
|
||||||
[id: string]: { 'tor-address': string; 'lan-address': string }
|
[id: string]: { 'tor-address': string; 'lan-address': string }
|
||||||
}
|
}
|
||||||
|
'marketplace-url': string | null
|
||||||
|
'developer-key': string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CurrentDependencyInfo {
|
export interface CurrentDependencyInfo {
|
||||||
|
|||||||
@@ -1,26 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import {
|
import { ModalController } from '@ionic/angular'
|
||||||
AlertController,
|
|
||||||
IonicSafeString,
|
|
||||||
ModalController,
|
|
||||||
NavController,
|
|
||||||
} from '@ionic/angular'
|
|
||||||
import { wizardModal } from '../components/install-wizard/install-wizard.component'
|
|
||||||
import { WizardBaker } from '../components/install-wizard/prebaked-wizards'
|
import { WizardBaker } from '../components/install-wizard/prebaked-wizards'
|
||||||
import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page'
|
import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page'
|
||||||
import { displayEmver } from '../pipes/emver.pipe'
|
|
||||||
import { RR } from './api/api.types'
|
|
||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
import { Emver } from './emver.service'
|
|
||||||
import { MarketplaceService } from '../pages/marketplace-routes/marketplace.service'
|
|
||||||
import { DataModel } from './patch-db/data-model'
|
|
||||||
import { PatchDbService } from './patch-db/patch-db.service'
|
import { PatchDbService } from './patch-db/patch-db.service'
|
||||||
import { filter, take } from 'rxjs/operators'
|
import { filter, take } from 'rxjs/operators'
|
||||||
import { isEmptyObject } from '../util/misc.util'
|
import { isEmptyObject } from '../util/misc.util'
|
||||||
import { ApiService } from './api/embassy-api.service'
|
import { ApiService } from './api/embassy-api.service'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { ServerConfigService } from './server-config.service'
|
import { ServerConfigService } from './server-config.service'
|
||||||
import { v4 } from 'uuid'
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -29,13 +17,9 @@ export class StartupAlertsService {
|
|||||||
private checks: Check<any>[]
|
private checks: Check<any>[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly alertCtrl: AlertController,
|
|
||||||
private readonly navCtrl: NavController,
|
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
private readonly modalCtrl: ModalController,
|
private readonly modalCtrl: ModalController,
|
||||||
private readonly marketplaceService: MarketplaceService,
|
|
||||||
private readonly api: ApiService,
|
private readonly api: ApiService,
|
||||||
private readonly emver: Emver,
|
|
||||||
private readonly wizardBaker: WizardBaker,
|
private readonly wizardBaker: WizardBaker,
|
||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
private readonly serverConfig: ServerConfigService,
|
private readonly serverConfig: ServerConfigService,
|
||||||
@@ -43,22 +27,14 @@ export class StartupAlertsService {
|
|||||||
const osWelcome: Check<boolean> = {
|
const osWelcome: Check<boolean> = {
|
||||||
name: 'osWelcome',
|
name: 'osWelcome',
|
||||||
shouldRun: () => this.shouldRunOsWelcome(),
|
shouldRun: () => this.shouldRunOsWelcome(),
|
||||||
check: async () => true,
|
|
||||||
display: () => this.displayOsWelcome(),
|
display: () => this.displayOsWelcome(),
|
||||||
}
|
}
|
||||||
const shareStats: Check<boolean> = {
|
const shareStats: Check<boolean> = {
|
||||||
name: 'shareStats',
|
name: 'shareStats',
|
||||||
shouldRun: () => this.shouldRunShareStats(),
|
shouldRun: () => this.shouldRunShareStats(),
|
||||||
check: async () => true,
|
|
||||||
display: () => this.displayShareStats(),
|
display: () => this.displayShareStats(),
|
||||||
}
|
}
|
||||||
const osUpdate: Check<RR.GetMarketplaceEOSRes | undefined> = {
|
this.checks = [osWelcome, shareStats]
|
||||||
name: 'osUpdate',
|
|
||||||
shouldRun: () => this.shouldRunOsUpdateCheck(),
|
|
||||||
check: () => this.osUpdateCheck(),
|
|
||||||
display: pkg => this.displayOsUpdateCheck(pkg),
|
|
||||||
}
|
|
||||||
this.checks = [osWelcome, shareStats, osUpdate]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This takes our three checks and filters down to those that should run.
|
// This takes our three checks and filters down to those that should run.
|
||||||
@@ -72,37 +48,14 @@ export class StartupAlertsService {
|
|||||||
filter(data => !isEmptyObject(data)),
|
filter(data => !isEmptyObject(data)),
|
||||||
take(1),
|
take(1),
|
||||||
)
|
)
|
||||||
.subscribe(async data => {
|
.subscribe(async () => {
|
||||||
if (!data.ui.marketplace) {
|
|
||||||
const uuid = v4()
|
|
||||||
const value = {
|
|
||||||
'selected-id': uuid,
|
|
||||||
options: {
|
|
||||||
[uuid]: {
|
|
||||||
url: 'marketplaceurl.com',
|
|
||||||
name: 'Start9',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await this.api.setDbValue({ pointer: 'marketplace', value })
|
|
||||||
}
|
|
||||||
await this.checks
|
await this.checks
|
||||||
.filter(c => !this.config.skipStartupAlerts && c.shouldRun())
|
.filter(c => !this.config.skipStartupAlerts && c.shouldRun())
|
||||||
// returning true in the below block means to continue to next modal
|
// returning true in the below block means to continue to next modal
|
||||||
// returning false means to skip all subsequent modals
|
// returning false means to skip all subsequent modals
|
||||||
.reduce(async (previousDisplay, c) => {
|
.reduce(async (previousDisplay, c) => {
|
||||||
let checkRes: any
|
|
||||||
try {
|
|
||||||
checkRes = await c.check()
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Exception in ${c.name} check:`, e)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayRes = await previousDisplay
|
const displayRes = await previousDisplay
|
||||||
|
if (displayRes) return c.display()
|
||||||
if (!checkRes) return true
|
|
||||||
if (displayRes) return c.display(checkRes)
|
|
||||||
}, Promise.resolve(true))
|
}, Promise.resolve(true))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -120,21 +73,6 @@ export class StartupAlertsService {
|
|||||||
return this.patch.getData().ui['auto-check-updates']
|
return this.patch.getData().ui['auto-check-updates']
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** check **
|
|
||||||
|
|
||||||
private async osUpdateCheck(): Promise<RR.GetMarketplaceEOSRes | undefined> {
|
|
||||||
const res = await this.api.getEos({
|
|
||||||
'eos-version-compat':
|
|
||||||
this.patch.getData()['server-info']['eos-version-compat'],
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.emver.compare(this.config.version, res.version) === -1) {
|
|
||||||
return res
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ** display **
|
// ** display **
|
||||||
|
|
||||||
private async displayOsWelcome(): Promise<boolean> {
|
private async displayOsWelcome(): Promise<boolean> {
|
||||||
@@ -174,69 +112,14 @@ export class StartupAlertsService {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async displayOsUpdateCheck(
|
|
||||||
eos: RR.GetMarketplaceEOSRes,
|
|
||||||
): Promise<boolean> {
|
|
||||||
const { update } = await this.presentAlertNewOS(eos.version)
|
|
||||||
if (update) {
|
|
||||||
const { cancelled } = await wizardModal(
|
|
||||||
this.modalCtrl,
|
|
||||||
this.wizardBaker.updateOS({
|
|
||||||
version: eos.version,
|
|
||||||
headline: eos.headline,
|
|
||||||
releaseNotes: eos['release-notes'],
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if (cancelled) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// more
|
|
||||||
|
|
||||||
private async presentAlertNewOS(
|
|
||||||
versionLatest: string,
|
|
||||||
): Promise<{ cancel?: true; update?: true }> {
|
|
||||||
return new Promise(async resolve => {
|
|
||||||
const alert = await this.alertCtrl.create({
|
|
||||||
header: 'New EmbassyOS Version!',
|
|
||||||
message: new IonicSafeString(
|
|
||||||
`<div style="display: flex; flex-direction: column; justify-content: space-around; min-height: 100px">
|
|
||||||
<div>Update EmbassyOS to version ${displayEmver(
|
|
||||||
versionLatest,
|
|
||||||
)}?</div>
|
|
||||||
<div style="font-size:x-small">You can disable these checks in your Embassy Config</div>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'Not now',
|
|
||||||
role: 'cancel',
|
|
||||||
handler: () => resolve({ cancel: true }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Update',
|
|
||||||
handler: () => resolve({ update: true }),
|
|
||||||
cssClass: 'enter-click',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
await alert.present()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Check<T> = {
|
type Check<T> = {
|
||||||
// validates whether a check should run based on server properties
|
// validates whether a check should run based on server properties
|
||||||
shouldRun: () => boolean
|
shouldRun: () => boolean
|
||||||
// executes a check, often requiring api call. It should return a false-y value if there should be no display.
|
|
||||||
check: () => Promise<T>
|
|
||||||
// display an alert based on the result of the check.
|
// display an alert based on the result of the check.
|
||||||
// return false if subsequent modals should not be displayed
|
// return false if subsequent modals should not be displayed
|
||||||
display: (a: T) => Promise<boolean>
|
display: () => Promise<boolean>
|
||||||
// for logging purposes
|
// for logging purposes
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user