-
-
Welcome to Embassy
-
Get started by installing your first service.
+
+
+
+
+
+
+
+
+
Welcome to Embassy
+
Get started by installing your first service.
+
+
+
+ Marketplace
+
-
-
- Marketplace
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ pkg.value.entry.state | titlecase }}...{{ (pkg.value.entry['install-progress'] | installState).totalProgress }}%
- {{ pkg.value.entry.manifest.title }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ pkg.value.entry.state | titlecase }}...{{ (pkg.value.entry['install-progress'] | installState).totalProgress }}%
+ {{ pkg.value.entry.manifest.title }}
+
+
+
+
+
+
+
diff --git a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts
index b8052a8a3..764d593d0 100644
--- a/ui/src/app/pages/apps-routes/app-list/app-list.page.ts
+++ b/ui/src/app/pages/apps-routes/app-list/app-list.page.ts
@@ -5,7 +5,7 @@ import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
import { Subscription } from 'rxjs'
import { PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
-import { filter } from 'rxjs/operators'
+import { delay, filter } from 'rxjs/operators'
@Component({
selector: 'app-list',
@@ -25,6 +25,7 @@ export class AppListPage {
sub: Subscription | null
}} = { }
PackageState = PackageState
+ loading = true
constructor (
private readonly config: ConfigService,
@@ -41,6 +42,8 @@ export class AppListPage {
}),
)
.subscribe(pkgs => {
+ this.loading = false
+
const ids = Object.keys(pkgs)
Object.keys(this.pkgs).forEach(id => {
diff --git a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
index 9e05ee079..dad860329 100644
--- a/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
+++ b/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
@@ -84,33 +84,31 @@ export class AppShowPage {
async stop (): Promise
{
const { id, title, version } = this.pkg.manifest
- const loader = await this.loadingCtrl.create({
- message: `Stopping...`,
- spinner: 'lines',
- cssClass: 'loader',
- })
- await loader.present()
- try {
- const breakages = await this.embassyApi.dryStopPackage({ id })
+ if (isEmptyObject(this.pkg.installed['current-dependents'])) {
+ const loader = await this.loadingCtrl.create({
+ message: `Stopping...`,
+ spinner: 'lines',
+ cssClass: 'loader',
+ })
+ await loader.present()
- if (!isEmptyObject(breakages)) {
- const { cancelled } = await wizardModal(
- this.modalCtrl,
- this.wizardBaker.stop({
- id,
- title,
- version,
- breakages,
- }),
- )
- if (cancelled) return
+ try {
+ await this.embassyApi.stopPackage({ id })
+ } catch (e) {
+ this.errToast.present(e)
+ } finally {
+ loader.dismiss()
}
- await this.embassyApi.stopPackage({ id })
- } catch (e) {
- this.errToast.present(e)
- } finally {
- loader.dismiss()
+ } else {
+ wizardModal(
+ this.modalCtrl,
+ this.wizardBaker.stop({
+ id,
+ title,
+ version,
+ }),
+ )
}
}
diff --git a/ui/src/app/pages/login/login.page.ts b/ui/src/app/pages/login/login.page.ts
index a59e2aba4..14c2ceca3 100644
--- a/ui/src/app/pages/login/login.page.ts
+++ b/ui/src/app/pages/login/login.page.ts
@@ -2,7 +2,6 @@ import { Component } from '@angular/core'
import { LoadingController } from '@ionic/angular'
import { Subscription } from 'rxjs'
import { AuthService } from 'src/app/services/auth.service'
-import { PatchConnection, PatchDbService } from 'src/app/services/patch-db/patch-db.service'
@Component({
selector: 'login',
@@ -19,13 +18,8 @@ export class LoginPage {
constructor (
private readonly authService: AuthService,
private readonly loadingCtrl: LoadingController,
- private readonly patch: PatchDbService,
) { }
- ngOnInit () {
-
- }
-
ngOnDestroy () {
if (this.loader) {
this.loader.dismiss()
@@ -52,16 +46,10 @@ export class LoginPage {
try {
await this.authService.login(this.password)
- this.loader.message = 'Loading Embassy Data'
this.password = ''
- this.patchConnectionSub = this.patch.watchPatchConnection$().subscribe(status => {
- if (status === PatchConnection.Disconnected) {
- this.error = 'Connection failed'
- this.loader.dismiss()
- }
- })
} catch (e) {
this.error = e.message
+ } finally {
this.loader.dismiss()
}
}
diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts
index 7a0db298c..75dcc5d9c 100644
--- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts
+++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts
@@ -6,6 +6,7 @@ import { MarketplaceListPage } from './marketplace-list.page'
import { SharingModule } from '../../../modules/sharing.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
+import { FormsModule } from '@angular/forms'
const routes: Routes = [
{
@@ -18,6 +19,7 @@ const routes: Routes = [
imports: [
CommonModule,
IonicModule,
+ FormsModule,
RouterModule.forChild(routes),
StatusComponentModule,
SharingModule,
diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html
index df3038dcb..27af02dc8 100644
--- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html
+++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.html
@@ -9,7 +9,14 @@
Embassy Marketplace
-
+
+
+
+
+
+
+
+
diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
index 6b07f5631..dab21c2b8 100644
--- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
+++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
@@ -88,10 +88,11 @@ export class MarketplaceListPage {
e.target.complete()
}
- async search (e?: any): Promise {
- this.query = e.target.value || undefined
- this.page = 1
+ async search (): Promise {
+ if (!this.query) return
this.pkgsLoading = true
+ this.category = undefined
+ this.page = 1
await this.getPkgs()
}
@@ -134,9 +135,9 @@ export class MarketplaceListPage {
}
async switchCategory (category: string): Promise {
- this.pkgs = []
- this.category = category
this.pkgsLoading = true
+ this.category = category
+ this.query = undefined
this.page = 1
await this.getPkgs()
}
diff --git a/ui/src/app/services/api/api.fixures.ts b/ui/src/app/services/api/api.fixures.ts
index 2d825c960..9bd69a8f5 100644
--- a/ui/src/app/services/api/api.fixures.ts
+++ b/ui/src/app/services/api/api.fixures.ts
@@ -581,56 +581,58 @@ export module Mock {
export const MarketplacePkgsList: RR.GetMarketplacePackagesRes = Object.values(Mock.MarketplacePkgs).map(service => service['latest'])
- export const bitcoinproxy: PackageDataEntry = {
- state: PackageState.Installed,
- 'static-files': {
- license: 'licenseUrl', // /public/package-data/bitcoinproxy/0.21.1/LICENSE.md,
- icon: 'assets/img/service-icons/bitcoin-proxy.png',
- instructions: 'instructionsUrl', // /public/package-data/bitcoinproxy/0.2.2/INSTRUCTIONS.md
- },
- manifest: MockManifestBitcoinProxy,
- installed: {
- status: {
- configured: true,
- main: {
- status: PackageMainStatus.Running,
- started: new Date().toISOString(),
- health: { },
- },
- 'dependency-errors': { },
+ export const Pkgs: { [key: string]: PackageDataEntry } = {
+ 'bitcoin-proxy': {
+ state: PackageState.Installed,
+ 'static-files': {
+ license: 'licenseUrl', // /public/package-data/bitcoinproxy/0.21.1/LICENSE.md,
+ icon: 'assets/img/service-icons/bitcoin-proxy.png',
+ instructions: 'instructionsUrl', // /public/package-data/bitcoinproxy/0.2.2/INSTRUCTIONS.md
},
manifest: MockManifestBitcoinProxy,
- 'interface-addresses': {
- rpc: {
- 'tor-address': 'bitcoinproxy-rpc-address.onion',
- 'lan-address': 'bitcoinproxy-rpc-address.local',
- },
- },
- 'system-pointers': [],
- 'current-dependents': {
- 'lnd': {
- pointers: [],
- 'health-checks': [],
- },
- },
- 'current-dependencies': {
- 'bitcoind': {
- pointers: [],
- 'health-checks': [],
- },
- },
- 'dependency-info': {
- 'lnd': {
- manifest: Mock.MockManifestLnd,
- icon: 'assets/img/service-icons/lnd.png',
- },
- 'bitcoind': {
- manifest: Mock.MockManifestBitcoind,
- icon: 'assets/img/service-icons/bitcoind.png',
+ installed: {
+ status: {
+ configured: true,
+ main: {
+ status: PackageMainStatus.Running,
+ started: new Date().toISOString(),
+ health: { },
+ },
+ 'dependency-errors': { },
+ },
+ manifest: MockManifestBitcoinProxy,
+ 'interface-addresses': {
+ rpc: {
+ 'tor-address': 'bitcoinproxy-rpc-address.onion',
+ 'lan-address': 'bitcoinproxy-rpc-address.local',
+ },
+ },
+ 'system-pointers': [],
+ 'current-dependents': {
+ 'lnd': {
+ pointers: [],
+ 'health-checks': [],
+ },
+ },
+ 'current-dependencies': {
+ 'bitcoind': {
+ pointers: [],
+ 'health-checks': [],
+ },
+ },
+ 'dependency-info': {
+ 'lnd': {
+ manifest: Mock.MockManifestLnd,
+ icon: 'assets/img/service-icons/lnd.png',
+ },
+ 'bitcoind': {
+ manifest: Mock.MockManifestBitcoind,
+ icon: 'assets/img/service-icons/bitcoind.png',
+ },
},
},
+ 'install-progress': undefined,
},
- 'install-progress': undefined,
}
export const Notifications: ServerNotifications = [
@@ -1455,62 +1457,62 @@ export module Mock {
// 'install-progress': undefined,
// }
- export const lnd: PackageDataEntry = {
- state: PackageState.Installed,
- 'static-files': {
- license: 'licenseUrl', // /public/package-data/lnd/0.21.1/LICENSE.md,
- icon: 'assets/img/service-icons/lnd.png',
- instructions: 'instructionsUrl', // /public/package-data/lnd/0.21.1/INSTRUCTIONS.md
- },
- manifest: MockManifestLnd,
- installed: {
- status: {
- configured: true,
- main: {
- status: PackageMainStatus.Stopped,
- },
- 'dependency-errors': {
- 'bitcoin-proxy': {
- type: DependencyErrorType.NotInstalled,
- },
- },
- },
- manifest: MockManifestLnd,
- 'interface-addresses': {
- rpc: {
- 'tor-address': 'lnd-rpc-address.onion',
- 'lan-address': 'lnd-rpc-address.local',
- },
- grpc: {
- 'tor-address': 'lnd-grpc-address.onion',
- 'lan-address': 'lnd-grpc-address.local',
- },
- },
- 'system-pointers': [],
- 'current-dependents': { },
- 'current-dependencies': {
- 'bitcoind': {
- pointers: [],
- 'health-checks': [],
- },
- 'bitcoin-proxy': {
- pointers: [],
- 'health-checks': [],
- },
- },
- 'dependency-info': {
- 'bitcoind': {
- manifest: Mock.MockManifestBitcoind,
- icon: 'assets/img/service-icons/bitcoind.png',
- },
- 'bitcoin-proxy': {
- manifest: Mock.MockManifestBitcoinProxy,
- icon: 'assets/img/service-icons/bitcoin-proxy.png',
- },
- },
- },
- 'install-progress': undefined,
- }
+ // export const lnd: PackageDataEntry = {
+ // state: PackageState.Installed,
+ // 'static-files': {
+ // license: 'licenseUrl', // /public/package-data/lnd/0.21.1/LICENSE.md,
+ // icon: 'assets/img/service-icons/lnd.png',
+ // instructions: 'instructionsUrl', // /public/package-data/lnd/0.21.1/INSTRUCTIONS.md
+ // },
+ // manifest: MockManifestLnd,
+ // installed: {
+ // status: {
+ // configured: true,
+ // main: {
+ // status: PackageMainStatus.Stopped,
+ // },
+ // 'dependency-errors': {
+ // 'bitcoin-proxy': {
+ // type: DependencyErrorType.NotInstalled,
+ // },
+ // },
+ // },
+ // manifest: MockManifestLnd,
+ // 'interface-addresses': {
+ // rpc: {
+ // 'tor-address': 'lnd-rpc-address.onion',
+ // 'lan-address': 'lnd-rpc-address.local',
+ // },
+ // grpc: {
+ // 'tor-address': 'lnd-grpc-address.onion',
+ // 'lan-address': 'lnd-grpc-address.local',
+ // },
+ // },
+ // 'system-pointers': [],
+ // 'current-dependents': { },
+ // 'current-dependencies': {
+ // 'bitcoind': {
+ // pointers: [],
+ // 'health-checks': [],
+ // },
+ // 'bitcoin-proxy': {
+ // pointers: [],
+ // 'health-checks': [],
+ // },
+ // },
+ // 'dependency-info': {
+ // 'bitcoind': {
+ // manifest: Mock.MockManifestBitcoind,
+ // icon: 'assets/img/service-icons/bitcoind.png',
+ // },
+ // 'bitcoin-proxy': {
+ // manifest: Mock.MockManifestBitcoinProxy,
+ // icon: 'assets/img/service-icons/bitcoin-proxy.png',
+ // },
+ // },
+ // },
+ // 'install-progress': undefined,
+ // }
// export const DbDump: RR.GetDumpRes = {
// id: 1,
diff --git a/ui/src/app/services/api/embassy-api.service.ts b/ui/src/app/services/api/embassy-api.service.ts
index 97a029cc5..4e2060444 100644
--- a/ui/src/app/services/api/embassy-api.service.ts
+++ b/ui/src/app/services/api/embassy-api.service.ts
@@ -13,6 +13,8 @@ export abstract class ApiService implements Source, Http {
return this.sync.asObservable()
}
+ connectionMade$ = new Subject()
+
// for getting static files: ex icons, instructions, licenses
abstract getStatic (url: string): Promise
@@ -195,6 +197,7 @@ export abstract class ApiService implements Source, Http {
throw e
})
.then(({ response, revision }) => {
+ this.connectionMade$.next()
if (revision) this.sync.next(revision)
return response
})
diff --git a/ui/src/app/services/api/embassy-live-api.service.ts b/ui/src/app/services/api/embassy-live-api.service.ts
index 005ed94cd..52fcca814 100644
--- a/ui/src/app/services/api/embassy-live-api.service.ts
+++ b/ui/src/app/services/api/embassy-live-api.service.ts
@@ -96,6 +96,7 @@ export class LiveApiService extends ApiService {
}
async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise {
+ if (params.query) params.category = undefined
return this.http.httpRequest({
method: Method.GET,
url: '/marketplace/package/index',
diff --git a/ui/src/app/services/api/embassy-mock-api.service.ts b/ui/src/app/services/api/embassy-mock-api.service.ts
index 042186985..b79f6feb2 100644
--- a/ui/src/app/services/api/embassy-mock-api.service.ts
+++ b/ui/src/app/services/api/embassy-mock-api.service.ts
@@ -335,7 +335,7 @@ export class MockApiService extends ApiService {
'unpack-complete': false,
}
const pkg: PackageDataEntry = {
- ...Mock[params.id],
+ ...Mock.Pkgs[params.id],
state: PackageState.Installing,
'install-progress': initialProgress,
}
diff --git a/ui/src/app/services/patch-db/patch-db.service.ts b/ui/src/app/services/patch-db/patch-db.service.ts
index 5ddb61d80..6fb2efb3a 100644
--- a/ui/src/app/services/patch-db/patch-db.service.ts
+++ b/ui/src/app/services/patch-db/patch-db.service.ts
@@ -1,7 +1,7 @@
import { Inject, Injectable, InjectionToken } from '@angular/core'
import { Bootstrapper, PatchDB, Source, Store } from 'patch-db-client'
-import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'
-import { catchError, debounceTime, finalize, map, tap } from 'rxjs/operators'
+import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'
+import { catchError, debounceTime, delay, filter, finalize, map, take, tap, timeout } from 'rxjs/operators'
import { pauseFor } from 'src/app/util/misc.util'
import { ApiService } from '../api/embassy-api.service'
import { DataModel } from './data-model'
@@ -42,18 +42,30 @@ export class PatchDbService {
start (): void {
// make sure everything is stopped before initializing
if (this.patchSub) {
+ console.log('Retrying')
this.patchSub.unsubscribe()
this.patchSub = undefined
}
- console.log('Retrying')
try {
- this.patchSub = this.patchDb.sync$()
- .pipe(debounceTime(500))
- .subscribe({
- next: cache => {
+ const connectedSub$ = this.patchDb.connectionMade$()
+ .pipe(
+ tap(() => {
this.patchConnection$.next(PatchConnection.Connected)
+ }),
+ timeout(30000),
+ take(1),
+ )
+
+ const updateSub$ = this.patchDb.sync$()
+ .pipe(
+ debounceTime(500),
+ tap(cache => {
this.bootstrapper.update(cache)
- },
+ }),
+ )
+
+ this.patchSub = combineLatest([connectedSub$, updateSub$])
+ .subscribe({
error: async e => {
console.error('patch-db-sync sub ERROR', e)
this.patchConnection$.next(PatchConnection.Disconnected)
diff --git a/ui/src/app/util/misc.util.ts b/ui/src/app/util/misc.util.ts
index 8ff4e12a6..e370fa361 100644
--- a/ui/src/app/util/misc.util.ts
+++ b/ui/src/app/util/misc.util.ts
@@ -171,4 +171,19 @@ export const exists = (t: any) => {
export type DeepPartial = {
[k in keyof T]?: DeepPartial
+}
+
+export function debounce (delay: number = 300): MethodDecorator {
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
+ const timeoutKey = Symbol()
+
+ const original = descriptor.value
+
+ descriptor.value = function (...args) {
+ clearTimeout(this[timeoutKey])
+ this[timeoutKey] = setTimeout(() => original.apply(this, args), delay)
+ }
+
+ return descriptor
+ }
}
\ No newline at end of file