From 31952afe1e7c2e1acc954d8c16af41d9bb5c2470 Mon Sep 17 00:00:00 2001
From: Lucy C <12953208+elvece@users.noreply.github.com>
Date: Mon, 27 Jun 2022 10:09:27 -0600
Subject: [PATCH] adjust service marketplace button for installation source
relevance (#1571)
* adjust service marketplace button for installation source relevance
* cleanup
* show marketplace name instead of url; cleanup from PR feedback
* fix spacing
* further cleanup
---
.../projects/shared/src/util/misc.util.ts | 13 +++
frontend/projects/shared/styles/shared.scss | 8 ++
.../apps-routes/app-show/app-show.page.html | 4 +-
.../apps-routes/app-show/app-show.page.ts | 19 +++-
.../app-show/pipes/to-buttons.pipe.ts | 107 ++++++++++++++----
.../src/app/services/marketplace.service.ts | 4 +
frontend/projects/ui/src/app/util/web.util.ts | 5 +-
7 files changed, 134 insertions(+), 26 deletions(-)
diff --git a/frontend/projects/shared/src/util/misc.util.ts b/frontend/projects/shared/src/util/misc.util.ts
index 85dc6231f..7123a95cc 100644
--- a/frontend/projects/shared/src/util/misc.util.ts
+++ b/frontend/projects/shared/src/util/misc.util.ts
@@ -36,3 +36,16 @@ export function debounce(delay: number = 300): MethodDecorator {
return descriptor
}
}
+
+export function removeTrailingSlash(word: string): string {
+ return word.replace(/\/+$/, '')
+}
+
+export function isValidHttpUrl(string: string): boolean {
+ try {
+ const _ = new URL(string)
+ return true
+ } catch (_) {
+ return false
+ }
+}
diff --git a/frontend/projects/shared/styles/shared.scss b/frontend/projects/shared/styles/shared.scss
index e794788c3..b6e0fcf77 100644
--- a/frontend/projects/shared/styles/shared.scss
+++ b/frontend/projects/shared/styles/shared.scss
@@ -110,4 +110,12 @@ ion-modal::part(content) {
.montserrat {
font-family: 'Montserrat', sans-serif !important;
+}
+
+.color-success-shade {
+ color: var(--ion-color-success-shade)
+}
+
+.color-primary-shade {
+ color: var(--ion-color-primary-shade)
}
\ No newline at end of file
diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html b/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html
index deee7307d..b33f3ef1f 100644
--- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html
+++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html
@@ -24,7 +24,9 @@
[dependencies]="dependencies"
>
-
+
diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
index dc69a06f0..93e2d2a1d 100644
--- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
+++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts
@@ -1,10 +1,11 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core'
+import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
import { NavController } from '@ionic/angular'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import {
PackageDataEntry,
PackageMainStatus,
PackageState,
+ UIMarketplaceData,
} from 'src/app/services/patch-db/data-model'
import {
PackageStatus,
@@ -17,6 +18,12 @@ import {
import { map, startWith, filter } from 'rxjs/operators'
import { ActivatedRoute } from '@angular/router'
import { getPkgId } from '@start9labs/shared'
+import { MarketplaceService } from 'src/app/services/marketplace.service'
+import {
+ AbstractMarketplaceService,
+ Marketplace,
+} from '@start9labs/marketplace'
+import { Observable } from 'rxjs'
const STATES = [
PackageState.Installing,
@@ -53,6 +60,12 @@ export class AppShowPage {
),
)
+ readonly currentMarketplace$: Observable =
+ this.marketplaceService.getMarketplace()
+
+ readonly altMarketplaceData$: Observable =
+ this.marketplaceService.getAltMarketplace()
+
readonly connectionFailure$ = this.connectionService
.watchFailure$()
.pipe(map(failure => failure !== ConnectionFailure.None))
@@ -62,7 +75,9 @@ export class AppShowPage {
private readonly navCtrl: NavController,
private readonly patch: PatchDbService,
private readonly connectionService: ConnectionService,
- ) { }
+ @Inject(AbstractMarketplaceService)
+ private readonly marketplaceService: MarketplaceService,
+ ) {}
isInstalled(
{ state }: PackageDataEntry,
diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts
index 6080fd7a0..37dc1047a 100644
--- a/frontend/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts
+++ b/frontend/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts
@@ -2,12 +2,19 @@ import { Inject, Pipe, PipeTransform } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { DOCUMENT } from '@angular/common'
import { AlertController, ModalController, NavController } from '@ionic/angular'
-import { MarkdownComponent } from '@start9labs/shared'
-import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
+import {
+ isValidHttpUrl,
+ MarkdownComponent,
+ removeTrailingSlash,
+} from '@start9labs/shared'
+import {
+ PackageDataEntry,
+ UIMarketplaceData,
+} from 'src/app/services/patch-db/data-model'
import { ModalService } from 'src/app/services/modal.service'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { from } from 'rxjs'
-
+import { Marketplace } from '@start9labs/marketplace'
export interface Button {
title: string
description: string
@@ -30,7 +37,11 @@ export class ToButtonsPipe implements PipeTransform {
private readonly apiService: ApiService,
) {}
- transform(pkg: PackageDataEntry): Button[] {
+ transform(
+ pkg: PackageDataEntry,
+ currentMarketplace: Marketplace | null,
+ altMarketplaces: UIMarketplaceData | null | undefined,
+ ): Button[] {
const pkgTitle = pkg.manifest.title
return [
@@ -87,7 +98,7 @@ export class ToButtonsPipe implements PipeTransform {
icon: 'receipt-outline',
},
// view in marketplace
- this.viewInMarketplaceButton(pkg),
+ this.viewInMarketplaceButton(pkg, currentMarketplace, altMarketplaces),
// donate
{
action: () => this.donate(pkg),
@@ -112,23 +123,53 @@ export class ToButtonsPipe implements PipeTransform {
await modal.present()
}
- private viewInMarketplaceButton(pkg: PackageDataEntry): Button {
- return pkg.installed?.['marketplace-url']
- ? {
- action: () =>
- this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`]),
- title: 'Marketplace',
- description: 'View service in marketplace',
- icon: 'storefront-outline',
- }
- : {
- disabled: true,
- action: () => {},
- title: 'Marketplace',
- description:
- 'This package has been side-loaded and is not available in the Start9 Marketplace',
- icon: 'storefront-outline',
+ private viewInMarketplaceButton(
+ pkg: PackageDataEntry,
+ currentMarketplace: Marketplace | null,
+ altMarketplaces: UIMarketplaceData | null | undefined,
+ ): Button {
+ const pkgMarketplace = pkg.installed?.['marketplace-url']
+ // default button if package marketplace and current marketplace are the same
+ let button: Button = {
+ title: 'Marketplace',
+ icon: 'storefront-outline',
+ action: () =>
+ this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`]),
+ disabled: false,
+ description: 'View service in marketplace',
+ }
+ if (!pkgMarketplace) {
+ button.disabled = true
+ button.description = 'This package was not installed from a marketplace.'
+ button.action = () => {}
+ } else if (
+ pkgMarketplace &&
+ currentMarketplace &&
+ removeTrailingSlash(pkgMarketplace) !==
+ removeTrailingSlash(currentMarketplace.url)
+ ) {
+ // attempt to get name for pkg marketplace
+ let pkgTitle = removeTrailingSlash(pkgMarketplace)
+ if (altMarketplaces) {
+ const nameOptions = Object.values(
+ altMarketplaces['known-hosts'],
+ ).filter(m => m.url === pkgTitle)
+ if (nameOptions.length) {
+ // if multiple of the same url exist, they will have the same name, so fine to grab first
+ pkgTitle = nameOptions[0].name
}
+ }
+ let marketplaceTitle = removeTrailingSlash(currentMarketplace.url)
+ // if we found a name for the pkg marketplace, use the name of the currently connected marketplace
+ if (!isValidHttpUrl(pkgTitle)) {
+ marketplaceTitle = currentMarketplace.name
+ }
+
+ button.action = () =>
+ this.differentMarketplaceAction(pkgTitle, marketplaceTitle)
+ button.description = 'Service was installed from a different marketplace'
+ }
+ return button
}
private async donate({ manifest }: PackageDataEntry): Promise {
@@ -143,4 +184,28 @@ export class ToButtonsPipe implements PipeTransform {
await alert.present()
}
}
+ private async differentMarketplaceAction(pkgM: string, currentM: string) {
+ const alert = await this.alertCtrl.create({
+ header: 'Marketplace Conflict',
+ message: `This service was installed from:
+
+ ${pkgM}
+
but you are currently connected to:
+ ${currentM}
+
+ To view the marketplace listing for this service, visit your Marketplace Settings and change marketplaces.`,
+ buttons: [
+ {
+ text: 'Cancel',
+ role: 'cancel',
+ },
+ {
+ text: 'Go to Settings',
+ handler: () => this.navCtrl.navigateForward(['embassy/marketplaces']),
+ cssClass: 'enter-click',
+ },
+ ],
+ })
+ await alert.present()
+ }
}
diff --git a/frontend/projects/ui/src/app/services/marketplace.service.ts b/frontend/projects/ui/src/app/services/marketplace.service.ts
index 820d6076f..89a65eb3f 100644
--- a/frontend/projects/ui/src/app/services/marketplace.service.ts
+++ b/frontend/projects/ui/src/app/services/marketplace.service.ts
@@ -92,6 +92,10 @@ export class MarketplaceService extends AbstractMarketplaceService {
return this.marketplace$
}
+ getAltMarketplace(): Observable {
+ return this.altMarketplaceData$
+ }
+
getCategories(): Observable {
return this.categories$
}
diff --git a/frontend/projects/ui/src/app/util/web.util.ts b/frontend/projects/ui/src/app/util/web.util.ts
index 484c443e9..01f808df6 100644
--- a/frontend/projects/ui/src/app/util/web.util.ts
+++ b/frontend/projects/ui/src/app/util/web.util.ts
@@ -1,6 +1,7 @@
-export async function copyToClipboard (str: string): Promise {
+export async function copyToClipboard(str: string): Promise {
if (window.isSecureContext) {
- return navigator.clipboard.writeText(str)
+ return navigator.clipboard
+ .writeText(str)
.then(() => {
return true
})