fix: implement flavor across the app

This commit is contained in:
waterplea
2024-08-20 18:53:55 +04:00
parent befa9eb16d
commit fa93e195cb
20 changed files with 283 additions and 236 deletions

View File

@@ -30,7 +30,7 @@
size="s"
appearance="tertiary-solid"
iconEnd="@tui.download"
href="/eos/local.crt"
href="/static/local-root-ca.crt"
>
Download
</a>

View File

@@ -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;
}
`,
],

View File

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

View File

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

View File

@@ -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,
},
})
}
}

View File

@@ -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;
}
`,
],

View File

@@ -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))
}
}

View File

@@ -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))
}
}

View File

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