diff --git a/web/package.json b/web/package.json index 2f91927e6..92c8870e1 100644 --- a/web/package.json +++ b/web/package.json @@ -13,6 +13,7 @@ "check:setup": "tsc --project projects/setup-wizard/tsconfig.json --noEmit --skipLibCheck", "check:ui": "tsc --project projects/ui/tsconfig.json --noEmit --skipLibCheck", "build:deps": "rm -rf .angular/cache && (cd ../patch-db/client && npm ci && npm run build) && (cd ../sdk && make bundle)", + "build:deps:win": "rimraf .angular/cache && (cd ../sdk && npm ci && npm run build) && (cd ../patch-db/client && npm ci && npm run build)", "build:install": "ng run install-wizard:build", "build:setup": "ng run setup-wizard:build", "build:ui": "ng run ui:build", diff --git a/web/projects/marketplace/package.json b/web/projects/marketplace/package.json index ef061a11e..ceef034c1 100644 --- a/web/projects/marketplace/package.json +++ b/web/projects/marketplace/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/marketplace", - "version": "0.3.32", + "version": "0.3.36", "peerDependencies": { "@angular/common": ">=13.2.0", "@angular/core": ">=13.2.0", diff --git a/web/projects/marketplace/src/components/menu/menu.component.html b/web/projects/marketplace/src/components/menu/menu.component.html index 45498ba50..964551a85 100644 --- a/web/projects/marketplace/src/components/menu/menu.component.html +++ b/web/projects/marketplace/src/components/menu/menu.component.html @@ -1,113 +1,108 @@
- -
- + +

+ {{ registry?.info?.name || 'Unnamed Registry' }} +

