feature/marketplace icons (#1921)

* add registry icons, update links, clean up code (#1913)

* add registry icons, update links, clean up code

* remove seeding of registry icon and name

* fix install wizard copy

Co-authored-by: Lucy C <12953208+elvece@users.noreply.github.com>

* remove references to bep and chime

* fix shutdown language and remove chime from initializing screen

* fix type error

Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Lucy C <12953208+elvece@users.noreply.github.com>
Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Aiden McClelland
2022-11-09 12:08:39 -07:00
parent c1ac66f6e5
commit d6bf52c11f
47 changed files with 275 additions and 162 deletions

View File

@@ -38,7 +38,7 @@ export class SnekDirective {
await loader.present()
try {
await this.embassyApi.setDbValue(
await this.embassyApi.setDbValue<number>(
['gaming', 'snake', 'high-score'],
data.highScore,
)

View File

@@ -33,7 +33,7 @@
a folder on another computer that is connected to the same network
as your Embassy. View the
<a
href="https://start9.com/latest/user-manual/backups/cifs-setup"
href="https://docs.start9.com/latest/user-manual/backups/backup-create"
target="_blank"
noreferrer
style="text-decoration: none"
@@ -109,7 +109,7 @@
a physcial drive that is plugged directly into your Embassy. View
the
<a
href="https://start9.com/latest/user-manual/backups/backups-create/#backup-using-a-physical-drive"
href="https://docs.start9.com/latest/user-manual/backups/backup-setup/backup-physical"
target="_blank"
noreferrer
style="text-decoration: none"

View File

@@ -1,6 +1,6 @@
<ion-header>
<ion-toolbar>
<ion-title>Marketplace Settings</ion-title>
<ion-title>Change Registry</ion-title>
<ion-buttons slot="end">
<ion-button (click)="dismiss()">
<ion-icon slot="icon-only" name="close"></ion-icon>
@@ -11,19 +11,20 @@
<ion-content class="ion-padding-top">
<ion-item-group *ngIf="marketplace$ | async as m">
<ion-item>
<ion-label>
Connect to a standard marketplaces or an alternative marketplace.
</ion-label>
</ion-item>
<ion-item-divider>Standard Marketplaces</ion-item-divider>
<ion-item-divider>Default Registries</ion-item-divider>
<ion-item
*ngFor="let s of m.standard"
detail="false"
[button]="s.url !== m.selected"
(click)="s.url === m.selected ? '' : presentAction(s)"
>
<ion-avatar slot="start">
<img [src]="'data:image/png;base64,' + s.icon | trustUrl" alt="" />
</ion-avatar>
<ion-label>
<h2>{{ s.name }}</h2>
<p>{{ s.url }}</p>
</ion-label>
<ion-icon
*ngIf="s.url === m.selected"
slot="end"
@@ -31,18 +32,14 @@
name="checkmark"
color="success"
></ion-icon>
<ion-label>
<h2>{{ s.name }}</h2>
<p>{{ s.url }}</p>
</ion-label>
</ion-item>
<ion-item-divider>Alt Marketplaces</ion-item-divider>
<ion-item-divider>Custom Registries</ion-item-divider>
<ion-item button detail="false" (click)="presentModalAdd()">
<ion-icon slot="start" name="add" color="dark"></ion-icon>
<ion-label>
<ion-text color="dark">
<b>Add Alt Marketplace</b>
<b>Add custom registry</b>
</ion-text>
</ion-label>
</ion-item>
@@ -53,6 +50,11 @@
[button]="a.url !== m.selected"
(click)="a.url === m.selected ? '' : presentAction(a, true)"
>
<ion-icon slot="start" name="storefront-outline"></ion-icon>
<ion-label>
<h2>{{ a.name }}</h2>
<p>{{ a.url }}</p>
</ion-label>
<ion-icon
*ngIf="a.url === m.selected"
slot="end"
@@ -60,10 +62,6 @@
name="checkmark"
color="success"
></ion-icon>
<ion-label>
<h2>{{ a.name }}</h2>
<p>{{ a.url }}</p>
</ion-label>
</ion-item>
</ion-item-group>
</ion-content>

View File

@@ -7,7 +7,10 @@ import {
} from '@ionic/angular'
import { ActionSheetButton } from '@ionic/core'
import { ErrorToastService } from '@start9labs/shared'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
import {
AbstractMarketplaceService,
StoreIdentifier,
} from '@start9labs/marketplace'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ValueSpecObject } from 'src/app/pkg-config/config-types'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
@@ -30,14 +33,14 @@ export class MarketplaceSettingsPage {
const hosts = Object.entries(m['known-hosts'])
const standard = hosts
.map(([url, name]) => {
return { url, name }
.map(([url, info]) => {
return { url, ...info }
})
.slice(0, 2) // 0 and 1 will always be prod and community
const alt = hosts
.map(([url, name]) => {
return { url, name }
.map(([url, info]) => {
return { url, ...info }
})
.slice(2) // 2 and beyond will always be alts
@@ -91,7 +94,7 @@ export class MarketplaceSettingsPage {
}
async presentAction(
{ url, name }: { url: string; name: string },
{ url, name }: { url: string; name?: string },
canDelete = false,
) {
const buttons: ActionSheetButton[] = [
@@ -108,7 +111,7 @@ export class MarketplaceSettingsPage {
text: 'Delete',
role: 'destructive',
handler: () => {
this.presentAlertDelete(url, name)
this.presentAlertDelete(url, name!)
},
})
}
@@ -146,7 +149,7 @@ export class MarketplaceSettingsPage {
url: string,
loader?: HTMLIonLoadingElement,
): Promise<void> {
const message = 'Changing Marketplace...'
const message = 'Changing Registry...'
if (!loader) {
loader = await this.loadingCtrl.create({ message })
await loader.present()
@@ -155,7 +158,7 @@ export class MarketplaceSettingsPage {
}
try {
await this.api.setDbValue(['marketplace', 'selected-url'], url)
await this.api.setDbValue<string>(['marketplace', 'selected-url'], url)
} catch (e: any) {
this.errToast.present(e)
} finally {
@@ -204,14 +207,17 @@ export class MarketplaceSettingsPage {
loader.message = 'Validating marketplace...'
await loader.present()
const { name } = await firstValueFrom(
const { name, icon } = await firstValueFrom(
this.marketplaceService.fetchInfo$(url),
)
// Save
loader.message = 'Saving...'
await this.api.setDbValue(['marketplace', 'known-hosts', url], name)
await this.api.setDbValue<StoreIdentifier>(
['marketplace', 'known-hosts', url],
{ name, icon },
)
}
private async delete(url: string): Promise<void> {
@@ -224,7 +230,7 @@ export class MarketplaceSettingsPage {
this.patch.watch$('ui', 'marketplace', 'known-hosts'),
)
const filtered = Object.keys(hosts)
const filtered: { [url: string]: StoreIdentifier } = Object.keys(hosts)
.filter(key => key !== url)
.reduce((prev, curr) => {
const name = hosts[curr]
@@ -235,7 +241,10 @@ export class MarketplaceSettingsPage {
}, {})
try {
await this.api.setDbValue(['marketplace', 'known-hosts'], filtered)
await this.api.setDbValue<{ [url: string]: StoreIdentifier }>(
['marketplace', 'known-hosts'],
filtered,
)
} catch (e: any) {
this.errToast.present(e)
} finally {
@@ -247,12 +256,12 @@ export class MarketplaceSettingsPage {
function getMarketplaceValueSpec(): ValueSpecObject {
return {
type: 'object',
name: 'Add Marketplace',
name: 'Add Custom Registry',
spec: {
url: {
type: 'string',
name: 'URL',
description: 'The fully-qualified URL of the alt marketplace.',
description: 'A fully-qualified URL of the custom registry',
nullable: false,
masked: false,
copyable: false,

View File

@@ -24,10 +24,12 @@
</p>
<h6>Highlights</h6>
<ul class="spaced-list">
<li>Kiosk mode</li>
<li>x86_64 architecture compatibility</li>
<li>Community marketplaces</li>
<li>New update all tab</li>
<li>Kiosk mode - use your Embassy with monitor, keyboard, and mouse</li>
<li>Community Registry now included in Marketplace</li>
<li>
"Updates" tab - view all service updates from all registries in one place
</li>
<li>Various UI/UX improvements</li>
<li>Various bugfixes and optimizations</li>
</ul>

View File

@@ -161,7 +161,7 @@ export class AppActionsPage {
try {
await this.embassyApi.uninstallPackage({ id: this.pkgId })
this.embassyApi
.setDbValue(['ack-instructions', this.pkgId], false)
.setDbValue<boolean>(['ack-instructions', this.pkgId], false)
.catch(e => console.error('Failed to mark instructions as unseen', e))
this.navCtrl.navigateRoot('/services')
} catch (e: any) {

View File

@@ -10,13 +10,26 @@
<p>{{ manifest.version | displayEmver }}</p>
</ion-label>
</ion-item>
<ion-item button detail="false" (click)="copy(manifest['git-hash'])">
<ion-item
*ngIf="manifest['git-hash'] as gitHash; else noHash"
button
detail="false"
(click)="copy(gitHash)"
>
<ion-label>
<h2>Git Hash</h2>
<p>{{ manifest['git-hash'] }}</p>
<p>{{ gitHash }}</p>
</ion-label>
<ion-icon slot="end" name="copy-outline"></ion-icon>
</ion-item>
<ng-template #noHash>
<ion-item>
<ion-label>
<h2>Git Hash</h2>
<p>Unknown</p>
</ion-label>
</ion-item>
</ng-template>
<ion-item button detail="false" (click)="presentModalLicense()">
<ion-label>
<h2>License</h2>

View File

@@ -109,7 +109,7 @@ export class ToButtonsPipe implements PipeTransform {
private async presentModalInstructions(pkg: PackageDataEntry) {
this.apiService
.setDbValue(['ack-instructions', pkg.manifest.id], true)
.setDbValue<boolean>(['ack-instructions', pkg.manifest.id], true)
.catch(e => console.error('Failed to mark instructions as seen', e))
const modal = await this.modalCtrl.create({
@@ -130,19 +130,19 @@ export class ToButtonsPipe implements PipeTransform {
const queryParams = url ? { url } : {}
let button: Button = {
title: 'Marketplace',
title: 'Marketplace Listing',
icon: 'storefront-outline',
action: () =>
this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`], {
queryParams,
}),
disabled: false,
description: 'View service in marketplace',
description: 'View service in the marketplace',
}
if (!url) {
button.disabled = true
button.description = 'This package was not installed from a marketplace.'
button.description = 'This package was not installed from the marketplace'
button.action = () => {}
}

View File

@@ -69,7 +69,10 @@ export class DevConfigPage {
async save() {
this.saving = true
try {
await this.api.setDbValue(['dev', this.projectId, 'config'], this.code)
await this.api.setDbValue<string>(
['dev', this.projectId, 'config'],
this.code,
)
} catch (e: any) {
this.errToast.present(e)
} finally {

View File

@@ -56,7 +56,7 @@ export class DevInstructionsPage {
async save() {
this.saving = true
try {
await this.api.setDbValue(
await this.api.setDbValue<string>(
['dev', this.projectId, 'instructions'],
this.code,
)

View File

@@ -148,7 +148,11 @@ export class DeveloperListPage {
.replace(/warning:/g, '# Optional\n warning:')
const def = { name, config, instructions: SAMPLE_INSTUCTIONS }
await this.api.setDbValue(['dev', id], def)
await this.api.setDbValue<{
name: string
config: string
instructions: string
}>(['dev', id], def)
} catch (e: any) {
this.errToast.present(e)
} finally {
@@ -184,7 +188,7 @@ export class DeveloperListPage {
await loader.present()
try {
await this.api.setDbValue(['dev', id, 'name'], newName)
await this.api.setDbValue<string>(['dev', id, 'name'], newName)
} catch (e: any) {
this.errToast.present(e)
} finally {
@@ -201,7 +205,7 @@ export class DeveloperListPage {
try {
const devDataToSave: DevData = JSON.parse(JSON.stringify(this.devData))
delete devDataToSave[id]
await this.api.setDbValue(['dev'], devDataToSave)
await this.api.setDbValue<DevData>(['dev'], devDataToSave)
} catch (e: any) {
this.errToast.present(e)
} finally {

View File

@@ -55,7 +55,7 @@ export class DeveloperMenuPage {
await loader.present()
try {
await this.api.setDbValue(
await this.api.setDbValue<BasicInfo>(
['dev', this.projectId, 'basic-info'],
basicInfo,
)

View File

@@ -151,7 +151,7 @@ export function getBasicInfoSpec(devData: DevProjectData): ConfigSpec {
type: 'string',
name: 'Support Site',
description: 'URL to the support site / channel for the project',
placeholder: 'e.g. www.start9labs.com',
placeholder: 'e.g. start9.com/support',
nullable: true,
masked: false,
copyable: false,
@@ -161,7 +161,7 @@ export function getBasicInfoSpec(devData: DevProjectData): ConfigSpec {
type: 'string',
name: 'Marketing Site',
description: 'URL to the marketing site / channel for the project',
placeholder: 'e.g. www.start9labs.com',
placeholder: 'e.g. start9.com',
nullable: true,
masked: false,
copyable: false,

View File

@@ -23,10 +23,20 @@
</ion-row>
<ion-row>
<ion-col size="12">
<h1 class="heading montserrat ion-text-center">{{ details.name }}</h1>
<div class="heading">
<img
*ngIf="details.icon; else noIcon"
[src]="'data:image/png;base64,' + details.icon | trustUrl"
alt=""
/>
<ng-template #noIcon>
<ion-icon name="storefront-outline"></ion-icon>
</ng-template>
<h1 class="montserrat ion-text-center">{{ details.name }}</h1>
</div>
<ion-button fill="clear" (click)="presentModalMarketplaceSettings()">
<ion-icon slot="start" name="repeat-outline"></ion-icon>
Switch Marketplaces
Change
</ion-button>
<marketplace-search [(query)]="query"></marketplace-search>
</ion-col>

View File

@@ -1,6 +1,15 @@
.heading {
font-size: 42px;
$icon-size: 64px;
margin-top: 32px;
img {
max-width: $icon-size;
}
h1 {
font-size: 42px;
}
ion-icon {
font-size: $icon-size;
}
}
.divider {

View File

@@ -30,35 +30,36 @@ export class MarketplaceListPage {
readonly localPkgs$ = this.patch.watch$('package-data')
readonly details$ = this.marketplaceService.getSelectedHost$().pipe(
map(({ url, name }) => {
map(({ url, name, icon }) => {
let color: string
let description: string
switch (url) {
case 'https://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.'
'Services from this registry are packaged and maintained by the Start9 team. If you experience an issue or have a questions related to a service from this registry, one of our dedicated support staff will be happy to assist you.'
break
case 'https://beta-registry-0-3.start9labs.com/':
case 'https://beta-registry.start9.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.'
'Services from this registry 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 'https://community.start9labs.com/':
case 'https://community-registry.start9.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.'
'Services from this registry 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>.'
'Warning. This is a <b>Custom</b> Registry. Start9 cannot verify the integrity or functionality of services from this registry, and they may cause harm to your system. <b>Install at your own risk</b>.'
}
return {
name,
url,
icon,
color,
description,
}

View File

@@ -99,11 +99,12 @@ export class MarketplaceShowControlsComponent {
this.patch.watch$('ui', 'marketplace'),
)
const name = marketplaces['known-hosts'][url] || url
const name: string = marketplaces['known-hosts'][url]?.name || url
let originalName: string | undefined
if (originalUrl) {
originalName = marketplaces['known-hosts'][originalUrl] || originalUrl
originalName =
marketplaces['known-hosts'][originalUrl]?.name || originalUrl
}
return new Promise(async resolve => {

View File

@@ -27,7 +27,7 @@
</ol>
View the full
<a
href="https://start9.com/latest/user-manual/connecting/connecting-lan"
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
target="_blank"
rel="noreferrer"
>instructions</a

View File

@@ -178,7 +178,7 @@ export class ServerShowPage {
async presentAlertRepairDisk() {
const alert = await this.alertCtrl.create({
header: 'Warning',
message: `<p>This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.</p><p>If anything happens to the device during the reboot (between the bep and chime), such as losing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>`,
message: `<p>This action will attempt to preform a disk repair operation and system reboot. No data will be deleted. This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.</p><p>If anything happens to the device during the reboot, such as losing power, a power surge, unplugging the drive, or unplugging the Embassy, the filesystem <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>`,
buttons: [
{
text: 'Cancel',
@@ -223,7 +223,7 @@ export class ServerShowPage {
await loader.present()
try {
await this.embassyApi.setDbValue([key], value)
await this.embassyApi.setDbValue<string>([key], value)
} finally {
loader.dismiss()
}
@@ -333,7 +333,7 @@ export class ServerShowPage {
private async presentAlertInProgress(verb: string, message: string) {
const alert = await this.alertCtrl.create({
header: `${verb} In Progress...`,
message: `Stopping all services gracefully. This can take a while.<br /><br />Your Embassy will then <b>♫ play a melody ♫</b> and become unreachable${message}`,
message: `Stopping all services gracefully. This can take a while.<br /><br />If you have a speaker, your Embassy will <b>♫ play a melody ♫</b> before shutting down. Your Embassy will then become unreachable${message}`,
buttons: [
{
text: 'OK',
@@ -484,7 +484,7 @@ export class ServerShowPage {
icon: 'map-outline',
action: () =>
window.open(
'https://start9.com/latest/user-manual/',
'https://docs.start9.com/latest/user-manual',
'_blank',
'noreferrer',
),
@@ -497,7 +497,7 @@ export class ServerShowPage {
icon: 'chatbubbles-outline',
action: () =>
window.open(
'https://start9.com/latest/support/contact/',
'https://docs.start9.com/latest/support/contact',
'_blank',
'noreferrer',
),

View File

@@ -20,7 +20,7 @@ import {
export class SSHKeysPage {
loading = true
sshKeys: SSHKey[] = []
readonly docsUrl = 'https://start9.com/latest/user-manual/ssh'
readonly docsUrl = 'https://docs.start9.com/latest/user-manual/ssh'
constructor(
private readonly loadingCtrl: LoadingController,

View File

@@ -23,7 +23,7 @@
Ethernet cable and move the device anywhere you want. Embassy will
automatically connect to available networks.
<a
href="https://start9.com/latest/user-manual/wifi"
href="https://docs.start9.com/latest/user-manual/wifi"
target="_blank"
rel="noreferrer"
>View instructions</a

View File

@@ -10,7 +10,14 @@
<ion-content class="ion-padding">
<ion-item-group *ngIf="data$ | async as data">
<ng-container *ngFor="let host of data.hosts | keyvalue">
<ion-item-divider> {{ host.value }} </ion-item-divider>
<ion-item-divider>
{{ host.value.name }} &nbsp;
<img
style="max-width: 24px"
[src]="'data:image/png;base64,' + host.value.icon | trustUrl"
alt=""
/>
</ion-item-divider>
<div class="ion-padding-start ion-padding-bottom">
<ion-item *ngIf="data.errors.includes(host.key)">
@@ -25,7 +32,7 @@
>
<ion-item *ngFor="let pkg of updates">
<ng-container *ngIf="data.localPkgs[pkg.manifest.id] as local">
<ion-avatar slot="start">
<ion-avatar slot="start" class="service-avatar">
<img [src]="'data:image/png;base64,' + pkg.icon | trustUrl" />
</ion-avatar>
<ion-label>

View File

@@ -1,4 +1,4 @@
ion-avatar {
.service-avatar {
position: absolute;
top: 6px;
}

View File

@@ -12,6 +12,7 @@ import {
Marketplace,
MarketplaceManifest,
MarketplacePkg,
StoreIdentifier,
} from '@start9labs/marketplace'
import { Emver } from '@start9labs/shared'
import { Pipe, PipeTransform } from '@angular/core'
@@ -19,7 +20,7 @@ import { combineLatest, Observable } from 'rxjs'
import { PrimaryRendering } from '../../services/pkg-status-rendering.service'
interface UpdatesData {
hosts: Record<string, string>
hosts: Record<string, StoreIdentifier>
marketplace: Marketplace
localPkgs: Record<string, PackageDataEntry>
errors: string[]

File diff suppressed because one or more lines are too long

View File

@@ -348,7 +348,6 @@ export module Mock {
id: 'lnd',
title: 'Lightning Network Daemon',
version: '0.11.1',
'git-hash': 'lalalalalala',
description: {
short: 'A bolt spec compliant client.',
long: 'More info about LND. More info about LND. More info about LND.',

View File

@@ -16,7 +16,7 @@ export module RR {
export type GetDumpRes = Dump<DataModel>
export type SetDBValueReq = { pointer: string; value: any } // db.put.ui
export type SetDBValueReq<T> = { pointer: string; value: T } // db.put.ui
export type SetDBValueRes = null
// auth

View File

@@ -18,9 +18,9 @@ export abstract class ApiService {
// db
abstract setDbValue(
abstract setDbValue<T>(
pathArr: Array<string | number>,
value: any,
value: T,
): Promise<RR.SetDBValueRes>
// auth

View File

@@ -54,12 +54,12 @@ export class LiveApiService extends ApiService {
// db
async setDbValue(
async setDbValue<T>(
pathArr: Array<string | number>,
value: any,
value: T,
): Promise<RR.SetDBValueRes> {
const pointer = pathFromArray(pathArr)
const params: RR.SetDBValueReq = { pointer, value }
const params: RR.SetDBValueReq<T> = { pointer, value }
return this.rpcRequest({ method: 'db.put.ui', params })
}

View File

@@ -37,6 +37,8 @@ import { mockPatchData } from './mock-patch'
import { WebSocketSubjectConfig } from 'rxjs/webSocket'
import { AuthService } from '../auth.service'
import { ConnectionService } from '../connection.service'
import { StoreInfo } from '@start9labs/marketplace'
import { COMMUNITY_REGISTRY, START9_REGISTRY } from './api-icons'
const PROGRESS: InstallProgress = {
size: 120,
@@ -93,12 +95,12 @@ export class MockApiService extends ApiService {
// db
async setDbValue(
async setDbValue<T>(
pathArr: Array<string | number>,
value: any,
value: T,
): Promise<RR.SetDBValueRes> {
const pointer = pathFromArray(pathArr)
const params: RR.SetDBValueReq = { pointer, value }
const params: RR.SetDBValueReq<T> = { pointer, value }
await pauseFor(2000)
const patch = [
{
@@ -280,8 +282,9 @@ export class MockApiService extends ApiService {
await pauseFor(2000)
if (path === '/package/v0/info') {
return {
name: 'Dark69',
const info: StoreInfo = {
name: 'Start9 Registry',
icon: START9_REGISTRY,
categories: [
'bitcoin',
'lightning',
@@ -292,6 +295,7 @@ export class MockApiService extends ApiService {
'alt coin',
],
}
return info
} else if (path === '/package/v0/index') {
return Mock.MarketplacePkgsList
} else if (path.startsWith('/package/v0/release-notes')) {

View File

@@ -7,6 +7,7 @@ import {
PackageMainStatus,
PackageState,
} from 'src/app/services/patch-db/data-model'
import { COMMUNITY_REGISTRY, START9_REGISTRY } from './api-icons'
import { Mock } from './api.fixures'
export const mockPatchData: DataModel = {
@@ -17,9 +18,16 @@ export const mockPatchData: DataModel = {
marketplace: {
'selected-url': 'https://registry.start9.com/',
'known-hosts': {
'https://registry.start9.com/': 'Start9 Marketplace',
'https://community-registry.start9.com/': 'Community Marketplace',
'https://dark9-marketplace.com/': 'Dark9',
'https://registry.start9.com/': {
name: 'Start9 Registry',
icon: START9_REGISTRY,
},
'https://community-registry.start9.com/': {
icon: COMMUNITY_REGISTRY,
},
'https://dark9-marketplace.com/': {
name: 'Dark9',
},
},
},
dev: {},
@@ -453,7 +461,6 @@ export const mockPatchData: DataModel = {
id: 'lnd',
title: 'Lightning Network Daemon',
version: '0.11.1',
'git-hash': 'lalalalalala',
description: {
short: 'A bolt spec compliant client.',
long: 'More info about LND. More info about LND. More info about LND.',

View File

@@ -19,7 +19,7 @@ const {
})
export class ConfigService {
origin = removePort(removeProtocol(window.origin))
version = require('../../../../../package.json').version
version = require('../../../../../package.json').version as string
useMocks = useMocks
mocks = mocks
targetArch = targetArch

View File

@@ -5,6 +5,7 @@ import {
StoreData,
Marketplace,
StoreInfo,
StoreIdentifier,
} from '@start9labs/marketplace'
import {
BehaviorSubject,
@@ -28,7 +29,6 @@ import {
shareReplay,
startWith,
switchMap,
tap,
} from 'rxjs/operators'
import { getNewEntries } from '@start9labs/shared'
@@ -44,19 +44,19 @@ export class MarketplaceService implements AbstractMarketplaceService {
distinctUntilKeyChanged('selected-url'),
map(data => ({
url: data['selected-url'],
name: data['known-hosts'][data['selected-url']],
...data['known-hosts'][data['selected-url']],
})),
shareReplay(1),
)
private readonly marketplace$ = this.knownHosts$.pipe(
startWith<Record<string, string>>({}),
startWith<Record<string, StoreIdentifier>>({}),
pairwise(),
mergeMap(([prev, curr]) => from(Object.entries(getNewEntries(prev, curr)))),
mergeMap(([url, name]) =>
mergeMap(([url, registry]) =>
this.fetchStore$(url).pipe(
map<StoreData, [string, StoreData | null]>(data => {
if (data.info) this.updateName(url, name, data.info.name)
if (data.info) this.updateStoreIdentifier(url, registry, data.info)
return [url, data]
}),
@@ -85,11 +85,11 @@ export class MarketplaceService implements AbstractMarketplaceService {
private readonly patch: PatchDB<DataModel>,
) {}
getKnownHosts$(): Observable<Record<string, string>> {
getKnownHosts$(): Observable<Record<string, StoreIdentifier>> {
return this.knownHosts$
}
getSelectedHost$(): Observable<{ url: string; name: string }> {
getSelectedHost$(): Observable<StoreIdentifier & { url: string }> {
return this.selectedHost$
}
@@ -233,13 +233,16 @@ export class MarketplaceService implements AbstractMarketplaceService {
)
}
private async updateName(
private async updateStoreIdentifier(
url: string,
name: string,
newName: string,
oldInfo: StoreIdentifier,
newInfo: StoreIdentifier,
): Promise<void> {
if (name !== newName) {
this.api.setDbValue(['marketplace', 'known-hosts', url], newName)
if (oldInfo.name !== newInfo.name || oldInfo.icon !== newInfo.icon) {
this.api.setDbValue<StoreIdentifier>(
['marketplace', 'known-hosts', url],
newInfo,
)
}
}
}

View File

@@ -59,7 +59,9 @@ export class PatchDataService extends Observable<DataModel> {
backdropDismiss: false,
})
modal.onWillDismiss().then(() => {
this.embassyApi.setDbValue(['ack-welcome'], this.config.version).catch()
this.embassyApi
.setDbValue<string>(['ack-welcome'], this.config.version)
.catch()
})
await modal.present()

View File

@@ -1,6 +1,6 @@
import { ConfigSpec } from 'src/app/pkg-config/config-types'
import { Url } from '@start9labs/shared'
import { MarketplaceManifest } from '@start9labs/marketplace'
import { MarketplaceManifest, StoreIdentifier } from '@start9labs/marketplace'
import { BasicInfo } from 'src/app/pages/developer-routes/developer-menu/form-info'
export interface DataModel {
@@ -26,9 +26,9 @@ export interface UIData {
export interface UIMarketplaceData {
'selected-url': string
'known-hosts': {
'https://registry.start9.com/': string
'https://community-registry.start9.com/': string
[url: string]: string
'https://registry.start9.com/': StoreIdentifier
'https://community-registry.start9.com/': StoreIdentifier
[url: string]: StoreIdentifier
}
}