add descriptions to marketplace list page (#1812)

* add descriptions to marketplace list page

* clean up unused styling

* rip descriptions from registry marketplace, use binary choice custom default and alternative messages

* cleanup

* fix selected type and remove uneeded conditional

* conditional color

* cleanup

* better comparision of marketplace url duplicates

* add logic to handle marketplace description display based on url

* decrease font size

* abstract helper fn to get url hostname; add error toast when adding duplicate marketplace

* move helper function to more appropriate file location

* rework marketplace list and don't worry about patch db firing before bootstrapped

* remove aes-js

* reinstall aes just to please things for now

Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Lucy C
2022-09-21 12:41:19 -06:00
committed by GitHub
parent 7575e8c1de
commit f8ea2ebf62
26 changed files with 930 additions and 893 deletions

View File

@@ -2,11 +2,7 @@ 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 {
isValidHttpUrl,
MarkdownComponent,
removeTrailingSlash,
} from '@start9labs/shared'
import { getUrlHostname, MarkdownComponent } from '@start9labs/shared'
import {
DataModel,
PackageDataEntry,
@@ -144,7 +140,7 @@ export class ToButtonsPipe implements PipeTransform {
currentMarketplace: Marketplace | null,
altMarketplaces: UIMarketplaceData | null | undefined,
): Button {
const pkgMarketplace = pkg.installed?.['marketplace-url']
const pkgMarketplaceUrl = pkg.installed?.['marketplace-url']
// default button if package marketplace and current marketplace are the same
let button: Button = {
title: 'Marketplace',
@@ -154,37 +150,32 @@ export class ToButtonsPipe implements PipeTransform {
disabled: false,
description: 'View service in marketplace',
}
if (!pkgMarketplace) {
if (!pkgMarketplaceUrl) {
button.disabled = true
button.description = 'This package was not installed from a marketplace.'
button.action = () => {}
} else if (
pkgMarketplace &&
pkgMarketplaceUrl &&
currentMarketplace &&
removeTrailingSlash(pkgMarketplace) !==
removeTrailingSlash(currentMarketplace.url)
getUrlHostname(pkgMarketplaceUrl) !==
getUrlHostname(currentMarketplace.url)
) {
// attempt to get name for pkg marketplace
let pkgTitle = removeTrailingSlash(pkgMarketplace)
let pkgMarketplaceName = getUrlHostname(pkgMarketplaceUrl)
if (altMarketplaces) {
const nameOptions = Object.values(
const pkgMarketplaces = Object.values(
altMarketplaces['known-hosts'],
).filter(m => m.url === pkgTitle)
if (nameOptions.length) {
).filter(m => getUrlHostname(m.url) === pkgMarketplaceName)
if (pkgMarketplaces.length) {
// if multiple of the same url exist, they will have the same name, so fine to grab first
pkgTitle = nameOptions[0].name
pkgMarketplaceName = pkgMarketplaces[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,
pkgMarketplaceName,
currentMarketplace.name,
pkg.manifest.id,
)
button.description = 'Service was installed from a different marketplace'

View File

@@ -1,36 +0,0 @@
<h1 class="heading montserrat ion-text-center">{{ name }}</h1>
<marketplace-search [(query)]="query"></marketplace-search>
<ng-container *ngIf="pkgs && categories; else loading">
<marketplace-categories
[categories]="categories"
[category]="category"
[updatesAvailable]="(pkgs | filterPackages: '':'updates':localPkgs).length"
(categoryChange)="onCategoryChange($event)"
></marketplace-categories>
<div class="divider"></div>
<ion-grid *ngIf="pkgs | filterPackages: query:category:localPkgs as filtered">
<div *ngIf="!filtered.length && category === 'updates'" class="ion-padding">
<h1>All services are up to date!</h1>
</div>
<ion-row>
<ion-col *ngFor="let pkg of filtered" sizeXs="12" sizeSm="12" sizeMd="6">
<marketplace-item [pkg]="pkg">
<marketplace-status
class="status"
[version]="pkg.manifest.version"
[localPkg]="localPkgs[pkg.manifest.id]"
></marketplace-status>
</marketplace-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
<ng-template #loading>
<marketplace-skeleton></marketplace-skeleton>
</ng-template>

View File

@@ -1,16 +0,0 @@
.heading {
font-size: 42px;
margin: 32px 0;
}
.divider {
margin: 24px;
}
.ion-padding {
text-align: center;
}
.status {
font-size: 14px;
}

View File

@@ -1,32 +0,0 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { MarketplacePkg } from '@start9labs/marketplace'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
@Component({
selector: 'marketplace-list-content',
templateUrl: 'marketplace-list-content.component.html',
styleUrls: ['./marketplace-list-content.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MarketplaceListContentComponent {
@Input()
pkgs: MarketplacePkg[] | null = null
@Input()
localPkgs: Record<string, PackageDataEntry> = {}
@Input()
categories: Set<string> | null = null
@Input()
name = ''
category = 'featured'
query = ''
onCategoryChange(category: string): void {
this.category = category
this.query = ''
}
}

View File

@@ -14,7 +14,6 @@ import {
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { MarketplaceStatusModule } from '../marketplace-status/marketplace-status.module'
import { MarketplaceListPage } from './marketplace-list.page'
import { MarketplaceListContentComponent } from './marketplace-list-content/marketplace-list-content.component'
const routes: Routes = [
{
@@ -39,7 +38,7 @@ const routes: Routes = [
SearchModule,
SkeletonModule,
],
declarations: [MarketplaceListPage, MarketplaceListContentComponent],
exports: [MarketplaceListPage, MarketplaceListContentComponent],
declarations: [MarketplaceListPage],
exports: [MarketplaceListPage],
})
export class MarketplaceListPageModule {}

View File

@@ -7,10 +7,76 @@
</ion-header>
<ion-content class="ion-padding">
<marketplace-list-content
[localPkgs]="(localPkgs$ | async) || {}"
[pkgs]="pkgs$ | async"
[categories]="categories$ | async"
[name]="(name$ | async) || ''"
></marketplace-list-content>
<ion-grid *ngIf="details$ | async as details">
<ion-row>
<ion-col size-lg="10" offset-lg="1" size-sm="12">
<ion-item class="description" [color]="details.color">
<ion-icon
text-wrap
size="large"
name="information-circle-outline"
></ion-icon>
<ion-label [innerHtml]="details.description"></ion-label>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12">
<h1 class="heading montserrat ion-text-center">{{ details.name }}</h1>
<p style="margin-top: 0">{{ details.url }}</p>
<marketplace-search [(query)]="query"></marketplace-search>
</ion-col>
</ion-row>
<ion-row class="ion-align-items-center">
<ion-col size="12">
<ng-container *ngIf="pkgs$ | async as pkgs; else loading">
<ng-container *ngIf="localPkgs$ | async as localPkgs">
<marketplace-categories
*ngIf="categories$ | async as categories"
[categories]="categories"
[category]="category"
[updatesAvailable]="
(pkgs | filterPackages: '':'updates':localPkgs).length
"
(categoryChange)="onCategoryChange($event)"
></marketplace-categories>
<div class="divider"></div>
<ion-grid
*ngIf="pkgs | filterPackages: query:category:localPkgs as filtered"
>
<div
*ngIf="!filtered.length && category === 'updates'"
class="ion-padding"
>
<h1>All services are up to date!</h1>
</div>
<ion-row>
<ion-col
*ngFor="let pkg of filtered"
sizeXs="12"
sizeSm="12"
sizeMd="6"
>
<marketplace-item [pkg]="pkg">
<marketplace-status
class="status"
[version]="pkg.manifest.version"
[localPkg]="localPkgs[pkg.manifest.id]"
></marketplace-status>
</marketplace-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
</ng-container>
<ng-template #loading>
<marketplace-skeleton></marketplace-skeleton>
</ng-template>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>

View File

@@ -0,0 +1,32 @@
.heading {
font-size: 42px;
margin-top: 32px;
}
.divider {
margin: 24px;
}
.ion-padding {
text-align: center;
}
.status {
font-size: 14px;
}
.description {
ion-icon {
padding-right: 8px;
}
@media (min-width: 1000px) {
ion-label {
::ng-deep p {
font-size: 1.1rem;
line-height: 25px;
}
}
}
}

View File

@@ -1,31 +1,65 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { map } from 'rxjs/operators'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
import { getUrlHostname } from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import { ConnectionService } from 'src/app/services/connection.service'
import { map } from 'rxjs'
import { DataModel } from 'src/app/services/patch-db/data-model'
@Component({
selector: 'marketplace-list',
templateUrl: './marketplace-list.page.html',
templateUrl: 'marketplace-list.page.html',
styleUrls: ['./marketplace-list.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MarketplaceListPage {
readonly connected$ = this.connectionService.connected$
readonly localPkgs$ = this.patch.watch$('package-data')
readonly categories$ = this.marketplaceService.getCategories()
readonly pkgs$ = this.marketplaceService.getPackages()
readonly details$ = this.marketplaceService.getMarketplace().pipe(
map(d => {
let color: string
let description: string
switch (getUrlHostname(d.url)) {
case 'registry.start9.com':
color = 'success'
description =
'Services in this marketplace are packaged and maintained by the Start9 team. If you experience an issue or have a questions related to a service in this marketplace, one of our dedicated support staff will be happy to assist you.'
break
case 'beta-registry-0-3.start9labs.com':
color = 'primary'
description =
'Services in this marketplace are undergoing active testing and may contain bugs. <b>Install at your own risk</b>. If you discover a bug or have a suggestion for improvement, please report it to the Start9 team in our community testing channel on Matrix.'
break
case 'community.start9labs.com':
color = 'tertiary'
description =
'Services in this marketplace are packaged and maintained by members of the Start9 community. <b>Install at your own risk</b>. If you experience an issue or have a question related to a service in this marketplace, please reach out to the package developer for assistance.'
break
default:
// alt marketplace
color = 'warning'
description =
'Warning. This is an <b>Alternative</b> Marketplace. Start9 cannot verify the integrity or functionality of services in this marketplace, and they may cause harm to your system. <b>Install at your own risk</b>.'
}
readonly name$ = this.marketplaceService
.getMarketplace()
.pipe(map(({ name }) => name))
return {
...d,
color,
description,
}
}),
)
constructor(
private readonly patch: PatchDB<DataModel>,
private readonly marketplaceService: AbstractMarketplaceService,
private readonly connectionService: ConnectionService,
) {}
category = 'featured'
query = ''
onCategoryChange(category: string): void {
this.category = category
this.query = ''
}
}

View File

@@ -6,7 +6,11 @@ import {
ModalController,
} from '@ionic/angular'
import { ActionSheetButton } from '@ionic/core'
import { DestroyService, ErrorToastService } from '@start9labs/shared'
import {
DestroyService,
ErrorToastService,
getUrlHostname,
} from '@start9labs/shared'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ValueSpecObject } from 'src/app/pkg-config/config-types'
@@ -244,8 +248,8 @@ export class MarketplacesPage {
}
// no-op on duplicates
const currentUrls = this.marketplaces.map(mp => mp.url)
if (currentUrls.includes(new URL(url).hostname)) return
const currentUrls = this.marketplaces.map(mp => getUrlHostname(mp.url))
if (currentUrls.includes(getUrlHostname(url))) return
const loader = await this.loadingCtrl.create({
message: 'Validating Marketplace...',
@@ -288,8 +292,11 @@ export class MarketplacesPage {
}
// no-op on duplicates
const currentUrls = this.marketplaces.map(mp => mp.url)
if (currentUrls.includes(new URL(url).hostname)) return
const currentUrls = this.marketplaces.map(mp => getUrlHostname(mp.url))
if (currentUrls.includes(getUrlHostname(url))) {
this.errToast.present({ message: 'Marketplace already added' })
return
}
const loader = await this.loadingCtrl.create({
message: 'Validating Marketplace...',

View File

@@ -5,7 +5,7 @@ import {
PipeTransform,
} from '@angular/core'
import { PatchDB } from 'patch-db-client'
import { filter, take } from 'rxjs/operators'
import { take } from 'rxjs/operators'
import {
DataModel,
PackageMainStatus,
@@ -18,9 +18,7 @@ import { Observable } from 'rxjs'
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BackingUpComponent {
readonly pkgs$ = this.patch
.watch$('package-data')
.pipe(filter(Boolean), take(1))
readonly pkgs$ = this.patch.watch$('package-data').pipe(take(1))
readonly backupProgress$ = this.patch.watch$(
'server-info',
'status-info',

View File

@@ -10,7 +10,7 @@ import { ActivatedRoute } from '@angular/router'
import { PatchDB } from 'patch-db-client'
import { ServerNameService } from 'src/app/services/server-name.service'
import { Observable, of } from 'rxjs'
import { filter, take, tap } from 'rxjs/operators'
import { take, tap } from 'rxjs/operators'
import { isEmptyObject, ErrorToastService } from '@start9labs/shared'
import { EOSService } from 'src/app/services/eos.service'
import { LocalStorageService } from 'src/app/services/local-storage.service'
@@ -52,7 +52,6 @@ export class ServerShowPage {
this.patch
.watch$('recovered-packages')
.pipe(
filter(Boolean),
take(1),
tap(data => (this.hasRecoveredPackage = !isEmptyObject(data))),
)