mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Feat/marketplace show links (#2105)
* closes #2084, rearranges marketplace show, app show, and donation link for Start9 * use url query param if present when fetching license and instructions * remove log * chore: Add some checking * chore: Update something about validation * chore: Update to use correct default Co-authored-by: BluJ <mogulslayer@gmail.com>
This commit is contained in:
committed by
Aiden McClelland
parent
212e94756b
commit
46222e9352
@@ -76,6 +76,9 @@ pub struct Manifest {
|
||||
pub dependencies: Dependencies,
|
||||
#[model]
|
||||
pub containers: Option<DockerContainers>,
|
||||
|
||||
#[serde(default)]
|
||||
pub replaces: Vec<String>,
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
|
||||
@@ -24,6 +24,9 @@ use crate::s9pk::docker::DockerReader;
|
||||
use crate::util::Version;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
const MAX_REPLACES: usize = 10;
|
||||
const MAX_TITLE_LEN: usize = 30;
|
||||
|
||||
#[pin_project::pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct ReadHandle<'a, R = File> {
|
||||
@@ -231,6 +234,35 @@ impl<R: AsyncRead + AsyncSeek + Unpin + Send + Sync> S9pkReader<R> {
|
||||
&validated_image_ids,
|
||||
)?;
|
||||
|
||||
#[cfg(feature = "js_engine")]
|
||||
if man.containers.is_some()
|
||||
|| matches!(man.main, crate::procedure::PackageProcedure::Script(_))
|
||||
{
|
||||
return Err(Error::new(
|
||||
eyre!("Right now we don't support the containers and the long running main"),
|
||||
crate::ErrorKind::ValidateS9pk,
|
||||
));
|
||||
}
|
||||
|
||||
if man.replaces.len() >= MAX_REPLACES {
|
||||
return Err(Error::new(
|
||||
eyre!("Cannot have more than {MAX_REPLACES} replaces"),
|
||||
crate::ErrorKind::ValidateS9pk,
|
||||
));
|
||||
}
|
||||
if let Some(too_big) = man.replaces.iter().find(|x| x.len() >= MAX_REPLACES) {
|
||||
return Err(Error::new(
|
||||
eyre!("We have found a replaces of ({too_big}) that exceeds the max length of {MAX_TITLE_LEN} "),
|
||||
crate::ErrorKind::ValidateS9pk,
|
||||
));
|
||||
}
|
||||
if man.title.len() >= MAX_TITLE_LEN {
|
||||
return Err(Error::new(
|
||||
eyre!("Cannot have more than a length of {MAX_TITLE_LEN} for title"),
|
||||
crate::ErrorKind::ValidateS9pk,
|
||||
));
|
||||
}
|
||||
|
||||
if man.containers.is_some()
|
||||
&& matches!(man.main, crate::procedure::PackageProcedure::Docker(_))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@start9labs/marketplace",
|
||||
"version": "0.3.8",
|
||||
"version": "0.3.9",
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=13.2.0",
|
||||
"@angular/core": ">=13.2.0",
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
<ion-item-divider>Description</ion-item-divider>
|
||||
<ion-item lines="none" color="transparent">
|
||||
<ion-label>
|
||||
{{ pkg.manifest.description.long }}
|
||||
<h2>{{ pkg.manifest.description.long }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
<div
|
||||
*ngIf="pkg.manifest['marketing-site'] as url"
|
||||
style="padding: 4px 0 10px 14px"
|
||||
>
|
||||
<ion-button [href]="url" target="_blank" rel="noreferrer" color="tertiary">
|
||||
View marketing website
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="pkg.manifest.replaces as replaces">
|
||||
<div *ngIf="replaces.length" class="ion-padding-bottom">
|
||||
<ion-item-divider>Intended to replace</ion-item-divider>
|
||||
<ul>
|
||||
<li *ngFor="let app of replaces">
|
||||
{{ app }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ion-item-divider>Additional Info</ion-item-divider>
|
||||
<ion-grid *ngIf="pkg.manifest as manifest">
|
||||
<ion-row>
|
||||
@@ -89,19 +110,6 @@
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['marketing-site']"
|
||||
[disabled]="!manifest['marketing-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Marketing Site</h2>
|
||||
<p>{{ manifest['marketing-site'] || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from '@start9labs/shared'
|
||||
import { MarketplacePkg } from '../../../types'
|
||||
import { AbstractMarketplaceService } from '../../../services/marketplace.service'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
|
||||
@Component({
|
||||
selector: 'marketplace-additional',
|
||||
@@ -31,12 +32,15 @@ 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 marketplaceService: AbstractMarketplaceService,
|
||||
private readonly toastCtrl: ToastController,
|
||||
private readonly route: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
async copy(address: string): Promise<void> {
|
||||
@@ -84,6 +88,7 @@ export class AdditionalComponent {
|
||||
const content = this.marketplaceService.fetchStatic$(
|
||||
this.pkg.manifest.id,
|
||||
title,
|
||||
this.url,
|
||||
)
|
||||
|
||||
const modal = await this.modalCtrl.create({
|
||||
|
||||
@@ -47,6 +47,7 @@ export interface MarketplaceManifest<T = unknown> {
|
||||
short: string
|
||||
long: string
|
||||
}
|
||||
replaces?: string[]
|
||||
'release-notes': string
|
||||
license: string // type of license
|
||||
'wrapper-repo': Url
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['marketing-site']"
|
||||
[disabled]="!manifest['marketing-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Marketing Site</h2>
|
||||
<p>{{ manifest['marketing-site'] || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
@@ -77,6 +90,19 @@
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['donation-url']"
|
||||
[disabled]="!manifest['donation-url']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Donation Link</h2>
|
||||
<p>{{ manifest['donation-url'] || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Inject, Pipe, PipeTransform } from '@angular/core'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { AlertController, ModalController, NavController } from '@ionic/angular'
|
||||
import { ModalController, NavController } from '@ionic/angular'
|
||||
import { MarkdownComponent } from '@start9labs/shared'
|
||||
import {
|
||||
DataModel,
|
||||
@@ -26,8 +25,6 @@ export interface Button {
|
||||
})
|
||||
export class ToButtonsPipe implements PipeTransform {
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private readonly document: Document,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly modalCtrl: ModalController,
|
||||
@@ -97,13 +94,6 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
},
|
||||
// view in marketplace
|
||||
this.viewInMarketplaceButton(pkg),
|
||||
// donate
|
||||
{
|
||||
action: () => this.donate(pkg),
|
||||
title: 'Donate',
|
||||
description: `Support ${pkgTitle}`,
|
||||
icon: 'logo-bitcoin',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -148,17 +138,4 @@ export class ToButtonsPipe implements PipeTransform {
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
private async donate({ manifest }: PackageDataEntry): Promise<void> {
|
||||
const url = manifest['donation-url']
|
||||
if (url) {
|
||||
this.document.defaultView?.open(url, '_blank', 'noreferrer')
|
||||
} else {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Not Accepting Donations',
|
||||
message: `The developers of ${manifest.title} have not provided a donation URL. Please contact them directly if you insist on giving them money.`,
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,6 +519,19 @@ export class ServerShowPage {
|
||||
detail: true,
|
||||
disabled$: of(false),
|
||||
},
|
||||
{
|
||||
title: 'Donate to Start9',
|
||||
description: `Support embassyOS development`,
|
||||
icon: 'logo-bitcoin',
|
||||
action: () =>
|
||||
this.document.defaultView?.open(
|
||||
'https://donate.start9.com',
|
||||
'_blank',
|
||||
'noreferrer',
|
||||
),
|
||||
detail: true,
|
||||
disabled$: of(false),
|
||||
},
|
||||
],
|
||||
Power: [
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ export module Mock {
|
||||
short: 'A Bitcoin full node by Bitcoin Core.',
|
||||
long: 'Bitcoin is a decentralized consensus protocol and settlement network.',
|
||||
},
|
||||
replaces: ['banks', 'governments'],
|
||||
'release-notes': 'Taproot, Schnorr, and more.',
|
||||
assets: {
|
||||
icon: 'icon.png',
|
||||
|
||||
Reference in New Issue
Block a user