mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
fix: implement flavor across the app
This commit is contained in:
@@ -9,7 +9,7 @@ header {
|
||||
@include scrollbar-hidden();
|
||||
|
||||
// TODO: Theme
|
||||
background: #2B2B2F;
|
||||
background: #2b2b2f;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
@@ -33,7 +33,7 @@ header {
|
||||
1;
|
||||
border-width: 0;
|
||||
border-right: 0.125rem solid;
|
||||
overflow: visible;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1536px) {
|
||||
@@ -133,11 +133,11 @@ header {
|
||||
|
||||
&-sidebar {
|
||||
background-color: rgb(var(--tw-color-zinc-700) / 0.9);
|
||||
height: calc(100vh - var(--portal-header-height));
|
||||
width: 70vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
&-top {
|
||||
display: flex;
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { Exver, MarkdownPipeModule } from '@start9labs/shared'
|
||||
import { TuiButton, TuiLoader } from '@taiga-ui/core'
|
||||
import { TuiCardLarge } from '@taiga-ui/layout'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { TuiButton, TuiDialogContext, TuiLoader } from '@taiga-ui/core'
|
||||
import { TuiAccordion } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@taiga-ui/polymorpheus'
|
||||
import { map } from 'rxjs'
|
||||
import { AbstractMarketplaceService } from '../services/marketplace.service'
|
||||
import { MarketplacePkg } from '../types'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: `
|
||||
@if (notes$ | async; as notes) {
|
||||
@for (note of notes | keyvalue: asIsOrder; track $index) {
|
||||
<button tuiButton (click)="setSelected(note.key)">
|
||||
{{ note.key }}
|
||||
</button>
|
||||
<div
|
||||
tuiCardLarge
|
||||
#element
|
||||
[id]="note.key"
|
||||
[style.max-height.px]="getDocSize(note.key, element)"
|
||||
[innerHTML]="note.value | markdown"
|
||||
></div>
|
||||
}
|
||||
<tui-accordion>
|
||||
@for (note of notes | keyvalue: asIsOrder; track $index) {
|
||||
<tui-accordion-item>
|
||||
{{ note.key }}
|
||||
<ng-template tuiAccordionItemContent>
|
||||
<div [innerHTML]="note.value | markdown"></div>
|
||||
</ng-template>
|
||||
</tui-accordion-item>
|
||||
}
|
||||
</tui-accordion>
|
||||
} @else {
|
||||
<tui-loader textContent="Loading Release Notes" />
|
||||
}
|
||||
@@ -38,15 +34,14 @@ import { MarketplacePkg } from '../types'
|
||||
CommonModule,
|
||||
TuiButton,
|
||||
TuiLoader,
|
||||
TuiCardLarge,
|
||||
TuiAccordion,
|
||||
MarkdownPipeModule,
|
||||
],
|
||||
})
|
||||
export class ReleaseNotesComponent {
|
||||
@Input() pkg!: MarketplacePkg
|
||||
|
||||
private selected: string | null = null
|
||||
private readonly exver = inject(Exver)
|
||||
private readonly pkg =
|
||||
inject<TuiDialogContext<void, MarketplacePkg>>(POLYMORPHEUS_CONTEXT).data
|
||||
|
||||
readonly notes$ = inject(AbstractMarketplaceService)
|
||||
.getSelectedStore$()
|
||||
@@ -70,18 +65,6 @@ export class ReleaseNotesComponent {
|
||||
}),
|
||||
)
|
||||
|
||||
isSelected(key: string): boolean {
|
||||
return this.selected === key
|
||||
}
|
||||
|
||||
setSelected(selected: string) {
|
||||
this.selected = this.isSelected(selected) ? null : selected
|
||||
}
|
||||
|
||||
getDocSize(key: string, { scrollHeight }: HTMLElement) {
|
||||
return this.isSelected(key) ? scrollHeight : 0
|
||||
}
|
||||
|
||||
asIsOrder(a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
<button
|
||||
*ngFor="let cat of categories || fallback | keyvalue"
|
||||
(click)="switchCategory(cat.key)"
|
||||
[class.category_selected]="cat.key === category"
|
||||
>
|
||||
<div
|
||||
class="category-wrapper"
|
||||
[class.tui-skeleton]="!categories"
|
||||
[class.tui-skeleton_rounded]="!categories"
|
||||
@for (cat of categories || fallback | keyvalue: asIsOrder; track $index) {
|
||||
<button
|
||||
(click)="switchCategory(cat.key)"
|
||||
[class.category_selected]="cat.key === category"
|
||||
>
|
||||
<tui-icon tuiAppearance="icon" [icon]="determineIcon(cat.key)" />
|
||||
</div>
|
||||
<span
|
||||
class="category-title"
|
||||
[class.tui-skeleton]="!categories"
|
||||
[class.tui-skeleton_rounded]="!categories"
|
||||
>
|
||||
{{
|
||||
cat.key === 'ai'
|
||||
? (cat.key | uppercase)
|
||||
: (cat.value.name | titlecase) || 'Loading category...'
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="category-wrapper" [tuiSkeleton]="!categories">
|
||||
<tui-icon tuiAppearance="icon" [icon]="determineIcon(cat.key)" />
|
||||
</div>
|
||||
<span class="category-title" [tuiSkeleton]="categories ? false : 3">
|
||||
{{
|
||||
cat.key === 'ai' ? (cat.key | uppercase) : (cat.value.name | titlecase)
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -7,6 +7,21 @@ import {
|
||||
} from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
|
||||
const ICONS: Record<string, string> = {
|
||||
all: '@tui.layout-grid',
|
||||
bitcoin: '@tui.bitcoin',
|
||||
messaging: '@tui.message-circle',
|
||||
communications: '@tui.message-circle',
|
||||
data: '@tui.file-text',
|
||||
'developer tools': '@tui.table-split',
|
||||
featured: '@tui.star',
|
||||
lightning: '@tui.zap',
|
||||
media: '@tui.circle-play',
|
||||
networking: '@tui.globe',
|
||||
social: '@tui.users',
|
||||
ai: '@tui.cpu',
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-categories',
|
||||
templateUrl: 'categories.component.html',
|
||||
@@ -37,32 +52,10 @@ export class CategoriesComponent {
|
||||
}
|
||||
|
||||
determineIcon(category: string): string {
|
||||
switch (category.toLowerCase()) {
|
||||
case 'all':
|
||||
return '@tui.layout-grid'
|
||||
case 'bitcoin':
|
||||
return '@tui.bitcoin'
|
||||
case 'messaging':
|
||||
case 'communications':
|
||||
return '@tui.message-circle'
|
||||
case 'data':
|
||||
return '@tui.file-text'
|
||||
case 'developer tools':
|
||||
return '@tui.table-split'
|
||||
case 'featured':
|
||||
return '@tui.star'
|
||||
case 'lightning':
|
||||
return '@tui.zap'
|
||||
case 'media':
|
||||
return '@tui.circle-play'
|
||||
case 'networking':
|
||||
return '@tui.globe'
|
||||
case 'social':
|
||||
return '@tui.users'
|
||||
case 'ai':
|
||||
return '@tui.cpu'
|
||||
default:
|
||||
return '@tui.box'
|
||||
}
|
||||
return ICONS[category.toLowerCase()] || '@tui.box'
|
||||
}
|
||||
|
||||
asIsOrder(a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { TuiIcon, TuiAppearance } from '@taiga-ui/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||
|
||||
import { CategoriesComponent } from './categories.component'
|
||||
import { RouterModule } from '@angular/router'
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule, CommonModule, TuiAppearance, TuiIcon],
|
||||
imports: [RouterModule, CommonModule, TuiAppearance, TuiIcon, TuiSkeleton],
|
||||
declarations: [CategoriesComponent],
|
||||
exports: [CategoriesComponent],
|
||||
})
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
<button tuiButton iconEnd="@tui.chevron-right" (click)="onPast()">
|
||||
Past Release Notes
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-container">
|
||||
<h2 class="additional-detail-title">About</h2>
|
||||
<h2 class="additional-detail-title" [style.margin-top.rem]="2">About</h2>
|
||||
<p>{{ pkg.description.long }}</p>
|
||||
<a
|
||||
*ngIf="pkg.marketingSite as url"
|
||||
|
||||
@@ -3,17 +3,10 @@
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.75rem;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
grid-column: span 5 / span 5;
|
||||
}
|
||||
@media (min-width: 1280px) {
|
||||
grid-column: span 4 / span 4;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class AboutComponent {
|
||||
|
||||
async onPast() {
|
||||
this.dialogs
|
||||
.open(RELEASE_NOTES, { label: 'Past Release Notes' })
|
||||
.open(RELEASE_NOTES, { label: 'Past Release Notes', data: this.pkg })
|
||||
.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { SharedPipesModule } from '@start9labs/shared'
|
||||
import { TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiAvatar } from '@taiga-ui/kit'
|
||||
import { TuiCell } from '@taiga-ui/layout'
|
||||
import { MarketplacePkg } from '../../../types'
|
||||
|
||||
@@ -9,23 +10,39 @@ import { MarketplacePkg } from '../../../types'
|
||||
standalone: true,
|
||||
selector: 'marketplace-flavors',
|
||||
template: `
|
||||
<h2>Alternative Implementations</h2>
|
||||
@for (pkg of pkgs; track $index) {
|
||||
<a
|
||||
tuiCell
|
||||
[routerLink]="['/marketplace', pkg.id]"
|
||||
[queryParams]="{ flavor: pkg.flavor }"
|
||||
>
|
||||
<img alt="" style="border-radius: 100%" [src]="pkg.icon | trustUrl" />
|
||||
<span tuiTitle>
|
||||
{{ pkg.title }}
|
||||
<span tuiSubtitle>{{ pkg.version }}</span>
|
||||
</span>
|
||||
</a>
|
||||
<div class="background-border box-shadow-lg shadow-color-light">
|
||||
<div class="box-container">
|
||||
<h2 class="additional-detail-title">Alternative Implementations</h2>
|
||||
@for (pkg of pkgs; track $index) {
|
||||
<a
|
||||
tuiCell
|
||||
[routerLink]="[]"
|
||||
[queryParams]="{ id: pkg.id, flavor: pkg.flavor }"
|
||||
>
|
||||
<tui-avatar [src]="pkg.icon | trustUrl" />
|
||||
<span tuiTitle>
|
||||
{{ pkg.title }}
|
||||
<span tuiSubtitle>{{ pkg.version }}</span>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: `
|
||||
.box-container {
|
||||
background-color: rgb(39 39 42);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.75rem;
|
||||
}
|
||||
|
||||
[tuiCell] {
|
||||
border-radius: 0.5rem;
|
||||
margin: 0 -1rem;
|
||||
}
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [RouterLink, TuiCell, TuiTitle, SharedPipesModule],
|
||||
imports: [RouterLink, TuiCell, TuiTitle, SharedPipesModule, TuiAvatar],
|
||||
})
|
||||
export class FlavorsComponent {
|
||||
@Input()
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
size="s"
|
||||
appearance="tertiary-solid"
|
||||
iconEnd="@tui.download"
|
||||
href="/eos/local.crt"
|
||||
href="/static/local-root-ca.crt"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
|
||||
@@ -87,22 +87,18 @@ import { ABOUT } from './about.component'
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
tui-icon {
|
||||
font-size: 1rem;
|
||||
:host {
|
||||
margin: 0 -0.5rem;
|
||||
}
|
||||
|
||||
tui-hosted-dropdown {
|
||||
margin: 0 -0.5rem;
|
||||
|
||||
[tuiIconButton] {
|
||||
height: calc(var(--tui-height-m) + 0.25rem);
|
||||
width: calc(var(--tui-height-m) + 0.625rem);
|
||||
}
|
||||
[tuiIconButton] {
|
||||
height: calc(var(--tui-height-m) + 0.25rem);
|
||||
width: calc(var(--tui-height-m) + 0.625rem);
|
||||
}
|
||||
|
||||
.item {
|
||||
justify-content: flex-start;
|
||||
gap: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
`,
|
||||
],
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { TuiButton, TuiNotification } from '@taiga-ui/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Component, inject, OnInit, signal } from '@angular/core'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { CT } from '@start9labs/start-sdk'
|
||||
import { TuiButton, TuiNotification } from '@taiga-ui/core'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||
import {
|
||||
|
||||
@@ -88,10 +88,10 @@ import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
|
||||
<button
|
||||
tuiButton
|
||||
type="button"
|
||||
appearance="primary"
|
||||
[appearance]="localFlavor ? 'warning' : 'primary'"
|
||||
(click)="tryInstall()"
|
||||
>
|
||||
Install
|
||||
{{ localFlavor ? 'Switch' : 'Install' }}
|
||||
</button>
|
||||
}
|
||||
`,
|
||||
@@ -119,6 +119,9 @@ export class MarketplaceControlsComponent {
|
||||
@Input()
|
||||
localPkg!: PackageDataEntry | null
|
||||
|
||||
@Input()
|
||||
localFlavor!: boolean
|
||||
|
||||
readonly showDevTools$ = inject(ClientStorageService).showDevTools$
|
||||
|
||||
async tryInstall() {
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
import { TuiDropdownService, TuiButton } from '@taiga-ui/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
Input,
|
||||
input,
|
||||
} from '@angular/core'
|
||||
import { toObservable, toSignal } from '@angular/core/rxjs-interop'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { ItemModule, MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { TuiSidebar } from '@taiga-ui/addon-mobile'
|
||||
import { TuiAutoFocus, TuiClickOutside } from '@taiga-ui/cdk'
|
||||
import { debounceTime, map } from 'rxjs'
|
||||
import { TuiButton, TuiDropdownService } from '@taiga-ui/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
debounceTime,
|
||||
filter,
|
||||
map,
|
||||
Observable,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
} from 'rxjs'
|
||||
import {
|
||||
DataModel,
|
||||
PackageDataEntry,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { getManifest } from 'src/app/utils/get-package-data'
|
||||
import { MarketplacePreviewComponent } from '../modals/preview.component'
|
||||
import { ToLocalPipe } from '../pipes/to-local.pipe'
|
||||
import { MarketplaceSidebarService } from '../services/sidebar.service'
|
||||
import { MarketplaceControlsComponent } from './controls.component'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-tile',
|
||||
template: `
|
||||
<marketplace-item [pkg]="pkg" (click)="toggle(true)">
|
||||
<marketplace-item [pkg]="pkg()" (click)="toggle(true)">
|
||||
<marketplace-preview
|
||||
*tuiSidebar="
|
||||
(id$ | async) === pkg.id;
|
||||
direction: 'right';
|
||||
autoWidth: true
|
||||
"
|
||||
[pkgId]="pkg.id"
|
||||
*tuiSidebar="!!open(); direction: 'right'; autoWidth: true"
|
||||
[pkgId]="pkg().id"
|
||||
class="preview-wrapper"
|
||||
(tuiClickOutside)="toggle(false)"
|
||||
>
|
||||
@@ -44,8 +55,9 @@ import { MarketplaceControlsComponent } from './controls.component'
|
||||
<marketplace-controls
|
||||
slot="controls"
|
||||
class="controls-wrapper"
|
||||
[pkg]="pkg"
|
||||
[localPkg]="pkg.id | toLocal | async"
|
||||
[pkg]="pkg()"
|
||||
[localPkg]="local$ | async"
|
||||
[localFlavor]="!!(flavor$ | async)"
|
||||
/>
|
||||
</marketplace-preview>
|
||||
</marketplace-item>
|
||||
@@ -104,7 +116,6 @@ import { MarketplaceControlsComponent } from './controls.component'
|
||||
imports: [
|
||||
CommonModule,
|
||||
ItemModule,
|
||||
ToLocalPipe,
|
||||
TuiAutoFocus,
|
||||
TuiClickOutside,
|
||||
TuiSidebar,
|
||||
@@ -114,18 +125,44 @@ import { MarketplaceControlsComponent } from './controls.component'
|
||||
],
|
||||
})
|
||||
export class MarketplaceTileComponent {
|
||||
private readonly exver = inject(Exver)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
private readonly router = inject(Router)
|
||||
readonly id$ = inject(ActivatedRoute).queryParamMap.pipe(
|
||||
map(map => map.get('id') || ''),
|
||||
debounceTime(100),
|
||||
private readonly params = toSignal(
|
||||
inject(ActivatedRoute).queryParamMap.pipe(debounceTime(100)),
|
||||
)
|
||||
|
||||
@Input({ required: true })
|
||||
pkg!: MarketplacePkg
|
||||
readonly pkg = input.required<MarketplacePkg>()
|
||||
readonly open = computed(
|
||||
() =>
|
||||
this.params()?.get('id') === this.pkg()?.id &&
|
||||
this.params()?.get('flavor') === this.pkg()?.flavor,
|
||||
)
|
||||
|
||||
readonly local$: Observable<PackageDataEntry | null> = toObservable(
|
||||
this.pkg,
|
||||
).pipe(
|
||||
switchMap(({ id, flavor }) =>
|
||||
this.patch.watch$('packageData', id).pipe(
|
||||
filter(Boolean),
|
||||
map(pkg =>
|
||||
this.exver.getFlavor(getManifest(pkg).version) === flavor
|
||||
? pkg
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
)
|
||||
|
||||
readonly flavor$ = this.local$.pipe(map(pkg => !pkg))
|
||||
|
||||
toggle(open: boolean) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { id: open ? this.pkg.id : null },
|
||||
queryParams: {
|
||||
id: open ? this.pkg().id : null,
|
||||
flavor: open ? this.pkg().flavor : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
height: calc(100vh - 3.5rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@@ -131,7 +130,7 @@ import { MarketplaceSidebarsComponent } from './components/sidebars.component'
|
||||
}
|
||||
|
||||
:host-context(tui-root._mobile) {
|
||||
height: calc(100vh - 3.5rem - var(--tui-height-l));
|
||||
padding: 0;
|
||||
}
|
||||
`,
|
||||
],
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
AboutModule,
|
||||
AbstractMarketplaceService,
|
||||
AdditionalModule,
|
||||
FlavorsComponent,
|
||||
MarketplaceAdditionalItemComponent,
|
||||
MarketplaceDependenciesComponent,
|
||||
MarketplacePackageHeroComponent,
|
||||
@@ -26,7 +27,16 @@ import {
|
||||
TuiLoader,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiRadioList, TuiStringifyContentPipe } from '@taiga-ui/kit'
|
||||
import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap,
|
||||
} from 'rxjs'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-preview',
|
||||
@@ -34,34 +44,36 @@ import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
|
||||
<div class="outer-container">
|
||||
<ng-content select="[slot=close]" />
|
||||
@if (pkg$ | async; as pkg) {
|
||||
@if (loading$ | async) {
|
||||
<tui-loader class="loading" textContent="Loading" />
|
||||
} @else {
|
||||
<marketplace-package-hero [pkg]="pkg">
|
||||
<ng-content select="[slot=controls]" />
|
||||
</marketplace-package-hero>
|
||||
<div class="inner-container">
|
||||
<marketplace-about [pkg]="pkg" />
|
||||
@if (!(pkg.dependencyMetadata | empty)) {
|
||||
<marketplace-dependencies
|
||||
[pkg]="pkg"
|
||||
(open)="open($event)"
|
||||
></marketplace-dependencies>
|
||||
}
|
||||
<marketplace-additional [pkg]="pkg">
|
||||
<marketplace-package-hero [pkg]="pkg">
|
||||
<ng-content select="[slot=controls]" />
|
||||
</marketplace-package-hero>
|
||||
<div class="inner-container">
|
||||
@if (flavors$ | async; as flavors) {
|
||||
<marketplace-flavors [pkgs]="flavors" />
|
||||
}
|
||||
<marketplace-about [pkg]="pkg" />
|
||||
@if (!(pkg.dependencyMetadata | empty)) {
|
||||
<marketplace-dependencies [pkg]="pkg" (open)="open($event)" />
|
||||
}
|
||||
<marketplace-additional [pkg]="pkg">
|
||||
@if (versions$ | async; as versions) {
|
||||
<marketplace-additional-item
|
||||
(click)="presentAlertVersions(pkg, version)"
|
||||
data="Click to view all versions"
|
||||
(click)="versions.length ? selectVersion(pkg, version) : 0"
|
||||
[data]="
|
||||
versions.length
|
||||
? 'Click to view all versions'
|
||||
: 'No other versions'
|
||||
"
|
||||
label="All versions"
|
||||
icon="@tui.chevron-right"
|
||||
class="versions"
|
||||
></marketplace-additional-item>
|
||||
/>
|
||||
<ng-template
|
||||
#version
|
||||
let-data="data"
|
||||
let-completeWith="completeWith"
|
||||
>
|
||||
<tui-radio-list [items]="data.items" [(ngModel)]="data.value" />
|
||||
<tui-radio-list [items]="versions" [(ngModel)]="data.value" />
|
||||
<footer class="buttons">
|
||||
<button
|
||||
tuiButton
|
||||
@@ -73,15 +85,17 @@ import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
|
||||
<button
|
||||
tuiButton
|
||||
appearance="secondary"
|
||||
(click)="loading$.next(true); completeWith(data.value)"
|
||||
(click)="completeWith(data.value)"
|
||||
>
|
||||
Ok
|
||||
</button>
|
||||
</footer>
|
||||
</ng-template>
|
||||
</marketplace-additional>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</marketplace-additional>
|
||||
</div>
|
||||
} @else {
|
||||
<tui-loader class="loading" textContent="Loading" />
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -164,54 +178,72 @@ import { BehaviorSubject, filter, switchMap, tap } from 'rxjs'
|
||||
TuiRadioList,
|
||||
TuiLoader,
|
||||
TuiIcon,
|
||||
FlavorsComponent,
|
||||
],
|
||||
})
|
||||
export class MarketplacePreviewComponent {
|
||||
@Input({ required: true })
|
||||
pkgId!: string
|
||||
|
||||
readonly loading$ = new BehaviorSubject(true)
|
||||
|
||||
private readonly dialogs = inject(TuiDialogService)
|
||||
private readonly exver = inject(Exver)
|
||||
private readonly router = inject(Router)
|
||||
private readonly marketplaceService = inject(AbstractMarketplaceService)
|
||||
readonly url = this.router.routerState.snapshot.root.queryParamMap.get('url')
|
||||
|
||||
readonly loadVersion$ = new BehaviorSubject<string>('*')
|
||||
readonly pkg$ = this.loadVersion$.pipe(
|
||||
switchMap(version =>
|
||||
this.marketplaceService.getPackage$(this.pkgId, version, this.url),
|
||||
),
|
||||
tap(data => {
|
||||
this.loading$.next(false)
|
||||
return data
|
||||
}),
|
||||
private readonly version$ = new BehaviorSubject<string>('*')
|
||||
private readonly flavor$ = this.router.routerState.root.queryParamMap.pipe(
|
||||
map(paramMap => paramMap.get('flavor')),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly dialogs: TuiDialogService,
|
||||
private readonly exver: Exver,
|
||||
) {}
|
||||
readonly pkg$ = combineLatest([this.version$, this.flavor$]).pipe(
|
||||
switchMap(([version, flavor]) =>
|
||||
this.marketplaceService
|
||||
.getPackage$(this.pkgId, version, flavor)
|
||||
.pipe(startWith(null)),
|
||||
),
|
||||
)
|
||||
|
||||
readonly flavors$ = this.flavor$.pipe(
|
||||
switchMap(current =>
|
||||
this.marketplaceService
|
||||
.getSelectedStore$()
|
||||
.pipe(
|
||||
map(({ packages }) =>
|
||||
packages.filter(
|
||||
({ id, flavor }) => id === this.pkgId && flavor !== current,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
readonly versions$ = combineLatest([
|
||||
this.pkg$.pipe(filter(Boolean)),
|
||||
this.flavor$,
|
||||
]).pipe(
|
||||
map(([{ otherVersions }, flavor]) =>
|
||||
Object.keys(otherVersions)
|
||||
.filter(v => this.exver.getFlavor(v) === flavor)
|
||||
.sort((a, b) => -1 * (this.exver.compareExver(a, b) || 0)),
|
||||
),
|
||||
)
|
||||
|
||||
open(id: string) {
|
||||
this.router.navigate([], { queryParams: { id } })
|
||||
}
|
||||
|
||||
presentAlertVersions(
|
||||
pkg: MarketplacePkg,
|
||||
version: TemplateRef<TuiDialogContext>,
|
||||
selectVersion(
|
||||
{ version }: MarketplacePkg,
|
||||
template: TemplateRef<TuiDialogContext>,
|
||||
) {
|
||||
this.dialogs
|
||||
.open<string>(version, {
|
||||
.open<string>(template, {
|
||||
label: 'Versions',
|
||||
size: 's',
|
||||
data: {
|
||||
value: pkg.version,
|
||||
items: [...new Set(Object.keys(pkg.otherVersions))].sort(
|
||||
(a, b) => -1 * (this.exver.compareExver(a, b) || 0),
|
||||
),
|
||||
value: version,
|
||||
},
|
||||
})
|
||||
.pipe(filter(Boolean))
|
||||
.subscribe(version => this.loadVersion$.next(version))
|
||||
.subscribe(version => this.version$.next(version))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { filter, Observable } from 'rxjs'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Pipe({
|
||||
name: 'toLocal',
|
||||
standalone: true,
|
||||
})
|
||||
export class ToLocalPipe implements PipeTransform {
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
|
||||
transform(id: string): Observable<PackageDataEntry> {
|
||||
return this.patch.watch$('packageData', id).pipe(filter(Boolean))
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,25 @@ export class FilterUpdatesPipe implements PipeTransform {
|
||||
|
||||
transform(
|
||||
pkgs?: MarketplacePkg[],
|
||||
local?: Record<string, PackageDataEntry<InstalledState | UpdatingState>>,
|
||||
local: Record<
|
||||
string,
|
||||
PackageDataEntry<InstalledState | UpdatingState>
|
||||
> = {},
|
||||
): MarketplacePkg[] | null {
|
||||
return (
|
||||
pkgs?.filter(
|
||||
({ version, id }) =>
|
||||
this.exver.compareExver(
|
||||
version,
|
||||
local?.[id]?.stateInfo.manifest.version || '',
|
||||
) === 1,
|
||||
({ 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
|
||||
}
|
||||
|
||||
@@ -521,7 +521,7 @@ export class MockApiService extends ApiService {
|
||||
versionRange: string,
|
||||
): Promise<GetPackageRes> {
|
||||
await pauseFor(2000)
|
||||
if (!versionRange) {
|
||||
if (!versionRange || versionRange === '=*') {
|
||||
return Mock.RegistryPackages[id]
|
||||
} else {
|
||||
return Mock.OtherPackageVersions[id][versionRange]
|
||||
|
||||
@@ -189,7 +189,6 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
this.marketplace$.pipe(
|
||||
switchMap(m => {
|
||||
const url = registryUrl || selected.url
|
||||
|
||||
const pkg = m[url]?.packages.find(
|
||||
p =>
|
||||
p.id === id &&
|
||||
@@ -197,9 +196,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
(!version || this.exver.compareExver(p.version, version) === 0),
|
||||
)
|
||||
|
||||
return !!pkg
|
||||
? of(pkg)
|
||||
: this.fetchPackage$(url, id, version, flavor)
|
||||
return pkg ? of(pkg) : this.fetchPackage$(url, id, version, flavor)
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -229,7 +226,18 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
}
|
||||
|
||||
fetchInfo$(url: string): Observable<T.RegistryInfo> {
|
||||
return from(this.api.getRegistryInfo(url))
|
||||
return from(this.api.getRegistryInfo(url)).pipe(
|
||||
map(info => ({
|
||||
...info,
|
||||
categories: {
|
||||
all: {
|
||||
name: 'All',
|
||||
description: { short: 'All services', long: 'All services' },
|
||||
},
|
||||
...info.categories,
|
||||
},
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
fetchStatic$(
|
||||
@@ -269,7 +277,7 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
|
||||
convertToMarketplacePkg(
|
||||
id: string,
|
||||
version: string | null,
|
||||
version: string | null | undefined,
|
||||
flavor: string | null,
|
||||
pkgInfo: GetPackageRes,
|
||||
): MarketplacePkg {
|
||||
@@ -299,7 +307,12 @@ export class MarketplaceService implements AbstractMarketplaceService {
|
||||
this.api.getRegistryPackage(url, id, version ? `=${version}` : null),
|
||||
).pipe(
|
||||
map(pkgInfo =>
|
||||
this.convertToMarketplacePkg(id, version, flavor, pkgInfo),
|
||||
this.convertToMarketplacePkg(
|
||||
id,
|
||||
version === '*' ? null : version,
|
||||
flavor,
|
||||
pkgInfo,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user