Update Marketplace (#2742)

* update abstract marketplace for usage accuracy andrename store to registry

* use new abstract functions

* fix(marketplace): get rid of `AbstractMarketplaceService`

* bump shared marketplace lib

* update marketplace to use query params for registry url; comment out updates page - will be refactored

* cleanup

* cleanup duplicate

* cleanup unused imports

* rework setting registry url when loading marketplace

* cleanup marketplace service

* fix background

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: waterplea <alexander@inkin.ru>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
This commit is contained in:
Lucy
2024-10-09 11:23:08 -06:00
committed by GitHub
parent a9569d0ed9
commit dfda2f7d5d
26 changed files with 688 additions and 807 deletions

View File

@@ -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 {

View File

@@ -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: `
<menu [iconConfig]="marketplace">
<menu [iconConfig]="marketplace" [registry]="registry$ | async">
<button
slot="desktop"
tuiIconButton
@@ -45,11 +47,13 @@ import { MARKETPLACE_REGISTRY } from '../modals/registry.component'
`,
],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [MenuModule, TuiButton, TuiIcon, TuiAppearance],
imports: [CommonModule, MenuModule, TuiButton, TuiIcon, TuiAppearance],
})
export class MarketplaceMenuComponent {
private readonly dialogs = inject(TuiDialogService)
readonly marketplace = inject(ConfigService).marketplace
private readonly marketplaceService = inject(MarketplaceService)
readonly registry$ = this.marketplaceService.getRegistry$()
changeRegistry() {
this.dialogs

View File

@@ -163,6 +163,7 @@ export class MarketplaceTileComponent {
id: open ? this.pkg().id : null,
flavor: open ? this.pkg().flavor : null,
},
queryParamsHandling: 'merge',
})
}
}

View File