+ + +
+ + +
diff --git a/web/projects/marketplace/src/components/menu/menu.component.ts b/web/projects/marketplace/src/components/menu/menu.component.ts index db486eae5..bcf242935 100644 --- a/web/projects/marketplace/src/components/menu/menu.component.ts +++ b/web/projects/marketplace/src/components/menu/menu.component.ts @@ -5,11 +5,10 @@ import { Input, OnDestroy, } from '@angular/core' -import { combineLatest, map, Subject, takeUntil } from 'rxjs' -import { StoreIdentity } from '../../types' -import { AbstractMarketplaceService } from '../../services/marketplace.service' -import { AbstractCategoryService } from '../../services/category.service' import { MarketplaceConfig } from '@start9labs/shared' +import { Subject, takeUntil } from 'rxjs' +import { AbstractCategoryService } from '../../services/category.service' +import { StoreDataWithUrl } from '../../types' @Component({ selector: 'menu', @@ -21,19 +20,11 @@ export class MenuComponent implements OnDestroy { @Input({ required: true }) iconConfig!: MarketplaceConfig + @Input({ required: true }) + registry!: StoreDataWithUrl | null + private destroy$ = new Subject() - private readonly marketplaceService = inject(AbstractMarketplaceService) private readonly categoryService = inject(AbstractCategoryService) - readonly store$ = this.marketplaceService.getSelectedStoreWithCategories$() - readonly alt$ = combineLatest([ - this.marketplaceService.getKnownHosts$(), - this.marketplaceService.getSelectedHost$(), - ]).pipe( - map(([stores, selected]) => - stores.filter(({ url }) => url != selected.url), - ), - ) - private hosts?: StoreIdentity[] category = '' query = '' open = false @@ -52,13 +43,6 @@ export class MenuComponent implements OnDestroy { .subscribe(val => { this.category = val }) - - this.marketplaceService - .getKnownHosts$() - .pipe(takeUntil(this.destroy$)) - .subscribe(hosts => { - this.hosts = hosts - }) } onCategoryChange(category: string): void { @@ -66,7 +50,6 @@ export class MenuComponent implements OnDestroy { this.query = '' this.categoryService.resetQuery() this.categoryService.changeCategory(category) - this.categoryService.handleNavigation() } onQueryChange(query: string): void { diff --git a/web/projects/marketplace/src/modals/release-notes.component.ts b/web/projects/marketplace/src/modals/release-notes.component.ts index 467b37660..7c805d36a 100644 --- a/web/projects/marketplace/src/modals/release-notes.component.ts +++ b/web/projects/marketplace/src/modals/release-notes.component.ts @@ -1,6 +1,5 @@ import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { MarketplacePkg } from '../../src/types' import { Exver, MarkdownPipeModule } from '@start9labs/shared' import { TuiButton, TuiDialogContext, TuiLoader } from '@taiga-ui/core' import { TuiAccordion } from '@taiga-ui/kit' @@ -8,26 +7,21 @@ import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, } from '@taiga-ui/polymorpheus' -import { map } from 'rxjs' -import { AbstractMarketplaceService } from '../services/marketplace.service' +import { MarketplacePkg } from '../../src/types' @Component({ standalone: true, template: ` - @if (notes$ | async; as notes) { - - @for (note of notes | keyvalue: asIsOrder; track $index) { - - {{ note.key }} - -
-
-
- } -
- } @else { - - } + + @for (note of notes | keyvalue: asIsOrder; track $index) { + + {{ note.key }} + +
+
+
+ } +
`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ @@ -43,26 +37,20 @@ export class ReleaseNotesComponent { private readonly pkg = inject>(POLYMORPHEUS_CONTEXT).data - readonly notes$ = inject(AbstractMarketplaceService) - .getSelectedStore$() - .pipe( - map(s => { - return Object.entries(this.pkg.otherVersions) - .filter( - ([v, _]) => - this.exver.getFlavor(v) === this.pkg.flavor && - this.exver.compareExver(this.pkg.version, v) === 1, - ) - .reduce( - (obj, [version, info]) => ({ - ...obj, - [version]: info.releaseNotes, - }), - { - [`${this.pkg.version} (current)`]: this.pkg.releaseNotes, - }, - ) + readonly notes = Object.entries(this.pkg.otherVersions) + .filter( + ([v, _]) => + this.exver.getFlavor(v) === this.pkg.flavor && + this.exver.compareExver(this.pkg.version, v) === 1, + ) + .reduce( + (obj, [version, info]) => ({ + ...obj, + [version]: info.releaseNotes, }), + { + [`${this.pkg.version} (current)`]: this.pkg.releaseNotes, + }, ) asIsOrder(a: any, b: any) { diff --git a/web/projects/marketplace/src/pages/show/additional/additional.component.html b/web/projects/marketplace/src/pages/show/additional/additional.component.html index 389ed2586..f4381eed6 100644 --- a/web/projects/marketplace/src/pages/show/additional/additional.component.html +++ b/web/projects/marketplace/src/pages/show/additional/additional.component.html @@ -28,7 +28,7 @@

Information

Information /> () constructor( readonly copyService: CopyService, @@ -30,19 +30,4 @@ export class AdditionalComponent { ) {} readonly url = this.route.snapshot.queryParamMap.get('url') || undefined - - presentModalMd(label: string) { - this.dialogs - .open(new PolymorpheusComponent(MarkdownComponent), { - label, - size: 'l', - data: { - content: this.marketplaceService.fetchStatic$( - this.pkg, - label === 'License' ? 'LICENSE.md' : 'instructions.md', - ), - }, - }) - .subscribe() - } } diff --git a/web/projects/marketplace/src/pages/show/flavors/flavors.component.ts b/web/projects/marketplace/src/pages/show/flavors/flavors.component.ts index ded119f6f..ba8feadca 100644 --- a/web/projects/marketplace/src/pages/show/flavors/flavors.component.ts +++ b/web/projects/marketplace/src/pages/show/flavors/flavors.component.ts @@ -18,6 +18,7 @@ import { MarketplacePkg } from '../../../types' tuiCell [routerLink]="[]" [queryParams]="{ id: pkg.id, flavor: pkg.flavor }" + queryParamsHandling="merge" > diff --git a/web/projects/marketplace/src/public-api.ts b/web/projects/marketplace/src/public-api.ts index 1a2a3e42d..d3b2582ce 100644 --- a/web/projects/marketplace/src/public-api.ts +++ b/web/projects/marketplace/src/public-api.ts @@ -29,7 +29,6 @@ export * from './components/menu/menu.component.module' export * from './components/menu/menu.component' export * from './components/registry.component' -export * from './services/marketplace.service' export * from './services/category.service' export * from './types' diff --git a/web/projects/marketplace/src/services/marketplace.service.ts b/web/projects/marketplace/src/services/marketplace.service.ts deleted file mode 100644 index fb5225c93..000000000 --- a/web/projects/marketplace/src/services/marketplace.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Observable } from 'rxjs' -import { Marketplace, MarketplacePkg, StoreData, StoreIdentity } from '../types' - -export abstract class AbstractMarketplaceService { - abstract getKnownHosts$(): Observable - - abstract getSelectedHost$(): Observable - - abstract getMarketplace$(): Observable - - abstract getSelectedStore$(): Observable - - abstract getSelectedStoreWithCategories$(): Observable< - StoreIdentity & StoreData - > - - abstract getPackage$( - id: string, - version: string | null, - flavor: string | null, - url?: string, - ): Observable - - abstract fetchStatic$( - pkg: MarketplacePkg, - type: 'LICENSE.md' | 'instructions.md', - ): Observable -} diff --git a/web/projects/marketplace/src/types.ts b/web/projects/marketplace/src/types.ts index 9853cddbb..e5022cc68 100644 --- a/web/projects/marketplace/src/types.ts +++ b/web/projects/marketplace/src/types.ts @@ -37,3 +37,5 @@ export type MarketplacePkg = T.PackageVersionInfo & version: string flavor: string | null } + +export type StoreDataWithUrl = StoreData & { url: string } diff --git a/web/projects/ui/src/app/app.providers.ts b/web/projects/ui/src/app/app.providers.ts index 6389eb76d..5a1679db3 100644 --- a/web/projects/ui/src/app/app.providers.ts +++ b/web/projects/ui/src/app/app.providers.ts @@ -3,7 +3,6 @@ import { UntypedFormBuilder } from '@angular/forms' import { Router } from '@angular/router' import { AbstractCategoryService, - AbstractMarketplaceService, FilterPackagesPipe, } from '@start9labs/marketplace' import { RELATIVE_URL, THEME, WorkspaceConfig } from '@start9labs/shared' @@ -35,7 +34,6 @@ import { CategoryService } from './services/category.service' import { ClientStorageService } from './services/client-storage.service' import { DateTransformerService } from './services/date-transformer.service' import { DatetimeTransformerService } from './services/datetime-transformer.service' -import { MarketplaceService } from './services/marketplace.service' import { StorageService } from './services/storage.service' import { ThemeSwitcherService } from './services/theme-switcher.service' @@ -90,10 +88,6 @@ export const APP_PROVIDERS: Provider[] = [ provide: THEME, useExisting: ThemeSwitcherService, }, - { - provide: AbstractMarketplaceService, - useClass: MarketplaceService, - }, { provide: AbstractCategoryService, useClass: CategoryService, diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts index 55712ce20..430cb4297 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/controls.component.ts @@ -7,10 +7,7 @@ import { Input, } from '@angular/core' import { Router } from '@angular/router' -import { - AbstractMarketplaceService, - MarketplacePkg, -} from '@start9labs/marketplace' +import { MarketplacePkg } from '@start9labs/marketplace' import { Exver, ErrorService, @@ -106,12 +103,7 @@ export class MarketplaceControlsComponent { private readonly loader = inject(LoadingService) private readonly exver = inject(Exver) private readonly router = inject(Router) - private readonly marketplace = inject( - AbstractMarketplaceService, - ) as MarketplaceService - - @Input() - url?: string + private readonly marketplaceService = inject(MarketplaceService) @Input({ required: true }) pkg!: MarketplacePkg @@ -125,19 +117,19 @@ export class MarketplaceControlsComponent { readonly showDevTools$ = inject(ClientStorageService).showDevTools$ async tryInstall() { - const current = await firstValueFrom(this.marketplace.getSelectedHost$()) - const url = this.url || current.url + const currentUrl = await firstValueFrom( + this.marketplaceService.getRegistryUrl$(), + ) const originalUrl = this.localPkg?.registry || '' - if (!this.localPkg) { - if (await this.alerts.alertInstall(this.pkg)) this.install(url) + if (await this.alerts.alertInstall(this.pkg)) this.install(currentUrl) return } if ( - !sameUrl(url, originalUrl) && - !(await this.alerts.alertMarketplace(url, originalUrl)) + !sameUrl(currentUrl, originalUrl) && + !(await this.alerts.alertMarketplace(currentUrl, originalUrl)) ) { return } @@ -148,9 +140,9 @@ export class MarketplaceControlsComponent { hasCurrentDeps(localManifest.id, await getAllPackages(this.patch)) && this.exver.compareExver(localManifest.version, this.pkg.version) !== 0 ) { - this.dryInstall(url) + this.dryInstall(currentUrl) } else { - this.install(url) + this.install(currentUrl) } } @@ -178,7 +170,7 @@ export class MarketplaceControlsComponent { const { id, version } = this.pkg try { - await this.marketplace.installPackage(id, version, url) + await this.marketplaceService.installPackage(id, version, url) } catch (e: any) { this.errorService.handleError(e) } finally { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/menu.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/menu.component.ts index 7506ba8fa..46603da40 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/menu.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/marketplace/components/menu.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { CommonModule } from '@angular/common' import { MenuModule } from '@start9labs/marketplace' import { TuiDialogService, @@ -8,12 +9,13 @@ import { } from '@taiga-ui/core' import { ConfigService } from 'src/app/services/config.service' import { MARKETPLACE_REGISTRY } from '../modals/registry.component' +import { MarketplaceService } from 'src/app/services/marketplace.service' @Component({ standalone: true, selector: 'marketplace-menu', template: ` - + - } @else { - - } - } - - - What's new -

- - View listing - -
- - `, - styles: [ - ` - :host { - display: block; - --tui-background-neutral-1-hover: transparent; +// @Component({ +// selector: 'updates-item', +// template: ` +// +//
+// +// +// +//
+// {{ marketplacePkg.title }} +//
+// {{ localPkg.stateInfo.manifest.version }} +// +// +// {{ marketplacePkg.version }} +// +//
+//
{{ errors }}
+//
+// @if (localPkg.stateInfo.state === 'updating') { +// +// } @else { +// @if (ready) { +// +// } @else { +// +// } +// } +//
+// +// What's new +//

+// +// View listing +// +//
+//
+// `, +// styles: [ +// ` +// :host { +// display: block; +// --tui-background-neutral-1-hover: transparent; - &:not(:last-child) { - border-bottom: 1px solid var(--tui-background-neutral-1); - } - } - `, - ], - standalone: true, - imports: [ - RouterLink, - MarkdownPipeModule, - NgDompurifyModule, - SafeLinksDirective, - SharedPipesModule, - TuiProgress, - TuiAccordion, - TuiAvatar, - TuiIcon, - TuiButton, - TuiLink, - TuiLoader, - InstallingProgressPipe, - ], -}) -export class UpdatesItemComponent { - private readonly dialogs = inject(TuiDialogService) - private readonly patch = inject>(PatchDB) - private readonly marketplace = inject( - AbstractMarketplaceService, - ) as MarketplaceService +// &:not(:last-child) { +// border-bottom: 1px solid var(--tui-background-neutral-1); +// } +// } +// `, +// ], +// standalone: true, +// imports: [ +// RouterLink, +// MarkdownPipeModule, +// NgDompurifyModule, +// SafeLinksDirective, +// SharedPipesModule, +// TuiProgress, +// TuiAccordion, +// TuiAvatar, +// TuiIcon, +// TuiButton, +// TuiLink, +// TuiLoader, +// InstallingProgressPipe, +// ], +// }) +// export class UpdatesItemComponent { +// private readonly dialogs = inject(TuiDialogService) +// private readonly patch = inject>(PatchDB) +// private readonly marketplaceService = inject(MarketplaceService) - @Input({ required: true }) - marketplacePkg!: MarketplacePkg +// @Input({ required: true }) +// marketplacePkg!: MarketplacePkg - @Input({ required: true }) - localPkg!: PackageDataEntry +// @Input({ required: true }) +// localPkg!: PackageDataEntry - @Input({ required: true }) - url!: string +// @Input({ required: true }) +// url!: string - get pkgId(): string { - return this.marketplacePkg.id - } +// get pkgId(): string { +// return this.marketplacePkg.id +// } - get errors(): string { - return this.marketplace.updateErrors[this.pkgId] - } +// get errors(): string { +// return this.marketplaceService.updateErrors[this.pkgId] +// } - get ready(): boolean { - return !this.marketplace.updateQueue[this.pkgId] - } +// get ready(): boolean { +// return !this.marketplaceService.updateQueue[this.pkgId] +// } - async onClick() { - const { id } = this.marketplacePkg +// async onClick() { +// const { id } = this.marketplacePkg - delete this.marketplace.updateErrors[id] - this.marketplace.updateQueue[id] = true +// delete this.marketplaceService.updateErrors[id] +// this.marketplaceService.updateQueue[id] = true - if (hasCurrentDeps(id, await getAllPackages(this.patch))) { - const proceed = await this.alert() +// if (hasCurrentDeps(id, await getAllPackages(this.patch))) { +// const proceed = await this.alert() - if (proceed) { - await this.update() - } else { - delete this.marketplace.updateQueue[id] - } - } else { - await this.update() - } - } +// if (proceed) { +// await this.update() +// } else { +// delete this.marketplaceService.updateQueue[id] +// } +// } else { +// await this.update() +// } +// } - private async update() { - const { id, version } = this.marketplacePkg +// private async update() { +// const { id, version } = this.marketplacePkg - try { - await this.marketplace.installPackage(id, version, this.url) - delete this.marketplace.updateQueue[id] - } catch (e: any) { - delete this.marketplace.updateQueue[id] - this.marketplace.updateErrors[id] = e.message - } - } +// try { +// await this.marketplaceService.installPackage(id, version, this.url) +// delete this.marketplaceService.updateQueue[id] +// } catch (e: any) { +// delete this.marketplaceService.updateQueue[id] +// this.marketplaceService.updateErrors[id] = e.message +// } +// } - private async alert(): Promise { - return new Promise(async resolve => { - this.dialogs - .open(TUI_CONFIRM, { - label: 'Warning', - size: 's', - data: { - content: `Services that depend on ${this.localPkg.stateInfo.manifest.title} will no longer work properly and may crash`, - yes: 'Continue', - no: 'Cancel', - }, - }) - .subscribe(response => resolve(response)) - }) - } -} +// private async alert(): Promise { +// return new Promise(async resolve => { +// this.dialogs +// .open(TUI_CONFIRM, { +// label: 'Warning', +// size: 's', +// data: { +// content: `Services that depend on ${this.localPkg.stateInfo.manifest.title} will no longer work properly and may crash`, +// yes: 'Continue', +// no: 'Cancel', +// }, +// }) +// .subscribe(response => resolve(response)) +// }) +// } +// } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts index 774976c0f..1303824c0 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/updates/updates.component.ts @@ -1,98 +1,95 @@ -import { CommonModule } from '@angular/common' -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { - AbstractMarketplaceService, - StoreIconComponentModule, -} from '@start9labs/marketplace' -import { TuiAvatar } from '@taiga-ui/kit' -import { TuiCell } from '@taiga-ui/layout' -import { PatchDB } from 'patch-db-client' -import { combineLatest, map } from 'rxjs' -import { FilterUpdatesPipe } from 'src/app/routes/portal/routes/system/updates/filter-updates.pipe' -import { UpdatesItemComponent } from 'src/app/routes/portal/routes/system/updates/item.component' -import { ConfigService } from 'src/app/services/config.service' -import { MarketplaceService } from 'src/app/services/marketplace.service' -import { - DataModel, - InstalledState, - PackageDataEntry, - UpdatingState, -} from 'src/app/services/patch-db/data-model' -import { isInstalled, isUpdating } from 'src/app/utils/get-package-data' +// import { CommonModule } from '@angular/common' +// import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +// import { +// StoreIconComponentModule, +// } from '@start9labs/marketplace' +// import { TuiAvatar } from '@taiga-ui/kit' +// import { TuiCell } from '@taiga-ui/layout' +// import { PatchDB } from 'patch-db-client' +// import { combineLatest, map } from 'rxjs' +// import { FilterUpdatesPipe } from 'src/app/routes/portal/routes/system/updates/filter-updates.pipe' +// import { UpdatesItemComponent } from 'src/app/routes/portal/routes/system/updates/item.component' +// import { ConfigService } from 'src/app/services/config.service' +// import { MarketplaceService } from 'src/app/services/marketplace.service' +// import { +// DataModel, +// InstalledState, +// PackageDataEntry, +// UpdatingState, +// } from 'src/app/services/patch-db/data-model' +// import { isInstalled, isUpdating } from 'src/app/utils/get-package-data' -@Component({ - template: ` - @if (data$ | async; as data) { - @for (host of data.hosts; track host) { -

- - {{ host.name }} -

- @if (data.errors.includes(host.url)) { -

Request Failed

- } - @if (data.mp[host.url]?.packages | filterUpdates: data.local; as pkgs) { - @for (pkg of pkgs; track pkg) { - - } @empty { -

All services are up to date!

- } - } @else { - @for (i of [0, 1, 2]; track i) { -
- - Loading update item - - Loading actions - -
- } - } - } - } - `, - host: { class: 'g-page' }, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - CommonModule, - TuiCell, - TuiAvatar, - StoreIconComponentModule, - FilterUpdatesPipe, - UpdatesItemComponent, - ], -}) -export default class UpdatesComponent { - private readonly service = inject( - AbstractMarketplaceService, - ) as MarketplaceService +// @Component({ +// template: ` +// @if (data$ | async; as data) { +// @for (host of data.hosts; track host) { +//

+// +// {{ host.name }} +//

+// @if (data.errors.includes(host.url)) { +//

Request Failed

+// } +// @if (data.mp[host.url]?.packages | filterUpdates: data.local; as pkgs) { +// @for (pkg of pkgs; track pkg) { +// +// } @empty { +//

All services are up to date!

+// } +// } @else { +// @for (i of [0, 1, 2]; track i) { +//
+// +// Loading update item +// +// Loading actions +// +//
+// } +// } +// } +// } +// `, +// host: { class: 'g-page' }, +// changeDetection: ChangeDetectionStrategy.OnPush, +// standalone: true, +// imports: [ +// CommonModule, +// TuiCell, +// TuiAvatar, +// StoreIconComponentModule, +// FilterUpdatesPipe, +// UpdatesItemComponent, +// ], +// }) +// export default class UpdatesComponent { +// private readonly marketplaceService = inject(MarketplaceService) - readonly mp = inject(ConfigService).marketplace - readonly data$ = combineLatest({ - hosts: this.service.getKnownHosts$(true), - mp: this.service.getMarketplace$(), - local: inject>(PatchDB) - .watch$('packageData') - .pipe( - map(pkgs => - Object.entries(pkgs).reduce( - (acc, [id, val]) => { - if (isInstalled(val) || isUpdating(val)) - return { ...acc, [id]: val } - return acc - }, - {} as Record< - string, - PackageDataEntry - >, - ), - ), - ), - errors: this.service.getRequestErrors$(), - }) -} +// readonly mp = inject(ConfigService).marketplace +// readonly data$ = combineLatest({ +// hosts: this.marketplaceService.getKnownHosts$(true), +// mp: this.marketplaceService.getMarketplace$(), +// local: inject>(PatchDB) +// .watch$('packageData') +// .pipe( +// map(pkgs => +// Object.entries(pkgs).reduce( +// (acc, [id, val]) => { +// if (isInstalled(val) || isUpdating(val)) +// return { ...acc, [id]: val } +// return acc +// }, +// {} as Record< +// string, +// PackageDataEntry +// >, +// ), +// ), +// ), +// errors: this.marketplaceService.getRequestErrors$(), +// }) +// } diff --git a/web/projects/ui/src/app/services/badge.service.ts b/web/projects/ui/src/app/services/badge.service.ts index 28917c6e8..d1b8ab062 100644 --- a/web/projects/ui/src/app/services/badge.service.ts +++ b/web/projects/ui/src/app/services/badge.service.ts @@ -1,5 +1,4 @@ import { inject, Injectable } from '@angular/core' -import { AbstractMarketplaceService } from '@start9labs/marketplace' import { Exver } from '@start9labs/shared' import { PatchDB } from 'patch-db-client' import { @@ -10,7 +9,6 @@ import { map, Observable, pairwise, - shareReplay, startWith, switchMap, } from 'rxjs' @@ -32,9 +30,7 @@ export class BadgeService { this.patch.watch$('serverInfo', 'ntpSynced'), inject(EOSService).updateAvailable$, ]).pipe(map(([synced, update]) => Number(!synced) + Number(update))) - private readonly marketplace = inject( - AbstractMarketplaceService, - ) as MarketplaceService + private readonly marketplaceService = inject(MarketplaceService) private readonly local$ = inject(ConnectionService).pipe( filter(Boolean), @@ -58,35 +54,35 @@ export class BadgeService { ), ) - private readonly updates$ = combineLatest([ - this.marketplace.getMarketplace$(true), - this.local$, - ]).pipe( - map( - ([marketplace, local]) => - Object.entries(marketplace).reduce( - (list, [_, store]) => - store?.packages.reduce( - (result, { id, version }) => - local[id] && - this.exver.compareExver( - version, - getManifest(local[id]).version, - ) === 1 - ? result.add(id) - : result, - list, - ) || list, - new Set(), - ).size, - ), - shareReplay(1), - ) + // private readonly updates$ = combineLatest([ + // this.marketplaceService.getMarketplace$(true), + // this.local$, + // ]).pipe( + // map( + // ([marketplace, local]) => + // Object.entries(marketplace).reduce( + // (list, [_, store]) => + // store?.packages.reduce( + // (result, { id, version }) => + // local[id] && + // this.exver.compareExver( + // version, + // getManifest(local[id]).version, + // ) === 1 + // ? result.add(id) + // : result, + // list, + // ) || list, + // new Set(), + // ).size, + // ), + // shareReplay(1), + // ) getCount(id: string): Observable { switch (id) { - case '/portal/system/updates': - return this.updates$ + // case '/portal/system/updates': + // return this.updates$ case '/portal/system/settings': return this.settings$ case '/portal/system/notifications': diff --git a/web/projects/ui/src/app/services/marketplace.service.ts b/web/projects/ui/src/app/services/marketplace.service.ts index f2cd6c878..2903bb939 100644 --- a/web/projects/ui/src/app/services/marketplace.service.ts +++ b/web/projects/ui/src/app/services/marketplace.service.ts @@ -1,42 +1,65 @@ import { Injectable } from '@angular/core' import { - AbstractMarketplaceService, - Marketplace, StoreIdentity, MarketplacePkg, GetPackageRes, - StoreData, + StoreDataWithUrl, } from '@start9labs/marketplace' import { PatchDB } from 'patch-db-client' import { BehaviorSubject, catchError, combineLatest, - distinctUntilKeyChanged, filter, from, map, - mergeMap, Observable, of, - scan, - pairwise, shareReplay, - startWith, switchMap, - take, + distinctUntilChanged, + ReplaySubject, tap, } from 'rxjs' import { RR } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { DataModel, UIStore } from 'src/app/services/patch-db/data-model' import { ConfigService } from './config.service' -import { Exver, sameUrl } from '@start9labs/shared' +import { Exver } from '@start9labs/shared' import { ClientStorageService } from './client-storage.service' import { T } from '@start9labs/start-sdk' -@Injectable() -export class MarketplaceService implements AbstractMarketplaceService { +@Injectable({ + providedIn: 'root', +}) +export class MarketplaceService { + private readonly registryUrlSubject$ = new ReplaySubject(1) + private readonly registryUrl$ = this.registryUrlSubject$.pipe( + distinctUntilChanged(), + ) + + private readonly registry$: Observable = + this.registryUrl$.pipe( + switchMap(url => this.fetchRegistry$(url)), + filter(Boolean), + // @TODO is updateStoreName needed? + map(registry => { + registry.info.categories = { + all: { + name: 'All', + description: { + short: 'All registry packages', + long: 'An unfiltered list of all packages available on this registry.', + }, + }, + ...registry.info.categories, + } + + return registry + }), + shareReplay(1), + ) + private readonly knownHosts$: Observable = this.patch .watch$('ui', 'marketplace', 'knownHosts') .pipe( @@ -69,71 +92,6 @@ export class MarketplaceService implements AbstractMarketplaceService { ), ) - private readonly selectedHost$: Observable = this.patch - .watch$('ui', 'marketplace') - .pipe( - distinctUntilKeyChanged('selectedUrl'), - map(({ selectedUrl: url, knownHosts: hosts }) => - toStoreIdentity(url, hosts[url]), - ), - shareReplay(1), - ) - - private readonly marketplace$ = this.knownHosts$.pipe( - startWith([]), - pairwise(), - mergeMap(([prev, curr]) => - curr.filter(c => !prev.find(p => sameUrl(c.url, p.url))), - ), - mergeMap(({ url, name }) => - this.fetchStore$(url).pipe( - tap(data => { - if (data?.info.name) this.updateStoreName(url, name, data.info.name) - }), - map(data => [url, data]), - startWith<[string, StoreData | null]>([url, null]), - ), - ), - scan<[string, StoreData | null], Record>( - (requests, [url, store]) => { - requests[url] = store - - return requests - }, - {}, - ), - shareReplay(1), - ) - - private readonly filteredMarketplace$ = combineLatest([ - this.clientStorageService.showDevTools$, - this.marketplace$, - ]).pipe( - map(([devMode, marketplace]) => - Object.entries(marketplace).reduce( - (filtered, [url, store]) => - !devMode && (url.includes('alpha') || url.includes('beta')) - ? filtered - : { - [url]: store, - ...filtered, - }, - {} as Marketplace, - ), - ), - ) - - private readonly selectedStore$: Observable = - this.selectedHost$.pipe( - switchMap(({ url }) => - this.marketplace$.pipe( - map(m => m[url]), - filter(Boolean), - take(1), - ), - ), - ) - private readonly requestErrors$ = new BehaviorSubject([]) constructor( @@ -149,33 +107,17 @@ export class MarketplaceService implements AbstractMarketplaceService { return filtered ? this.filteredKnownHosts$ : this.knownHosts$ } - getSelectedHost$(): Observable { - return this.selectedHost$ + getRegistryUrl$() { + return this.registryUrl$ } - getMarketplace$(filtered = false): Observable { - // option to filter out hosts containing 'alpha' or 'beta' substrings in registryURL - return filtered ? this.filteredMarketplace$ : this.marketplace$ + setRegistryUrl(url: string | null) { + const registryUrl = url || this.config.marketplace.start9 + this.registryUrlSubject$.next(registryUrl) } - getSelectedStore$(): Observable { - return this.selectedStore$ - } - - getSelectedStoreWithCategories$() { - return this.selectedHost$.pipe( - switchMap(({ url }) => - this.marketplace$.pipe( - map(m => m[url]), - filter(Boolean), - map(({ info, packages }) => ({ - url, - info, - packages, - })), - ), - ), - ) + getRegistry$(): Observable { + return this.registry$ } getPackage$( @@ -184,47 +126,20 @@ export class MarketplaceService implements AbstractMarketplaceService { flavor: string | null, registryUrl?: string, ): Observable { - return this.selectedHost$.pipe( - switchMap(selected => - this.marketplace$.pipe( - switchMap(m => { - const url = registryUrl || selected.url - const pkg = m[url]?.packages.find( - p => - p.id === id && - p.flavor === flavor && - (!version || this.exver.compareExver(p.version, version) === 0), - ) - - return pkg ? of(pkg) : this.fetchPackage$(url, id, version, flavor) - }), - ), - ), + return this.registry$.pipe( + switchMap(registry => { + const url = registryUrl || registry.url + const pkg = registry.packages.find( + p => + p.id === id && + p.flavor === flavor && + (!version || this.exver.compareExver(p.version, version) === 0), + ) + return pkg ? of(pkg) : this.fetchPackage$(url, id, version, flavor) + }), ) } - // UI only - readonly updateErrors: Record = {} - readonly updateQueue: Record = {} - - getRequestErrors$(): Observable { - return this.requestErrors$ - } - - async installPackage( - id: string, - version: string, - url: string, - ): Promise { - const params: RR.InstallPackageReq = { - id, - version, - registry: url, - } - - await this.api.installPackage(params) - } - fetchInfo$(url: string): Observable { return from(this.api.getRegistryInfo(url)).pipe( map(info => ({ @@ -232,7 +147,10 @@ export class MarketplaceService implements AbstractMarketplaceService { categories: { all: { name: 'All', - description: { short: 'All services', long: 'All services' }, + description: { + short: 'All services', + long: 'An unfiltered list of all services available on this registry.', + }, }, ...info.categories, }, @@ -240,16 +158,17 @@ export class MarketplaceService implements AbstractMarketplaceService { ) } - fetchStatic$( + getStatic$( pkg: MarketplacePkg, type: 'LICENSE.md' | 'instructions.md', ): Observable { return from(this.api.getStaticProxy(pkg, type)) } - private fetchStore$(url: string): Observable { + private fetchRegistry$(url: string): Observable { + console.log('FETCHING REGISTRY: ', url) return combineLatest([this.fetchInfo$(url), this.fetchPackages$(url)]).pipe( - map(([info, packages]) => ({ info, packages })), + map(([info, packages]) => ({ info, packages, url })), catchError(e => { console.error(e) this.requestErrors$.next(this.requestErrors$.value.concat(url)) @@ -275,7 +194,22 @@ export class MarketplaceService implements AbstractMarketplaceService { ) } - convertToMarketplacePkg( + private fetchPackage$( + url: string, + id: string, + version: string | null, + flavor: string | null, + ): Observable { + return from( + this.api.getRegistryPackage(url, id, version ? `=${version}` : null), + ).pipe( + map(pkgInfo => + this.convertToMarketplacePkg(id, version, flavor, pkgInfo), + ), + ) + } + + private convertToMarketplacePkg( id: string, version: string | null | undefined, flavor: string | null, @@ -297,26 +231,6 @@ export class MarketplaceService implements AbstractMarketplaceService { } } - private fetchPackage$( - url: string, - id: string, - version: string | null, - flavor: string | null, - ): Observable { - return from( - this.api.getRegistryPackage(url, id, version ? `=${version}` : null), - ).pipe( - map(pkgInfo => - this.convertToMarketplacePkg( - id, - version === '*' ? null : version, - flavor, - pkgInfo, - ), - ), - ) - } - private async updateStoreName( url: string, oldName: string | undefined, @@ -329,6 +243,28 @@ export class MarketplaceService implements AbstractMarketplaceService { ) } } + + // UI only + readonly updateErrors: Record = {} + readonly updateQueue: Record = {} + + getRequestErrors$(): Observable { + return this.requestErrors$ + } + + async installPackage( + id: string, + version: string, + url: string, + ): Promise { + const params: RR.InstallPackageReq = { + id, + version, + registry: url, + } + + await this.api.installPackage(params) + } } function toStoreIdentity(url: string, uiStore: UIStore): StoreIdentity { diff --git a/web/projects/ui/src/app/services/patch-data.service.ts b/web/projects/ui/src/app/services/patch-data.service.ts index ae42987bd..35c86550e 100644 --- a/web/projects/ui/src/app/services/patch-data.service.ts +++ b/web/projects/ui/src/app/services/patch-data.service.ts @@ -1,14 +1,12 @@ -import { Inject, Injectable } from '@angular/core' -import { AbstractMarketplaceService } from '@start9labs/marketplace' +import { Injectable } from '@angular/core' import { TuiDialogService } from '@taiga-ui/core' -import { filter, share, switchMap, take, Observable, map } from 'rxjs' +import { filter, share, switchMap, Observable, map } from 'rxjs' import { PatchDB } from 'patch-db-client' import { DataModel } from 'src/app/services/patch-db/data-model' import { EOSService } from 'src/app/services/eos.service' import { WelcomeComponent } from 'src/app/components/welcome.component' import { ConfigService } from 'src/app/services/config.service' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { MarketplaceService } from 'src/app/services/marketplace.service' import { ConnectionService } from 'src/app/services/connection.service' import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' import { LocalStorageBootstrap } from './patch-db/local-storage-bootstrap' @@ -40,8 +38,6 @@ export class PatchDataService extends Observable { private readonly config: ConfigService, private readonly dialogs: TuiDialogService, private readonly embassyApi: ApiService, - @Inject(AbstractMarketplaceService) - private readonly marketplaceService: MarketplaceService, private readonly connection$: ConnectionService, private readonly bootstrapper: LocalStorageBootstrap, ) { @@ -50,7 +46,7 @@ export class PatchDataService extends Observable { private checkForUpdates(): void { this.eosService.loadEos() - this.marketplaceService.getMarketplace$().pipe(take(1)).subscribe() + // this.marketplaceService.getMarketplace$().pipe(take(1)).subscribe() } private showEosWelcome(ackVersion: string) { diff --git a/web/projects/ui/src/app/utils/system-utilities.ts b/web/projects/ui/src/app/utils/system-utilities.ts index 825904955..c726c8170 100644 --- a/web/projects/ui/src/app/utils/system-utilities.ts +++ b/web/projects/ui/src/app/utils/system-utilities.ts @@ -12,10 +12,10 @@ export const SYSTEM_UTILITIES: Record = icon: '@tui.shopping-cart', title: 'Marketplace', }, - '/portal/system/updates': { - icon: '@tui.globe', - title: 'Updates', - }, + // '/portal/system/updates': { + // icon: '@tui.globe', + // title: 'Updates', + // }, '/portal/system/sideload': { icon: '@tui.upload', title: 'Sideload',