mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 06:19:44 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -163,6 +163,7 @@ export class MarketplaceTileComponent {
|
||||
id: open ? this.pkg().id : null,
|
||||
flavor: open ? this.pkg().flavor : null,
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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$()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
|
||||
@@ -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))
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -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$(),
|
||||
// })
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user