@@ -3,16 +3,21 @@ import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import {
AbstractCategoryService,
AbstractMarketplaceService,
FilterPackagesPipe,
FilterPackagesPipeModule,
} from '@start9labs/marketplace'
import { combineLatest, map } from 'rxjs'
import { tap, withLatestFrom } from 'rxjs'
import { MarketplaceNotificationComponent } from './components/notification.component'
import { MarketplaceMenuComponent } from './components/menu.component'
import { MarketplaceTileComponent } from './components/tile.component'
import { MarketplaceControlsComponent } from './components/controls.component'
import { MarketplacePreviewComponent } from './modals/preview.component'
import { MarketplaceSidebarsComponent } from './components/sidebars.component'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import { ActivatedRoute, Router } from '@angular/router'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { PatchDB } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model'
@Component({
standalone: true,
@@ -21,15 +26,19 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
<tui-scrollbar>
<div class="marketplace-content-wrapper">
<div class="marketplace-content-inner">
<marketplace-notification [url]="(details$ | async)?.url || ''" />
<marketplace-notification [url]="(url$ | async) || ''" />
<div class="title-wrapper">
<h1>
{{ category$ | async | titlecase }}
</h1>
</div>
@if (filtered$ | async; as filtered) {
@if (registry$ | async; as registry) {
<section class="marketplace-content-list">
@for (pkg of filtered; track $index) {
@for (
pkg of registry.packages
| filterPackages: (query$ | async) : (category$ | async);
track $index
) {
<marketplace-tile
[pkg]="pkg"
[style.--animation-order]="$index"
@@ -58,6 +67,7 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
padding: 0;
background: rgb(55 58 63 / 90%)
url('/assets/img/background_marketplace.png') no-repeat top right;
background-size: cover;
}
.marketplace-content {
@@ -127,6 +137,7 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
font-size: 1.25rem;
line-height: 1.75rem;
padding-left: 1.5rem;
font-weight: normal;
}
:host-context(tui-root._mobile) {
@@ -145,20 +156,34 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
MarketplacePreviewComponent,
MarketplaceSidebarsComponent,
TuiScrollbar,
FilterPackagesPipeModule,
],
})
export class MarketplaceComponent {
private readonly pipe = inject(FilterPackagesPipe)
private readonly categoryService = inject(AbstractCategoryService)
private readonly marketplaceService = inject(AbstractMarketplaceService)
private readonly marketplaceService = inject(MarketplaceService)
private readonly router = inject(Router)
private readonly patch = inject(PatchDB<DataModel>)
private readonly route = inject(ActivatedRoute)
.queryParamMap.pipe(
takeUntilDestroyed(),
withLatestFrom(this.patch.watch$('ui', 'marketplace', 'selectedUrl')),
tap(([params, selectedUrl]) => {
const registry = params.get('registry')
if (!registry) {
this.router.navigate([], {
queryParams: { registry: selectedUrl },
queryParamsHandling: 'merge',
})
} else {
this.marketplaceService.setRegistryUrl(registry)
}
}),
)
.subscribe()
readonly details$ = this.marketplaceService.getSelectedHost$()
readonly url$ = this.marketplaceService.getRegistryUrl$()
readonly category$ = this.categoryService.getCategory$()
readonly filtered$ = combineLatest([
this.marketplaceService
.getSelectedStore$()
.pipe(map(({ packages }) => packages)),
this.categoryService.getQuery$(),
this.category$,
]).pipe(map(args => this.pipe.transform(...args)))
readonly query$ = this.categoryService.getQuery$()
readonly registry$ = this.marketplaceService.getRegistry$()
}

View File

@@ -10,7 +10,6 @@ import { FormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import {
AboutModule,
AbstractMarketplaceService,
AdditionalModule,
FlavorsComponent,
MarketplaceAdditionalItemComponent,
@@ -35,6 +34,7 @@ import {
startWith,
switchMap,
} from 'rxjs'
import { MarketplaceService } from 'src/app/services/marketplace.service'
@Component({
selector: 'marketplace-preview',
@@ -186,8 +186,8 @@ export class MarketplacePreviewComponent {
private readonly dialogs = inject(TuiDialogService)
private readonly exver = inject(Exver)
private readonly router = inject(Router)
private readonly marketplaceService = inject(AbstractMarketplaceService)
private readonly version$ = new BehaviorSubject<string>('*')
private readonly marketplaceService = inject(MarketplaceService)
private readonly version$ = new BehaviorSubject<string | null>(null)
private readonly flavor$ = this.router.routerState.root.queryParamMap.pipe(
map(paramMap => paramMap.get('flavor')),
)
@@ -202,7 +202,7 @@ export class MarketplacePreviewComponent {
readonly flavors$ = this.flavor$.pipe(
switchMap(current =>
this.marketplaceService.getSelectedStore$().pipe(
this.marketplaceService.getRegistry$().pipe(
map(({ packages }) =>
packages.filter(
({ id, flavor }) => id === this.pkgId && flavor !== current,

View File

@@ -9,12 +9,20 @@ import {
toUrl,
} from '@start9labs/shared'
import {
AbstractMarketplaceService,
StoreIconComponentModule,
MarketplaceRegistryComponent,
} from '@start9labs/marketplace'
import { TuiDialogService, TuiIcon, TuiTitle, TuiButton } from '@taiga-ui/core'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import {
TuiDialogService,
TuiIcon,
TuiTitle,
TuiButton,
TuiDialogContext,
} from '@taiga-ui/core'
import {
PolymorpheusComponent,
POLYMORPHEUS_CONTEXT,
} from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { combineLatest, filter, firstValueFrom, map, Subscription } from 'rxjs'
import { FormComponent } from 'src/app/routes/portal/components/form.component'
@@ -24,6 +32,7 @@ import { MarketplaceService } from 'src/app/services/marketplace.service'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { getMarketplaceValueSpec, getPromptOptions } from '../utils/registry'
import { ConfigService } from 'src/app/services/config.service'
import { ActivatedRoute, Router } from '@angular/router'
@Component({
standalone: true,
@@ -90,9 +99,10 @@ export class MarketplaceRegistryModal {
private readonly errorService = inject(ErrorService)
private readonly formDialog = inject(FormDialogService)
private readonly dialogs = inject(TuiDialogService)
private readonly marketplace = inject(
AbstractMarketplaceService,
) as MarketplaceService
private readonly marketplaceService = inject(MarketplaceService)
private readonly context = inject<TuiDialogContext>(POLYMORPHEUS_CONTEXT)
private readonly route = inject(ActivatedRoute)
private readonly router = inject(Router)
private readonly hosts$ = inject<PatchDB<DataModel>>(PatchDB).watch$(
'ui',
'marketplace',
@@ -101,13 +111,13 @@ export class MarketplaceRegistryModal {
readonly marketplaceConfig = inject(ConfigService).marketplace
readonly stores$ = combineLatest([
this.marketplace.getKnownHosts$(),
this.marketplace.getSelectedHost$(),
this.marketplaceService.getKnownHosts$(),
this.marketplaceService.getRegistryUrl$(),
]).pipe(
map(([stores, selected]) =>
map(([stores, selectedUrl]) =>
stores.map(s => ({
...s,
selected: sameUrl(s.url, selected.url),
selected: sameUrl(s.url, selectedUrl),
})),
),
// 0 and 1 are prod and community, 2 and beyond are alts
@@ -170,9 +180,14 @@ export class MarketplaceRegistryModal {
loader.unsubscribe()
loader.closed = false
loader.add(this.loader.open('Changing Registry...').subscribe())
try {
await this.api.setDbValue<string>(['marketplace', 'selectedUrl'], url)
this.marketplaceService.setRegistryUrl(url)
this.router.navigate([], {
queryParams: { registry: url },
queryParamsHandling: 'merge',
})
this.api.setDbValue<string>(['marketplace', 'selectedUrl'], url)
this.context.$implicit.complete()
} catch (e: any) {
this.errorService.handleError(e)
} finally {
@@ -210,7 +225,9 @@ export class MarketplaceRegistryModal {
loader.closed = false
loader.add(this.loader.open('Validating marketplace...').subscribe())
const { name } = await firstValueFrom(this.marketplace.fetchInfo$(url))
const { name } = await firstValueFrom(
this.marketplaceService.fetchInfo$(url),
)
// Save
loader.unsubscribe()

View File

@@ -42,12 +42,12 @@ const ROUTES: Routes = [
loadComponent: () => import('./sideload/sideload.component'),
data: toNavigationItem('/portal/system/sideload'),
},
{
title: systemTabResolver,
path: 'updates',
loadComponent: () => import('./updates/updates.component'),
data: toNavigationItem('/portal/system/updates'),
},
// {
// title: systemTabResolver,
// path: 'updates',
// loadComponent: () => import('./updates/updates.component'),
// data: toNavigationItem('/portal/system/updates'),
// },
{
title: systemTabResolver,
path: 'metrics',

View File

@@ -1,40 +1,40 @@
import { inject, Pipe, PipeTransform } from '@angular/core'
import { Exver } from '@start9labs/shared'
import { MarketplacePkg } from '@start9labs/marketplace'
import {
InstalledState,
PackageDataEntry,
UpdatingState,
} from 'src/app/services/patch-db/data-model'
// import { inject, Pipe, PipeTransform } from '@angular/core'
// import { Exver } from '@start9labs/shared'
// import { MarketplacePkg } from '@start9labs/marketplace'
// import {
// InstalledState,
// PackageDataEntry,
// UpdatingState,
// } from 'src/app/services/patch-db/data-model'
@Pipe({
name: 'filterUpdates',
standalone: true,
})
export class FilterUpdatesPipe implements PipeTransform {
private readonly exver = inject(Exver)
// @Pipe({
// name: 'filterUpdates',
// standalone: true,
// })
// export class FilterUpdatesPipe implements PipeTransform {
// private readonly exver = inject(Exver)
transform(
pkgs?: MarketplacePkg[],
local: Record<
string,
PackageDataEntry<InstalledState | UpdatingState>
> = {},
): MarketplacePkg[] | null {
return (
pkgs?.filter(
({ id, version, flavor }) =>
local[id] &&
this.exver.getFlavor(getVersion(local, id)) === flavor &&
this.exver.compareExver(version, getVersion(local, id)) === 1,
) || null
)
}
}
// transform(
// pkgs?: MarketplacePkg[],
// local: Record<
// string,
// PackageDataEntry<InstalledState | UpdatingState>
// > = {},
// ): MarketplacePkg[] | null {
// return (
// pkgs?.filter(
// ({ id, version, flavor }) =>
// local[id] &&
// this.exver.getFlavor(getVersion(local, id)) === flavor &&
// this.exver.compareExver(version, getVersion(local, id)) === 1,
// ) || null
// )
// }
// }
function getVersion(
local: Record<string, PackageDataEntry<InstalledState | UpdatingState>>,
id: string,
): string {
return local[id].stateInfo.manifest.version
}
// function getVersion(
// local: Record<string, PackageDataEntry<InstalledState | UpdatingState>>,
// id: string,
// ): string {
// return local[id].stateInfo.manifest.version
// }

View File

@@ -1,202 +1,199 @@
import { Component, inject, Input } from '@angular/core'
import { RouterLink } from '@angular/router'
import {
AbstractMarketplaceService,
MarketplacePkg,
} from '@start9labs/marketplace'
import {
MarkdownPipeModule,
SafeLinksDirective,
SharedPipesModule,
} from '@start9labs/shared'
import {
TuiDialogService,
TuiLoader,
TuiIcon,
TuiLink,
TuiButton,
} from '@taiga-ui/core'
import {
TuiProgress,
TuiAccordion,
TuiAvatar,
TUI_CONFIRM,
} from '@taiga-ui/kit'
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
import { PatchDB } from 'patch-db-client'
import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pipes/install-progress.pipe'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import {
DataModel,
InstalledState,
PackageDataEntry,
UpdatingState,
} from 'src/app/services/patch-db/data-model'
import { getAllPackages } from 'src/app/utils/get-package-data'
import { hasCurrentDeps } from 'src/app/utils/has-deps'
// import { Component, inject, Input } from '@angular/core'
// import { RouterLink } from '@angular/router'
// import {
// MarketplacePkg,
// } from '@start9labs/marketplace'
// import {
// MarkdownPipeModule,
// SafeLinksDirective,
// SharedPipesModule,
// } from '@start9labs/shared'
// import {
// TuiDialogService,
// TuiLoader,
// TuiIcon,
// TuiLink,
// TuiButton,
// } from '@taiga-ui/core'
// import {
// TuiProgress,
// TuiAccordion,
// TuiAvatar,
// TUI_CONFIRM,
// } from '@taiga-ui/kit'
// import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
// import { PatchDB } from 'patch-db-client'
// import { InstallingProgressPipe } from 'src/app/routes/portal/routes/service/pipes/install-progress.pipe'
// import { MarketplaceService } from 'src/app/services/marketplace.service'
// import {
// DataModel,
// InstalledState,
// PackageDataEntry,
// UpdatingState,
// } from 'src/app/services/patch-db/data-model'
// import { getAllPackages } from 'src/app/utils/get-package-data'
// import { hasCurrentDeps } from 'src/app/utils/has-deps'
@Component({
selector: 'updates-item',
template: `
<tui-accordion-item borders="top-bottom">
<div class="g-action">
<tui-avatar size="s">
<img alt="" [src]="marketplacePkg.icon" />
</tui-avatar>
<div [style.flex]="1" [style.overflow]="'hidden'">
<strong>{{ marketplacePkg.title }}</strong>
<div>
{{ localPkg.stateInfo.manifest.version }}
<tui-icon icon="@tui.arrow-right" [style.font-size.rem]="1" />
<span [style.color]="'var(--tui-text-positive)'">
{{ marketplacePkg.version }}
</span>
</div>
<div [style.color]="'var(--tui-text-negative)'">{{ errors }}</div>
</div>
@if (localPkg.stateInfo.state === 'updating') {
<tui-progress-circle
class="g-success"
size="s"
[max]="1"
[value]="
(localPkg.stateInfo.installingInfo.progress.overall
| installingProgress) || 0
"
/>
} @else {
@if (ready) {
<button
tuiButton
size="s"
[appearance]="errors ? 'destructive' : 'primary'"
(click.stop)="onClick()"
>
{{ errors ? 'Retry' : 'Update' }}
</button>
} @else {
<tui-loader [style.width.rem]="2" [inheritColor]="true" />
}
}
</div>
<ng-template tuiAccordionItemContent>
<strong>What's new</strong>
<p
safeLinks
[innerHTML]="marketplacePkg.releaseNotes | markdown | dompurify"
></p>
<a
tuiLink
iconEnd="@tui.external-link"
routerLink="/marketplace"
[queryParams]="{ url: url, id: marketplacePkg.id }"
>
View listing
</a>
</ng-template>
</tui-accordion-item>
`,
styles: [
`
:host {
display: block;
--tui-background-neutral-1-hover: transparent;
// @Component({
// selector: 'updates-item',
// template: `
// <tui-accordion-item borders="top-bottom">
// <div class="g-action">
// <tui-avatar size="s">
// <img alt="" [src]="marketplacePkg.icon" />
// </tui-avatar>
// <div [style.flex]="1" [style.overflow]="'hidden'">
// <strong>{{ marketplacePkg.title }}</strong>
// <div>
// {{ localPkg.stateInfo.manifest.version }}
// <tui-icon icon="@tui.arrow-right" [style.font-size.rem]="1" />
// <span [style.color]="'var(--tui-text-positive)'">
// {{ marketplacePkg.version }}
// </span>
// </div>
// <div [style.color]="'var(--tui-text-negative)'">{{ errors }}</div>
// </div>
// @if (localPkg.stateInfo.state === 'updating') {
// <tui-progress-circle
// class="g-success"
// size="s"
// [max]="1"
// [value]="
// (localPkg.stateInfo.installingInfo.progress.overall
// | installingProgress) || 0
// "
// />
// } @else {
// @if (ready) {
// <button
// tuiButton
// size="s"
// [appearance]="errors ? 'destructive' : 'primary'"
// (click.stop)="onClick()"
// >
// {{ errors ? 'Retry' : 'Update' }}
// </button>
// } @else {
// <tui-loader [style.width.rem]="2" [inheritColor]="true" />
// }
// }
// </div>
// <ng-template tuiAccordionItemContent>
// <strong>What's new</strong>
// <p
// safeLinks
// [innerHTML]="marketplacePkg.releaseNotes | markdown | dompurify"
// ></p>
// <a
// tuiLink
// iconEnd="@tui.external-link"
// routerLink="/marketplace"
// [queryParams]="{ url: url, id: marketplacePkg.id }"
// >
// View listing
// </a>
// </ng-template>
// </tui-accordion-item>
// `,
// 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<DataModel>>(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<DataModel>>(PatchDB)
// private readonly marketplaceService = inject(MarketplaceService)
@Input({ required: true })
marketplacePkg!: MarketplacePkg
// @Input({ required: true })
// marketplacePkg!: MarketplacePkg
@Input({ required: true })
localPkg!: PackageDataEntry<InstalledState | UpdatingState>
// @Input({ required: true })
// localPkg!: PackageDataEntry<InstalledState | UpdatingState>
@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<boolean> {
return new Promise(async resolve => {
this.dialogs
.open<boolean>(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<boolean> {
// return new Promise(async resolve => {
// this.dialogs
// .open<boolean>(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))
// })
// }
// }

View File

@@ -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) {
<h3 class="g-title">
<store-icon [url]="host.url" [marketplace]="mp" size="26px" />
{{ host.name }}
</h3>
@if (data.errors.includes(host.url)) {
<p class="g-error">Request Failed</p>
}
@if (data.mp[host.url]?.packages | filterUpdates: data.local; as pkgs) {
@for (pkg of pkgs; track pkg) {
<updates-item
[marketplacePkg]="pkg"
[localPkg]="data.local[pkg.id]"
[url]="host.url"
/>
} @empty {
<p>All services are up to date!</p>
}
} @else {
@for (i of [0, 1, 2]; track i) {
<section tuiCell>
<tui-avatar class="tui-skeleton" />
<span class="tui-skeleton">Loading update item</span>
<span class="tui-skeleton" [style.margin-left]="'auto'">
Loading actions
</span>
</section>
}
}
}
}
`,
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) {
// <h3 class="g-title">
// <store-icon [url]="host.url" [marketplace]="mp" size="26px" />
// {{ host.name }}
// </h3>
// @if (data.errors.includes(host.url)) {
// <p class="g-error">Request Failed</p>
// }
// @if (data.mp[host.url]?.packages | filterUpdates: data.local; as pkgs) {
// @for (pkg of pkgs; track pkg) {
// <updates-item
// [marketplacePkg]="pkg"
// [localPkg]="data.local[pkg.id]"
// [url]="host.url"
// />
// } @empty {
// <p>All services are up to date!</p>
// }
// } @else {
// @for (i of [0, 1, 2]; track i) {
// <section tuiCell>
// <tui-avatar class="tui-skeleton" />
// <span class="tui-skeleton">Loading update item</span>
// <span class="tui-skeleton" [style.margin-left]="'auto'">
// Loading actions
// </span>
// </section>
// }
// }
// }
// }
// `,
// 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<DataModel>>(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<InstalledState | UpdatingState>
>,
),
),
),
errors: this.service.getRequestErrors$(),
})
}
// readonly mp = inject(ConfigService).marketplace
// readonly data$ = combineLatest({
// hosts: this.marketplaceService.getKnownHosts$(true),
// mp: this.marketplaceService.getMarketplace$(),
// local: inject<PatchDB<DataModel>>(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<InstalledState | UpdatingState>
// >,
// ),
// ),
// ),
// errors: this.marketplaceService.getRequestErrors$(),
// })
// }