mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
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:
@@ -4,8 +4,8 @@
|
||||
"marketplace": {
|
||||
"selected-url": "https://registry.start9.com/",
|
||||
"known-hosts": {
|
||||
"https://registry.start9.com/": "Start9 Marketplace",
|
||||
"https://community-registry.start9.com/": "Community Marketplace"
|
||||
"https://registry.start9.com/": {},
|
||||
"https://community-registry.start9.com/": {}
|
||||
}
|
||||
},
|
||||
"dev": {},
|
||||
|
||||
@@ -60,7 +60,7 @@ export class HomePage {
|
||||
code: 2,
|
||||
problem: 'Filesystem I/O error.',
|
||||
solution:
|
||||
'Repairing the disk could help resolve this issue. This will occur on a restart between the bep and chime. Please DO NOT unplug the drive or Embassy during this time or the situation will become worse.',
|
||||
'Repairing the disk could help resolve this issue. Please DO NOT unplug the drive or Embassy during this time or the situation will become worse.',
|
||||
details: error.data?.details,
|
||||
}
|
||||
// disk management error - disk needs repair
|
||||
@@ -69,7 +69,7 @@ export class HomePage {
|
||||
code: 48,
|
||||
problem: 'Disk management error.',
|
||||
solution:
|
||||
'Repairing the disk could help resolve this issue. This will occur on a restart between the bep and chime. Please DO NOT unplug the drive or Embassy during this time or the situation will become worse.',
|
||||
'Repairing the disk could help resolve this issue. Please DO NOT unplug the drive or Embassy during this time or the situation will become worse.',
|
||||
details: error.data?.details,
|
||||
}
|
||||
} else {
|
||||
@@ -139,7 +139,7 @@ export class HomePage {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
message:
|
||||
'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. 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 *will* be in an unrecoverable state. Please proceed with caution.',
|
||||
'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. 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 *will* be in an unrecoverable state. Please proceed with caution.',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
|
||||
@@ -43,15 +43,21 @@
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
<ng-template swiperSlide>
|
||||
<ion-item button (click)="tryInstall(false)">
|
||||
<ion-item
|
||||
*ngIf="selectedDisk?.['embassy-data']"
|
||||
button
|
||||
(click)="tryInstall(false)"
|
||||
>
|
||||
<ion-icon
|
||||
color="dark"
|
||||
slot="start"
|
||||
name="medkit-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h2><ion-text color="success">Light Install</ion-text></h2>
|
||||
<p>Reinstall embassyOS but keep your existing data</p>
|
||||
<h2>
|
||||
<ion-text color="success">Re-Install embassyOS</ion-text>
|
||||
</h2>
|
||||
<h4>Will preserve existing embassyOS data</h4>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button lines="none" (click)="tryInstall(true)">
|
||||
@@ -61,10 +67,14 @@
|
||||
name="download-outline"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h2><ion-text color="warning">Full Install</ion-text></h2>
|
||||
<p>
|
||||
Install embassyOS and delete all existing data on disk
|
||||
</p>
|
||||
<h2>
|
||||
<ion-text
|
||||
[color]="selectedDisk?.['embassy-data'] ? 'danger' : 'success'"
|
||||
>{{ selectedDisk?.['embassy-data'] ? 'Factory Reset' :
|
||||
'Install embassyOS' }}</ion-text
|
||||
>
|
||||
</h2>
|
||||
<h4>Will delete existing data on disk</h4>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
|
||||
@@ -75,8 +75,8 @@ export class HomePage {
|
||||
|
||||
private async presentAlertDanger(logicalname: string, embassyData: boolean) {
|
||||
const message = embassyData
|
||||
? 'This action COMPLETELY erases your existing Embassy data'
|
||||
: `This action COMPLETELY erases the disk ${logicalname} and installs embassyOS`
|
||||
? 'This action will COMPLETELY erase your existing Embassy data'
|
||||
: `This action will COMPLETELY erase the disk <b>${logicalname}</b> and install embassyOS!`
|
||||
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
@@ -93,7 +93,7 @@ export class HomePage {
|
||||
},
|
||||
},
|
||||
],
|
||||
cssClass: 'alert-warning-message',
|
||||
cssClass: 'alert-danger-message',
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export class HomePage {
|
||||
private async presentAlertReboot() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Install Success',
|
||||
message: 'Reboot your device to begin using your new Emabssy',
|
||||
message: 'Reboot your device to begin using your new Embassy',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Reboot',
|
||||
@@ -110,7 +110,7 @@ export class HomePage {
|
||||
},
|
||||
},
|
||||
],
|
||||
cssClass: 'alert-warning-message',
|
||||
cssClass: 'alert-success-message',
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
@@ -134,7 +134,6 @@ export class HomePage {
|
||||
header: 'Rebooting',
|
||||
message: 'Please wait for embassyOS to restart, then refresh this page',
|
||||
buttons: ['OK'],
|
||||
cssClass: 'alert-warning-message',
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
@@ -39,3 +39,21 @@
|
||||
--spinner-color: var(--ion-color-warning) !important;
|
||||
z-index: 40000 !important;
|
||||
}
|
||||
|
||||
.alert-danger-message {
|
||||
.alert-title {
|
||||
color: var(--ion-color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.alert-success-message {
|
||||
.alert-title {
|
||||
color: var(--ion-color-success);
|
||||
}
|
||||
}
|
||||
|
||||
ion-alert {
|
||||
.alert-button {
|
||||
color: var(--ion-color-dark) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,26 @@
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<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)="presentAlertVersions()">
|
||||
<ion-label>
|
||||
<h2>Other Versions</h2>
|
||||
|
||||
@@ -2,15 +2,15 @@ import { Observable } from 'rxjs'
|
||||
import {
|
||||
MarketplacePkg,
|
||||
Marketplace,
|
||||
MarketplaceURL,
|
||||
MarketplaceName,
|
||||
StoreURL,
|
||||
StoreData,
|
||||
StoreIdentifier,
|
||||
} from '../types'
|
||||
|
||||
export abstract class AbstractMarketplaceService {
|
||||
abstract getKnownHosts$(): Observable<Record<MarketplaceURL, MarketplaceName>>
|
||||
abstract getKnownHosts$(): Observable<Record<StoreURL, StoreIdentifier>>
|
||||
|
||||
abstract getSelectedHost$(): Observable<{ url: string; name: string }>
|
||||
abstract getSelectedHost$(): Observable<StoreIdentifier & { url: string }>
|
||||
|
||||
abstract getMarketplace$(): Observable<Marketplace>
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { Url } from '@start9labs/shared'
|
||||
|
||||
export type MarketplaceURL = string
|
||||
export type StoreURL = string
|
||||
export type StoreName = string
|
||||
export type StoreIcon = string // base64
|
||||
|
||||
export type MarketplaceName = string
|
||||
export interface StoreIdentifier {
|
||||
name?: StoreName
|
||||
icon?: StoreIcon // base64
|
||||
}
|
||||
|
||||
export type Marketplace = Record<MarketplaceURL, StoreData | null>
|
||||
export type Marketplace = Record<StoreURL, StoreData | null>
|
||||
|
||||
export interface StoreData {
|
||||
info: StoreInfo
|
||||
@@ -12,7 +17,8 @@ export interface StoreData {
|
||||
}
|
||||
|
||||
export interface StoreInfo {
|
||||
name: MarketplaceName
|
||||
name: StoreName
|
||||
icon?: StoreIcon
|
||||
categories: string[]
|
||||
}
|
||||
|
||||
@@ -36,7 +42,7 @@ export interface MarketplaceManifest<T = unknown> {
|
||||
id: string
|
||||
title: string
|
||||
version: string
|
||||
'git-hash': string
|
||||
'git-hash'?: string
|
||||
description: {
|
||||
short: string
|
||||
long: string
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
can bypass this warning on most browsers. The warning will go
|
||||
away after you
|
||||
<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"
|
||||
class="inline"
|
||||
@@ -138,7 +138,7 @@
|
||||
<b>Important!</b>
|
||||
This address will only work from a
|
||||
<a
|
||||
href="https://start9.com/latest/user-manual/connecting/connecting-tor"
|
||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="inline"
|
||||
@@ -185,9 +185,9 @@
|
||||
any Tor-enabled browser.
|
||||
</p>
|
||||
<p>
|
||||
For a list of recommended browsers, click
|
||||
For more detailed instructions, click
|
||||
<a
|
||||
href="https://start9.com/latest/user-manual/connecting/connecting-tor"
|
||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
><b>here</b></a
|
||||
@@ -213,7 +213,7 @@
|
||||
<p>
|
||||
For step-by-step instructions, click
|
||||
<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"
|
||||
><b>here</b></a
|
||||
|
||||
@@ -47,7 +47,7 @@ export function getErrorMessage(
|
||||
): string | IonicSafeString {
|
||||
if (!message) {
|
||||
message = 'Unknown Error.'
|
||||
link = 'https://start9.com/latest/support/FAQ'
|
||||
link = 'https://docs.start9.com/latest/support/faq'
|
||||
}
|
||||
|
||||
if (link) {
|
||||
|
||||
@@ -32,19 +32,8 @@ export type RPCResponse<T> = RPCSuccessRes<T> | RPCErrorRes
|
||||
|
||||
export interface RPCOptions {
|
||||
method: string
|
||||
headers?: {
|
||||
[header: string]: string | string[]
|
||||
}
|
||||
params: {
|
||||
[param: string]:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| object
|
||||
| string[]
|
||||
| number[]
|
||||
| null
|
||||
}
|
||||
headers?: Record<string, string | string[]>
|
||||
params: Record<string, any>
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = () => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
<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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ion-avatar {
|
||||
.service-avatar {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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.',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,5 @@
|
||||
<body>
|
||||
<h2 class="header-title"><span>embassyOS Initializing</span></h2>
|
||||
<p style="padding: 20px;">This process can take up to several minutes to complete.</p>
|
||||
<p>Please wait to refresh the page until you hear a chime from your device.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user