mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Multiple bugs and styling (#1975)
* remove updates from marketplace, fix search, link updates to marketplace, styling * add dependency checks and error handling to updates tab * add circle around back buttons
This commit is contained in:
committed by
Aiden McClelland
parent
9146c31abf
commit
2336e36314
@@ -2,12 +2,8 @@
|
||||
*ngFor="let cat of categories"
|
||||
fill="clear"
|
||||
class="category"
|
||||
[color]="cat === 'updates' && updatesAvailable ? 'success' : undefined"
|
||||
[class.category_selected]="cat === category"
|
||||
[class.category_selected]="cat === category && !disableCategories"
|
||||
(click)="switchCategory(cat)"
|
||||
>
|
||||
{{ cat }}
|
||||
<span *ngIf="cat === 'updates' && updatesAvailable">
|
||||
({{ updatesAvailable }})
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
@@ -23,7 +23,7 @@ export class CategoriesComponent {
|
||||
category = ''
|
||||
|
||||
@Input()
|
||||
updatesAvailable = 0
|
||||
disableCategories = false
|
||||
|
||||
@Output()
|
||||
readonly categoryChange = new EventEmitter<string>()
|
||||
|
||||
@@ -1,110 +1,108 @@
|
||||
<ion-item-divider>Additional Info</ion-item-divider>
|
||||
<ion-card *ngIf="pkg.manifest as manifest">
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
*ngIf="manifest['git-hash'] as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
>
|
||||
<ion-grid *ngIf="pkg.manifest as manifest">
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
*ngIf="manifest['git-hash'] as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>{{ gitHash }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="copy-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ng-template #noHash>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>{{ gitHash }}</p>
|
||||
<p>Unknown</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="copy-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ng-template #noHash>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>Unknown</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
<ion-item button detail="false" (click)="presentAlertVersions()">
|
||||
<ion-label>
|
||||
<h2>Other Versions</h2>
|
||||
<p>Click to view other versions</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item button detail="false" (click)="presentModalMd('license')">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
button
|
||||
detail="false"
|
||||
(click)="presentModalMd('instructions')"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Instructions</h2>
|
||||
<p>Click to view instructions</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest['upstream-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest['upstream-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['wrapper-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest['wrapper-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['support-site']"
|
||||
[disabled]="!manifest['support-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest['support-site'] || 'Not provided' }}</p>
|
||||
</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>
|
||||
</ion-grid>
|
||||
</ion-card>
|
||||
</ng-template>
|
||||
<ion-item button detail="false" (click)="presentAlertVersions()">
|
||||
<ion-label>
|
||||
<h2>Other Versions</h2>
|
||||
<p>Click to view other versions</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item button detail="false" (click)="presentModalMd('license')">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
button
|
||||
detail="false"
|
||||
(click)="presentModalMd('instructions')"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Instructions</h2>
|
||||
<p>Click to view instructions</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest['upstream-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest['upstream-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['wrapper-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest['wrapper-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['support-site']"
|
||||
[disabled]="!manifest['support-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest['support-site'] || 'Not provided' }}</p>
|
||||
</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>
|
||||
</ion-grid>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NgModule, Pipe, PipeTransform } from '@angular/core'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { MarketplaceManifest, MarketplacePkg } from '../types'
|
||||
import { MarketplacePkg } from '../types'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
@Pipe({
|
||||
@@ -13,75 +13,40 @@ export class FilterPackagesPipe implements PipeTransform {
|
||||
packages: MarketplacePkg[],
|
||||
query: string,
|
||||
category: string,
|
||||
local: Record<string, { manifest: MarketplaceManifest }> = {},
|
||||
): MarketplacePkg[] {
|
||||
// query
|
||||
if (query) {
|
||||
let options: Fuse.IFuseOptions<MarketplacePkg> = {
|
||||
includeScore: true,
|
||||
includeMatches: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
keys: [
|
||||
{
|
||||
name: 'manifest.title',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
name: 'manifest.id',
|
||||
weight: 0.5,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.short',
|
||||
weight: 0.4,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.long',
|
||||
weight: 0.1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if (query.length < 4) {
|
||||
options = {
|
||||
...options,
|
||||
threshold: 0,
|
||||
location: 0,
|
||||
distance: 1,
|
||||
keys: [
|
||||
{
|
||||
name: 'manifest.title',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
name: 'manifest.id',
|
||||
weight: 0.5,
|
||||
},
|
||||
],
|
||||
}
|
||||
} else {
|
||||
options = {
|
||||
...options,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
keys: [
|
||||
{
|
||||
name: 'manifest.title',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
name: 'manifest.id',
|
||||
weight: 0.5,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.short',
|
||||
weight: 0.4,
|
||||
},
|
||||
{
|
||||
name: 'manifest.description.long',
|
||||
weight: 0.1,
|
||||
},
|
||||
],
|
||||
}
|
||||
query = `'${query}`
|
||||
}
|
||||
query = `'${query}`
|
||||
|
||||
const fuse = new Fuse(packages, options)
|
||||
return fuse.search(query).map(p => p.item)
|
||||
}
|
||||
|
||||
// updates
|
||||
if (category === 'updates') {
|
||||
return packages.filter(
|
||||
({ manifest }) =>
|
||||
local[manifest.id] &&
|
||||
this.emver.compare(
|
||||
manifest.version,
|
||||
local[manifest.id].manifest.version,
|
||||
) === 1,
|
||||
)
|
||||
}
|
||||
|
||||
// category
|
||||
return packages
|
||||
.filter(p => category === 'all' || p.categories.includes(category))
|
||||
|
||||
@@ -8,7 +8,6 @@ import { StatusComponentModule } from 'src/app/components/status/status.componen
|
||||
import { AppConfigPageModule } from 'src/app/modals/app-config/app-config.module'
|
||||
import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module'
|
||||
import { UiPipeModule } from 'src/app/pipes/ui/ui.module'
|
||||
import { AppShowHeaderComponent } from './components/app-show-header/app-show-header.component'
|
||||
import { AppShowProgressComponent } from './components/app-show-progress/app-show-progress.component'
|
||||
import { AppShowStatusComponent } from './components/app-show-status/app-show-status.component'
|
||||
import { AppShowDependenciesComponent } from './components/app-show-dependencies/app-show-dependencies.component'
|
||||
@@ -38,7 +37,6 @@ const routes: Routes = [
|
||||
ToButtonsPipe,
|
||||
ToDependenciesPipe,
|
||||
ToStatusPipe,
|
||||
AppShowHeaderComponent,
|
||||
AppShowProgressComponent,
|
||||
AppShowStatusComponent,
|
||||
AppShowDependenciesComponent,
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
<ng-container *ngIf="pkg$ | async as pkg">
|
||||
<app-show-header [pkg]="pkg"></app-show-header>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button defaultHref="services"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Service Details</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<ng-container *ngIf="pkg$ | async as pkg">
|
||||
<div class="ion-text-center ion-padding-top">
|
||||
<img [src]="pkg['static-files'].icon" alt="" style="width: 84px" />
|
||||
<h1>{{ pkg.manifest.title }}</h1>
|
||||
<h4>{{ pkg.manifest.version | displayEmver }}</h4>
|
||||
</div>
|
||||
|
||||
<ion-content>
|
||||
<!-- ** installing, updating, restoring ** -->
|
||||
<ng-container *ngIf="showProgress(pkg); else installed">
|
||||
<app-show-progress
|
||||
@@ -73,5 +86,5 @@
|
||||
</ion-grid>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ion-content>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,85 +1,83 @@
|
||||
<ion-item-divider>Additional Info</ion-item-divider>
|
||||
<ion-card *ngIf="pkg.manifest as manifest">
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-grid *ngIf="pkg.manifest as manifest">
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Version</h2>
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
*ngIf="manifest['git-hash'] as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>{{ gitHash }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="copy-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ng-template #noHash>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Version</h2>
|
||||
<p>{{ manifest.version | displayEmver }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
*ngIf="manifest['git-hash'] as gitHash; else noHash"
|
||||
button
|
||||
detail="false"
|
||||
(click)="copy(gitHash)"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>{{ gitHash }}</p>
|
||||
<p>Unknown</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="copy-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ng-template #noHash>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Git Hash</h2>
|
||||
<p>Unknown</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
<ion-item button detail="false" (click)="presentModalLicense()">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest['upstream-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest['upstream-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['wrapper-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest['wrapper-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['support-site']"
|
||||
[disabled]="!manifest['support-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest['support-site'] || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-card>
|
||||
</ng-template>
|
||||
<ion-item button detail="false" (click)="presentModalLicense()">
|
||||
<ion-label>
|
||||
<h2>License</h2>
|
||||
<p>{{ manifest.license }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="chevron-forward"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeMd="6">
|
||||
<ion-item-group>
|
||||
<ion-item
|
||||
[href]="manifest['upstream-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Source Repository</h2>
|
||||
<p>{{ manifest['upstream-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['wrapper-repo']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Wrapper Repository</h2>
|
||||
<p>{{ manifest['wrapper-repo'] }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item
|
||||
[href]="manifest['support-site']"
|
||||
[disabled]="!manifest['support-site']"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
detail="false"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>Support Site</h2>
|
||||
<p>{{ manifest['support-site'] || 'Not provided' }}</p>
|
||||
</ion-label>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button defaultHref="services"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-item lines="none" color="light">
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="pkg['static-files'].icon" alt="" />
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<h1
|
||||
class="montserrat"
|
||||
[class.less-large]="pkg.manifest.title.length > 20"
|
||||
>
|
||||
{{ pkg.manifest.title }}
|
||||
</h1>
|
||||
<h2>{{ pkg.manifest.version | displayEmver }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
@@ -1,3 +0,0 @@
|
||||
.less-large {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-header',
|
||||
templateUrl: './app-show-header.component.html',
|
||||
styleUrls: ['./app-show-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppShowHeaderComponent {
|
||||
@Input()
|
||||
pkg!: PackageDataEntry
|
||||
}
|
||||
@@ -36,46 +36,35 @@
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col size="12">
|
||||
<ng-container *ngIf="store$ | async as store; else loading">
|
||||
<ng-container *ngIf="localPkgs$ | async as localPkgs">
|
||||
<marketplace-categories
|
||||
[categories]="store.categories"
|
||||
[category]="category"
|
||||
[updatesAvailable]="
|
||||
(store.packages | filterPackages: '':'updates':localPkgs).length
|
||||
"
|
||||
(categoryChange)="onCategoryChange($event)"
|
||||
></marketplace-categories>
|
||||
<marketplace-categories
|
||||
[categories]="store.categories"
|
||||
[category]="category"
|
||||
[disableCategories]="!!query"
|
||||
(categoryChange)="onCategoryChange($event)"
|
||||
></marketplace-categories>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<ion-grid
|
||||
*ngIf="store.packages | filterPackages: query:category:localPkgs as filtered"
|
||||
>
|
||||
<div
|
||||
*ngIf="!filtered.length && category === 'updates'"
|
||||
class="ion-padding"
|
||||
<ion-grid
|
||||
*ngIf="store.packages | filterPackages: query:category as filtered"
|
||||
>
|
||||
<ion-row *ngIf="localPkgs$ | async as localPkgs">
|
||||
<ion-col
|
||||
*ngFor="let pkg of filtered"
|
||||
sizeXs="12"
|
||||
sizeSm="12"
|
||||
sizeMd="6"
|
||||
>
|
||||
<h1>All services are up to date!</h1>
|
||||
</div>
|
||||
|
||||
<ion-row>
|
||||
<ion-col
|
||||
*ngFor="let pkg of filtered"
|
||||
sizeXs="12"
|
||||
sizeSm="12"
|
||||
sizeMd="6"
|
||||
>
|
||||
<marketplace-item [pkg]="pkg">
|
||||
<marketplace-status
|
||||
class="status"
|
||||
[version]="pkg.manifest.version"
|
||||
[localPkg]="localPkgs[pkg.manifest.id]"
|
||||
></marketplace-status>
|
||||
</marketplace-item>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ng-container>
|
||||
<marketplace-item [pkg]="pkg">
|
||||
<marketplace-status
|
||||
class="status"
|
||||
[version]="pkg.manifest.version"
|
||||
[localPkg]="localPkgs[pkg.manifest.id]"
|
||||
></marketplace-status>
|
||||
</marketplace-item>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #loading>
|
||||
|
||||
@@ -30,11 +30,19 @@
|
||||
>
|
||||
<ion-item *ngFor="let pkg of updates">
|
||||
<ng-container *ngIf="data.localPkgs[pkg.manifest.id] as local">
|
||||
<ion-avatar slot="start" class="service-avatar">
|
||||
<ion-avatar
|
||||
class="service-avatar"
|
||||
(click)="viewInMarketplace(local)"
|
||||
>
|
||||
<img [src]="'data:image/png;base64,' + pkg.icon | trustUrl" />
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<h1>{{ pkg.manifest.title }}</h1>
|
||||
<h1
|
||||
(click)="viewInMarketplace(local)"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
{{ pkg.manifest.title }}
|
||||
</h1>
|
||||
<h2 class="inline">
|
||||
<span>{{ local.manifest.version }}</span>
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
@@ -43,6 +51,9 @@
|
||||
</ion-text>
|
||||
</h2>
|
||||
<p [innerHTML]="pkg.manifest['release-notes'] | markdown"></p>
|
||||
<p *ngIf="errors[pkg.manifest.id] as error">
|
||||
<ion-text color="danger">{{ error }}</ion-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
|
||||
<div slot="end">
|
||||
@@ -62,11 +73,11 @@
|
||||
></ion-spinner>
|
||||
<ng-template #updateBtn>
|
||||
<ion-button
|
||||
(click)="update(pkg.manifest.id, host.url)"
|
||||
color="dark"
|
||||
(click)="tryUpdate(pkg.manifest, host.url, local)"
|
||||
[color]="errors[pkg.manifest.id] ? 'danger' : 'dark'"
|
||||
strong
|
||||
>
|
||||
Update
|
||||
{{ errors[pkg.manifest.id] ? 'Retry' : 'Update' }}
|
||||
</ion-button>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.service-avatar {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
|
||||
@@ -14,10 +14,18 @@ import {
|
||||
MarketplacePkg,
|
||||
StoreIdentity,
|
||||
} from '@start9labs/marketplace'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Emver, isEmptyObject } from '@start9labs/shared'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { combineLatest, Observable } from 'rxjs'
|
||||
import { PrimaryRendering } from '../../services/pkg-status-rendering.service'
|
||||
import {
|
||||
AlertController,
|
||||
LoadingController,
|
||||
NavController,
|
||||
} from '@ionic/angular'
|
||||
import { hasCurrentDeps } from 'src/app/util/has-deps'
|
||||
import { getAllPackages } from 'src/app/util/get-package-data'
|
||||
import { Breakages } from 'src/app/services/api/api.types'
|
||||
|
||||
interface UpdatesData {
|
||||
hosts: StoreIdentity[]
|
||||
@@ -33,6 +41,7 @@ interface UpdatesData {
|
||||
})
|
||||
export class UpdatesPage {
|
||||
queued: Record<string, boolean> = {}
|
||||
errors: Record<string, string> = {}
|
||||
|
||||
readonly data$: Observable<UpdatesData> = combineLatest({
|
||||
hosts: this.marketplaceService.getKnownHosts$(),
|
||||
@@ -49,11 +58,115 @@ export class UpdatesPage {
|
||||
private readonly marketplaceService: MarketplaceService,
|
||||
private readonly api: ApiService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly navCtrl: NavController,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
private readonly alertCtrl: AlertController,
|
||||
) {}
|
||||
|
||||
async update(id: string, url: string): Promise<void> {
|
||||
viewInMarketplace(pkg: PackageDataEntry) {
|
||||
const url = pkg.installed?.['marketplace-url']
|
||||
const queryParams = url ? { url } : {}
|
||||
|
||||
this.navCtrl.navigateForward([`marketplace/${pkg.manifest.id}`], {
|
||||
queryParams,
|
||||
})
|
||||
}
|
||||
|
||||
async tryUpdate(
|
||||
manifest: MarketplaceManifest,
|
||||
url: string,
|
||||
local: PackageDataEntry,
|
||||
): Promise<void> {
|
||||
const { id, version } = manifest
|
||||
|
||||
delete this.errors[id]
|
||||
this.queued[id] = true
|
||||
this.api.installPackage({ id, 'marketplace-url': url })
|
||||
|
||||
if (hasCurrentDeps(local)) {
|
||||
this.dryUpdate(manifest, url)
|
||||
} else {
|
||||
this.update(id, version, url)
|
||||
}
|
||||
}
|
||||
|
||||
private async dryUpdate(manifest: MarketplaceManifest, url: string) {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Checking dependent services...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
const { id, version } = manifest
|
||||
|
||||
try {
|
||||
const breakages = await this.api.dryUpdatePackage({
|
||||
id,
|
||||
version: `${version}`,
|
||||
})
|
||||
await loader.dismiss()
|
||||
|
||||
if (isEmptyObject(breakages)) {
|
||||
this.update(id, version, url)
|
||||
} else {
|
||||
const proceed = await this.presentAlertBreakages(
|
||||
manifest.title,
|
||||
breakages,
|
||||
)
|
||||
if (proceed) {
|
||||
this.update(id, version, url)
|
||||
} else {
|
||||
delete this.queued[id]
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
delete this.queued[id]
|
||||
this.errors[id] = e.message
|
||||
}
|
||||
}
|
||||
|
||||
private async presentAlertBreakages(
|
||||
title: string,
|
||||
breakages: Breakages,
|
||||
): Promise<boolean> {
|
||||
let message: string = `As a result of updating ${title}, the following services will no longer work properly and may crash:<ul>`
|
||||
const localPkgs = await getAllPackages(this.patch)
|
||||
const bullets = Object.keys(breakages).map(id => {
|
||||
const title = localPkgs[id].manifest.title
|
||||
return `<li><b>${title}</b></li>`
|
||||
})
|
||||
message = `${message}${bullets.join('')}</ul>`
|
||||
|
||||
return new Promise(async resolve => {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Warning',
|
||||
message,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
handler: () => {
|
||||
resolve(false)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Continue',
|
||||
handler: () => {
|
||||
resolve(true)
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
],
|
||||
cssClass: 'alert-warning-message',
|
||||
})
|
||||
|
||||
await alert.present()
|
||||
})
|
||||
}
|
||||
|
||||
private update(id: string, version: string, url: string) {
|
||||
this.marketplaceService.installPackage(id, version, url).catch(e => {
|
||||
delete this.queued[id]
|
||||
this.errors[id] = e.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,13 @@ img {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
ion-back-button {
|
||||
margin-left: 16px;
|
||||
&::part(native) {
|
||||
border: 1px solid white;
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-title {
|
||||
font-family: 'Montserrat';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user