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:
Matt Hill
2022-11-26 13:18:10 -07:00
committed by Aiden McClelland
parent 9146c31abf
commit 2336e36314
15 changed files with 388 additions and 336 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>
&nbsp;<ion-icon name="arrow-forward"></ion-icon>&nbsp;
@@ -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>

View File

@@ -1,6 +1,7 @@
.service-avatar {
position: absolute;
top: 6px;
cursor: pointer;
}
ion-label {

View File

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

View File

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