mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 04:01:58 +00:00
Feature/fe new registry (#2647)
* bugfixes * update fe types * implement new registry types in marketplace and ui * fix marketplace types to have default params * add alt implementation toggle * merge cleanup * more cleanup and notes * fix build * cleanup sync with next/minor * add exver JS parser * parse ValidExVer to string * update types to interface * add VersionRange and comparative functions * Parse ExtendedVersion from string * add conjunction, disjunction, and inversion logic * consider flavor in satisfiedBy fn * consider prerelease for ordering * add compare fn for sorting * rename fns for consistency * refactoring * update compare fn to return null if flavors don't match * begin simplifying dependencies * under construction * wip * add dependency metadata to CurrentDependencyInfo * ditch inheritance for recursive VersionRange constructor. Recursive 'satisfiedBy' fn wip * preprocess manifest * misc fixes * use sdk version as osVersion in manifest * chore: Change the type to just validate and not generate all solutions. * add publishedAt * fix pegjs exports * integrate exver into sdk * misc fixes * complete satisfiedBy fn * refactor - use greaterThanOrEqual and lessThanOrEqual fns * fix tests * update dependency details * update types * remove interim types * rename alt implementation to flavor * cleanup os update * format exver.ts * add s9pk parsing endpoints * fix build * update to exver * exver and bug fixes * update static endpoints + cleanup * cleanup * update static proxy verification * make mocks more robust; fix dep icon fallback; cleanup * refactor alert versions and update fixtures * registry bugfixes * misc fixes * cleanup unused * convert patchdb ui seed to camelCase * update otherVersions type * change otherVersions: null to 'none' * refactor and complete feature * improve static endpoints * fix install params * mask systemd-networkd-wait-online * fix static file fetching * include non-matching versions in otherVersions * convert release notes to modal and clean up displayExver * alert for no other versions * Fix ack-instructions casing * fix indeterminate loader on service install --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
<ion-content class="with-widgets">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Past Release Notes</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="dismiss()" class="enter-click">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding with-widgets">
|
||||
<ng-container *ngIf="notes$ | async as notes; else loading">
|
||||
<div *ngFor="let note of notes | keyvalue: asIsOrder">
|
||||
<div *ngFor="let note of notes | keyvalue : asIsOrder">
|
||||
<ion-button
|
||||
expand="full"
|
||||
color="light"
|
||||
@@ -8,7 +19,7 @@
|
||||
[class.ion-activated]="isSelected(note.key)"
|
||||
(click)="setSelected(note.key)"
|
||||
>
|
||||
<p class="version">{{ note.key | displayEmver }}</p>
|
||||
<p class="version">{{ note.key }}</p>
|
||||
</ion-button>
|
||||
<ion-card
|
||||
tuiElement
|
||||
@@ -20,4 +20,5 @@
|
||||
.version {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
text-transform: none;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { AbstractMarketplaceService } from '../../services/marketplace.service'
|
||||
import { MarketplacePkg } from '../../types'
|
||||
import { map } from 'rxjs'
|
||||
import { Exver } from '@start9labs/shared'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
|
||||
@Component({
|
||||
selector: 'release-notes',
|
||||
templateUrl: './release-notes.component.html',
|
||||
styleUrls: ['./release-notes.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ReleaseNotesComponent {
|
||||
@Input() pkg!: MarketplacePkg
|
||||
|
||||
private selected: string | null = null
|
||||
|
||||
readonly notes$ = this.marketplaceService.getSelectedStore$().pipe(
|
||||
map(s => {
|
||||
return Object.entries(this.pkg.otherVersions)
|
||||
.filter(
|
||||
([v, _]) =>
|
||||
this.exver.getFlavor(v) === this.pkg.flavor &&
|
||||
this.exver.compareExver(this.pkg.version, v) === 1,
|
||||
)
|
||||
.reduce(
|
||||
(obj, [version, info]) => ({
|
||||
...obj,
|
||||
[version]: info.releaseNotes,
|
||||
}),
|
||||
{
|
||||
[`${this.pkg.version} (current)`]: this.pkg.releaseNotes,
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly marketplaceService: AbstractMarketplaceService,
|
||||
private readonly exver: Exver,
|
||||
private readonly modalCtrl: ModalController,
|
||||
) {}
|
||||
|
||||
async dismiss() {
|
||||
return this.modalCtrl.dismiss()
|
||||
}
|
||||
|
||||
isSelected(key: string): boolean {
|
||||
return this.selected === key
|
||||
}
|
||||
|
||||
setSelected(selected: string) {
|
||||
this.selected = this.isSelected(selected) ? null : selected
|
||||
}
|
||||
|
||||
getDocSize(key: string, { nativeElement }: ElementRef<HTMLElement>) {
|
||||
return this.isSelected(key) ? nativeElement.scrollHeight : 0
|
||||
}
|
||||
|
||||
asIsOrder(a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,11 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
TextSpinnerComponentModule,
|
||||
} from '@start9labs/shared'
|
||||
import { TuiElementModule } from '@taiga-ui/cdk'
|
||||
|
||||
import { ReleaseNotesComponent } from './release-notes.component'
|
||||
|
||||
@NgModule({
|
||||
@@ -15,11 +14,11 @@ import { ReleaseNotesComponent } from './release-notes.component'
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TextSpinnerComponentModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
MarkdownPipeModule,
|
||||
TuiElementModule,
|
||||
],
|
||||
declarations: [ReleaseNotesComponent],
|
||||
exports: [ReleaseNotesComponent],
|
||||
})
|
||||
export class ReleaseNotesModule {}
|
||||
export class ReleaseNotesComponentModule {}
|
||||
@@ -1,9 +1,9 @@
|
||||
<ion-button
|
||||
*ngFor="let cat of categories"
|
||||
*ngFor="let cat of categories | keyvalue : originalOrder"
|
||||
fill="clear"
|
||||
class="category"
|
||||
[class.category_selected]="cat === category"
|
||||
(click)="switchCategory(cat)"
|
||||
[class.category_selected]="cat.key === category"
|
||||
(click)="switchCategory(cat.key)"
|
||||
>
|
||||
{{ cat }}
|
||||
{{ cat.value.name }}
|
||||
</ion-button>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-categories',
|
||||
@@ -17,7 +18,7 @@ import {
|
||||
})
|
||||
export class CategoriesComponent {
|
||||
@Input()
|
||||
categories: readonly string[] = []
|
||||
categories!: Map<string, T.Category>
|
||||
|
||||
@Input()
|
||||
category = ''
|
||||
@@ -29,4 +30,8 @@ export class CategoriesComponent {
|
||||
this.category = category
|
||||
this.categoryChange.emit(category)
|
||||
}
|
||||
|
||||
originalOrder() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<ion-item class="service-card" [routerLink]="['/marketplace', pkg.manifest.id]">
|
||||
<ion-item
|
||||
class="service-card"
|
||||
[routerLink]="['/marketplace', pkg.id]"
|
||||
[queryParams]="{ flavor: pkg.flavor, version: pkg.version }"
|
||||
>
|
||||
<ion-thumbnail slot="start">
|
||||
<img alt="" [src]="pkg.icon | trustUrl" />
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2 class="montserrat">
|
||||
<strong>{{ pkg.manifest.title }}</strong>
|
||||
<strong>{{ pkg.title }}</strong>
|
||||
</h2>
|
||||
<h3>{{ pkg.manifest.description.short }}</h3>
|
||||
<h3>{{ pkg.description.short }}</h3>
|
||||
<ng-content></ng-content>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, ElementRef } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
import { AbstractMarketplaceService } from '../../services/marketplace.service'
|
||||
|
||||
@Component({
|
||||
selector: 'release-notes',
|
||||
templateUrl: './release-notes.component.html',
|
||||
styleUrls: ['./release-notes.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ReleaseNotesComponent {
|
||||
private readonly pkgId = getPkgId(this.route)
|
||||
|
||||
private selected: string | null = null
|
||||
|
||||
readonly notes$ = this.marketplaceService.fetchReleaseNotes$(this.pkgId)
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly marketplaceService: AbstractMarketplaceService,
|
||||
) {}
|
||||
|
||||
isSelected(key: string): boolean {
|
||||
return this.selected === key
|
||||
}
|
||||
|
||||
setSelected(selected: string) {
|
||||
this.selected = this.isSelected(selected) ? null : selected
|
||||
}
|
||||
|
||||
getDocSize(key: string, { nativeElement }: ElementRef<HTMLElement>) {
|
||||
return this.isSelected(key) ? nativeElement.scrollHeight : 0
|
||||
}
|
||||
|
||||
asIsOrder(a: any, b: any) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
<!-- release notes -->
|
||||
<ion-item-divider>
|
||||
New in {{ pkg.manifest.version | displayEmver }}
|
||||
</ion-item-divider>
|
||||
<ion-item-divider>New in {{ pkg.version }}</ion-item-divider>
|
||||
<ion-item lines="none" color="transparent">
|
||||
<ion-label>
|
||||
<div [innerHTML]="pkg.manifest.releaseNotes | markdown"></div>
|
||||
<div [innerHTML]="pkg.releaseNotes | markdown"></div>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button routerLink="notes" fill="clear" strong>
|
||||
<ion-button fill="clear" strong (click)="presentModalNotes()">
|
||||
Past Release Notes
|
||||
<ion-icon slot="end" name="arrow-forward"></ion-icon>
|
||||
</ion-button>
|
||||
@@ -15,10 +13,10 @@
|
||||
<ion-item-divider>Description</ion-item-divider>
|
||||
<ion-item lines="none" color="transparent">
|
||||
<ion-label>
|
||||
<h2>{{ pkg.manifest.description.long }}</h2>
|
||||
<h2>{{ pkg.description.long }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<div *ngIf="pkg.manifest.marketingSite as url" style="padding: 4px 0 10px 14px">
|
||||
<div *ngIf="pkg.marketingSite as url" style="padding: 4px 0 10px 14px">
|
||||
<ion-button [href]="url" target="_blank" rel="noreferrer" color="tertiary">
|
||||
View website
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { MarketplacePkg } from '../../../types'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { ReleaseNotesComponent } from '../../../modals/release-notes/release-notes.component'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-about',
|
||||
@@ -10,4 +12,15 @@ import { MarketplacePkg } from '../../../types'
|
||||
export class AboutComponent {
|
||||
@Input()
|
||||
pkg!: MarketplacePkg
|
||||
|
||||
constructor(private readonly modalCtrl: ModalController) {}
|
||||
|
||||
async presentModalNotes() {
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: { pkg: this.pkg },
|
||||
component: ReleaseNotesComponent,
|
||||
})
|
||||
|
||||
await modal.present()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { EmverPipesModule, MarkdownPipeModule } from '@start9labs/shared'
|
||||
|
||||
import { ExverPipesModule, MarkdownPipeModule } from '@start9labs/shared'
|
||||
import { AboutComponent } from './about.component'
|
||||
import { ReleaseNotesComponentModule } from '../../../modals/release-notes/release-notes.module'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -12,7 +12,8 @@ import { AboutComponent } from './about.component'
|
||||
RouterModule,
|
||||
IonicModule,
|
||||
MarkdownPipeModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
ReleaseNotesComponentModule,
|
||||
],
|
||||
declarations: [AboutComponent],
|
||||
exports: [AboutComponent],
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<ion-item-divider>Additional Info</ion-item-divider>
|
||||
<ion-grid *ngIf="pkg.manifest as manifest">
|
||||
<ion-grid *ngIf="pkg">
|
||||
<ion-row>
|
||||
<ion-col responsiveCol sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
*ngIf="manifest.gitHash as gitHash; else noHash"
|
||||
*ngIf="pkg.gitHash as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
@@ -33,7 +33,7 @@
|
||||
<ion-item button detail="false" (click)="presentModalMd('license')">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
<p>{{ pkg.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
@@ -53,39 +53,39 @@
|
||||
<ion-col responsiveCol sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest.upstreamRepo"
|
||||
[href]="pkg.upstreamRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest.upstreamRepo }}</p>
|
||||
<p>{{ pkg.upstreamRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.wrapperRepo"
|
||||
[href]="pkg.wrapperRepo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest.wrapperRepo }}</p>
|
||||
<p>{{ pkg.wrapperRepo }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest.supportSite"
|
||||
[disabled]="!manifest.supportSite"
|
||||
[href]="pkg.supportSite"
|
||||
[disabled]="!pkg.supportSite"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest.supportSite || 'Not provided' }}</p>
|
||||
<p>{{ pkg.supportSite || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
@@ -10,15 +10,9 @@ import {
|
||||
ModalController,
|
||||
ToastController,
|
||||
} from '@ionic/angular'
|
||||
import {
|
||||
copyToClipboard,
|
||||
displayEmver,
|
||||
Emver,
|
||||
MarkdownComponent,
|
||||
} from '@start9labs/shared'
|
||||
import { copyToClipboard, Exver, MarkdownComponent } from '@start9labs/shared'
|
||||
import { MarketplacePkg } from '../../../types'
|
||||
import { AbstractMarketplaceService } from '../../../services/marketplace.service'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-additional',
|
||||
@@ -32,15 +26,12 @@ export class AdditionalComponent {
|
||||
@Output()
|
||||
version = new EventEmitter<string>()
|
||||
|
||||
readonly url = this.route.snapshot.queryParamMap.get('url') || undefined
|
||||
|
||||
constructor(
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly emver: Emver,
|
||||
private readonly exver: Exver,
|
||||
private readonly marketplaceService: AbstractMarketplaceService,
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly route: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
async copy(address: string): Promise<void> {
|
||||
@@ -58,41 +49,53 @@ export class AdditionalComponent {
|
||||
}
|
||||
|
||||
async presentAlertVersions() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Versions',
|
||||
inputs: this.pkg.versions
|
||||
.sort((a, b) => -1 * (this.emver.compare(a, b) || 0))
|
||||
.map(v => ({
|
||||
name: v, // for CSS
|
||||
type: 'radio',
|
||||
label: displayEmver(v), // appearance on screen
|
||||
value: v, // literal SEM version value
|
||||
checked: this.pkg.manifest.version === v,
|
||||
})),
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Ok',
|
||||
handler: (version: string) => this.version.emit(version),
|
||||
},
|
||||
],
|
||||
})
|
||||
const versions = Object.keys(this.pkg.otherVersions).filter(
|
||||
v => this.exver.getFlavor(v) === this.pkg.flavor,
|
||||
)
|
||||
|
||||
await alert.present()
|
||||
if (!versions.length) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Versions',
|
||||
message: 'No other versions',
|
||||
})
|
||||
|
||||
await alert.present()
|
||||
} else {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Versions',
|
||||
inputs: versions
|
||||
.sort((a, b) => -1 * (this.exver.compareExver(a, b) || 0))
|
||||
.map(v => ({
|
||||
name: v, // for CSS
|
||||
type: 'radio',
|
||||
label: v, // appearance on screen
|
||||
value: v, // literal SEM version value
|
||||
checked: this.pkg.version === v,
|
||||
})),
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Ok',
|
||||
handler: (version: string) => this.version.emit(version),
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await alert.present()
|
||||
}
|
||||
}
|
||||
|
||||
async presentModalMd(title: string) {
|
||||
async presentModalMd(asset: 'license' | 'instructions') {
|
||||
const content = this.marketplaceService.fetchStatic$(
|
||||
this.pkg.manifest.id,
|
||||
title,
|
||||
this.url,
|
||||
this.pkg,
|
||||
asset === 'license' ? 'LICENSE.md' : 'instructions.md',
|
||||
)
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
componentProps: { title, content },
|
||||
componentProps: { title: asset, content },
|
||||
component: MarkdownComponent,
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col
|
||||
*ngFor="let dep of pkg.manifest.dependencies | keyvalue"
|
||||
*ngFor="let dep of pkg.dependencyMetadata | keyvalue"
|
||||
responsiveCol
|
||||
sizeSm="12"
|
||||
sizeMd="6"
|
||||
@@ -17,7 +17,11 @@
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2>
|
||||
{{ pkg.dependencyMetadata[dep.key].title }}
|
||||
{{
|
||||
pkg.dependencyMetadata[dep.key].title
|
||||
? pkg.dependencyMetadata[dep.key].title
|
||||
: dep.key
|
||||
}}
|
||||
<span *ngIf="dep.value.optional; else required">(optional)</span>
|
||||
<ng-template #required>
|
||||
<span>(Required)</span>
|
||||
|
||||
@@ -11,6 +11,7 @@ export class DependenciesComponent {
|
||||
pkg!: MarketplacePkg
|
||||
|
||||
getImg(key: string): string {
|
||||
return this.pkg.dependencyMetadata[key].icon
|
||||
const icon = this.pkg.dependencyMetadata[key]?.icon
|
||||
return icon ? icon : 'assets/img/service-icons/fallback.png'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ResponsiveColModule,
|
||||
SharedPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import { ResponsiveColModule, SharedPipesModule } from '@start9labs/shared'
|
||||
|
||||
import { DependenciesComponent } from './dependencies.component'
|
||||
|
||||
@@ -16,7 +12,6 @@ import { DependenciesComponent } from './dependencies.component'
|
||||
RouterModule,
|
||||
IonicModule,
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ResponsiveColModule,
|
||||
],
|
||||
declarations: [DependenciesComponent],
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<ion-item-divider>Alternative Implementations</ion-item-divider>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col *ngFor="let pkg of pkgs" responsiveCol sizeSm="12" sizeMd="6">
|
||||
<ion-item
|
||||
[routerLink]="['/marketplace', pkg.id]"
|
||||
[queryParams]="{ flavor: pkg.flavor }"
|
||||
>
|
||||
<ion-thumbnail slot="start">
|
||||
<img alt="" style="border-radius: 100%" [src]="pkg.icon | trustUrl" />
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2>
|
||||
{{ pkg.title }}
|
||||
</h2>
|
||||
<p>{{ pkg.version }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { MarketplacePkg } from '../../../types'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-flavors',
|
||||
templateUrl: 'flavors.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FlavorsComponent {
|
||||
@Input()
|
||||
pkgs!: MarketplacePkg[]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { ResponsiveColModule, SharedPipesModule } from '@start9labs/shared'
|
||||
import { FlavorsComponent } from './flavors.component'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
IonicModule,
|
||||
SharedPipesModule,
|
||||
ResponsiveColModule,
|
||||
],
|
||||
declarations: [FlavorsComponent],
|
||||
exports: [FlavorsComponent],
|
||||
})
|
||||
export class FlavorsModule {}
|
||||
@@ -1,9 +1,11 @@
|
||||
<div class="header montserrat">
|
||||
<img class="logo" alt="" [src]="pkg.icon | trustUrl" />
|
||||
<div class="text">
|
||||
<h1 ticker class="title">{{ pkg.manifest.title }}</h1>
|
||||
<p class="version">{{ pkg.manifest.version | displayEmver }}</p>
|
||||
<p class="published">Released: {{ pkg.publishedAt | date: 'medium' }}</p>
|
||||
<h1 ticker class="title">{{ pkg.title }}</h1>
|
||||
<p class="version">{{ pkg.version }}</p>
|
||||
<p class="published">
|
||||
Released: {{ pkg.s9pk.publishedAt | date : 'medium' }}
|
||||
</p>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
SharedPipesModule,
|
||||
TickerModule,
|
||||
} from '@start9labs/shared'
|
||||
@@ -16,7 +16,7 @@ import { PackageComponent } from './package.component'
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
SharedPipesModule,
|
||||
EmverPipesModule,
|
||||
ExverPipesModule,
|
||||
TickerModule,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -26,11 +26,11 @@ export class FilterPackagesPipe implements PipeTransform {
|
||||
distance: 16,
|
||||
keys: [
|
||||
{
|
||||
name: 'manifest.title',
|
||||
name: 'title',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
name: 'manifest.id',
|
||||
name: 'id',
|
||||
weight: 0.5,
|
||||
},
|
||||
],
|
||||
@@ -42,19 +42,19 @@ export class FilterPackagesPipe implements PipeTransform {
|
||||
useExtendedSearch: true,
|
||||
keys: [
|
||||
{
|
||||
name: 'manifest.title',
|
||||
name: 'title',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
name: 'manifest.id',
|
||||
name: 'id',
|
||||
weight: 0.5,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.short',
|
||||
name: 'description.short',
|
||||
weight: 0.4,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.long',
|
||||
name: 'description.long',
|
||||
weight: 0.1,
|
||||
},
|
||||
],
|
||||
@@ -71,7 +71,8 @@ export class FilterPackagesPipe implements PipeTransform {
|
||||
.filter(p => category === 'all' || p.categories.includes(category))
|
||||
.sort((a, b) => {
|
||||
return (
|
||||
new Date(b.publishedAt).valueOf() - new Date(a.publishedAt).valueOf()
|
||||
new Date(b.s9pk.publishedAt).valueOf() -
|
||||
new Date(a.s9pk.publishedAt).valueOf()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ export * from './pages/list/search/search.component'
|
||||
export * from './pages/list/search/search.module'
|
||||
export * from './pages/list/skeleton/skeleton.component'
|
||||
export * from './pages/list/skeleton/skeleton.module'
|
||||
export * from './pages/release-notes/release-notes.component'
|
||||
export * from './pages/release-notes/release-notes.module'
|
||||
export * from './pages/show/about/about.component'
|
||||
export * from './pages/show/about/about.module'
|
||||
export * from './pages/show/additional/additional.component'
|
||||
@@ -20,6 +18,8 @@ export * from './pages/show/dependencies/dependencies.component'
|
||||
export * from './pages/show/dependencies/dependencies.module'
|
||||
export * from './pages/show/package/package.component'
|
||||
export * from './pages/show/package/package.module'
|
||||
export * from './pages/show/flavors/flavors.component'
|
||||
export * from './pages/show/flavors/flavors.module'
|
||||
|
||||
export * from './pipes/filter-packages.pipe'
|
||||
|
||||
|
||||
@@ -12,18 +12,13 @@ export abstract class AbstractMarketplaceService {
|
||||
|
||||
abstract getPackage$(
|
||||
id: string,
|
||||
version: string,
|
||||
version: string | null,
|
||||
flavor: string | null,
|
||||
url?: string,
|
||||
): Observable<MarketplacePkg> // could be {} so need to check in show page
|
||||
|
||||
abstract fetchReleaseNotes$(
|
||||
id: string,
|
||||
url?: string,
|
||||
): Observable<Record<string, string>>
|
||||
): Observable<MarketplacePkg>
|
||||
|
||||
abstract fetchStatic$(
|
||||
id: string,
|
||||
type: string,
|
||||
url?: string,
|
||||
pkg: MarketplacePkg,
|
||||
type: 'LICENSE.md' | 'instructions.md',
|
||||
): Observable<string>
|
||||
}
|
||||
|
||||
@@ -1,48 +1,40 @@
|
||||
import { Url } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
|
||||
export type StoreURL = string
|
||||
export type StoreName = string
|
||||
|
||||
export interface StoreIdentity {
|
||||
url: StoreURL
|
||||
name?: StoreName
|
||||
export type GetPackageReq = {
|
||||
id: string
|
||||
version: string | null
|
||||
sourceVersion: null // @TODO what is this?
|
||||
otherVersions: 'short'
|
||||
}
|
||||
export type GetPackageRes = T.GetPackageResponse & {
|
||||
otherVersions: { [version: string]: T.PackageInfoShort }
|
||||
}
|
||||
export type Marketplace = Record<StoreURL, StoreData | null>
|
||||
|
||||
export interface StoreData {
|
||||
info: StoreInfo
|
||||
export type GetPackagesReq = {
|
||||
id: null
|
||||
version: null
|
||||
sourceVersion: null
|
||||
otherVersions: 'short'
|
||||
}
|
||||
export type GetPackagesRes = {
|
||||
[id: T.PackageId]: GetPackageRes
|
||||
}
|
||||
|
||||
export type StoreIdentity = {
|
||||
url: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export type Marketplace = Record<string, StoreData | null>
|
||||
|
||||
export type StoreData = {
|
||||
info: T.RegistryInfo
|
||||
packages: MarketplacePkg[]
|
||||
}
|
||||
|
||||
export interface StoreInfo {
|
||||
name: StoreName
|
||||
categories: string[]
|
||||
}
|
||||
|
||||
export type StoreIdentityWithData = StoreData & StoreIdentity
|
||||
|
||||
export interface MarketplacePkg {
|
||||
icon: Url
|
||||
license: Url
|
||||
instructions: Url
|
||||
manifest: T.Manifest
|
||||
categories: string[]
|
||||
versions: string[]
|
||||
dependencyMetadata: {
|
||||
[id: string]: DependencyMetadata
|
||||
export type MarketplacePkg = T.PackageVersionInfo &
|
||||
Omit<GetPackageRes, 'best'> & {
|
||||
id: T.PackageId
|
||||
version: string
|
||||
flavor: string | null
|
||||
}
|
||||
publishedAt: string
|
||||
}
|
||||
|
||||
export interface DependencyMetadata {
|
||||
title: string
|
||||
icon: Url
|
||||
optional: boolean
|
||||
hidden: boolean
|
||||
}
|
||||
|
||||
export interface Dependency {
|
||||
description: string | null
|
||||
optional: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user