mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 22:39:46 +00:00
Feat/update tab (#1865)
* implement updates tab for viewing all updates from all marketplaces in one place * remove auto-check-updates * feat: implement updates page (#1888) * feat: implement updates page * chore: comments * better styling in update tab * rework marketplace service (#1891) * rework marketplace service * remove unneeded ? * fix: refactor marketplace to cache requests Co-authored-by: waterplea <alexander@inkin.ru> Co-authored-by: Alex Inkin <alexander@inkin.ru>
This commit is contained in:
committed by
Aiden McClelland
parent
d380cc31fa
commit
26c37ba824
33
frontend/projects/ui/src/app/pages/updates/updates.module.ts
Normal file
33
frontend/projects/ui/src/app/pages/updates/updates.module.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { FilterUpdatesPipe, UpdatesPage } from './updates.page'
|
||||
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
|
||||
import { MarkdownPipeModule, SharedPipesModule } from '@start9labs/shared'
|
||||
import { SkeletonListComponentModule } from 'src/app/components/skeleton-list/skeleton-list.component.module'
|
||||
import { RoundProgressModule } from 'angular-svg-round-progressbar'
|
||||
import { InstallProgressPipeModule } from 'src/app/pipes/install-progress/install-progress.module'
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: UpdatesPage,
|
||||
},
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
BadgeMenuComponentModule,
|
||||
SharedPipesModule,
|
||||
SkeletonListComponentModule,
|
||||
MarkdownPipeModule,
|
||||
RoundProgressModule,
|
||||
InstallProgressPipeModule,
|
||||
],
|
||||
declarations: [UpdatesPage, FilterUpdatesPipe],
|
||||
})
|
||||
export class UpdatesPageModule {}
|
||||
84
frontend/projects/ui/src/app/pages/updates/updates.page.html
Normal file
84
frontend/projects/ui/src/app/pages/updates/updates.page.html
Normal file
@@ -0,0 +1,84 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Updates</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<badge-menu-button></badge-menu-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item-group *ngIf="data$ | async as data">
|
||||
<ng-container *ngFor="let host of data.hosts | keyvalue">
|
||||
<ion-item-divider> {{ host.value }} </ion-item-divider>
|
||||
|
||||
<div class="ion-padding-start ion-padding-bottom">
|
||||
<ion-item *ngIf="data.errors.includes(host.key)">
|
||||
<ion-text color="danger">Request Failed</ion-text>
|
||||
</ion-item>
|
||||
|
||||
<ng-container
|
||||
*ngIf="data.marketplace[host.key]?.packages as packages else loading"
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="packages | filterUpdates : data.localPkgs : host.key as updates"
|
||||
>
|
||||
<ion-item *ngFor="let pkg of updates">
|
||||
<ng-container *ngIf="data.localPkgs[pkg.manifest.id] as local">
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="'data:image/png;base64,' + pkg.icon | trustUrl" />
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<h1>{{ pkg.manifest.title }}</h1>
|
||||
<h2 class="inline">
|
||||
<span>{{ local.manifest.version }}</span>
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
<ion-text color="success">
|
||||
{{ pkg.manifest.version }}
|
||||
</ion-text>
|
||||
</h2>
|
||||
<p [innerHTML]="pkg.manifest['release-notes'] | markdown"></p>
|
||||
</ion-label>
|
||||
|
||||
<div slot="end">
|
||||
<round-progress
|
||||
*ngIf="local.state === PackageState.Installing else notInstalling"
|
||||
[current]="local['install-progress'] | installProgress"
|
||||
[max]="100"
|
||||
[radius]="24"
|
||||
[stroke]="4"
|
||||
[rounded]="true"
|
||||
color="var(--ion-color-primary)"
|
||||
></round-progress>
|
||||
<ng-template #notInstalling>
|
||||
<ion-spinner
|
||||
*ngIf="queued[pkg.manifest.id] else updateBtn"
|
||||
color="dark"
|
||||
></ion-spinner>
|
||||
<ng-template #updateBtn>
|
||||
<ion-button
|
||||
(click)="update(pkg.manifest.id, host.key)"
|
||||
color="dark"
|
||||
strong
|
||||
>
|
||||
Update
|
||||
</ion-button>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="!updates.length">
|
||||
<p>All services are up to date!</p>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #loading>
|
||||
<skeleton-list [showAvatar]="true" [rows]="2"></skeleton-list>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
</ion-content>
|
||||
12
frontend/projects/ui/src/app/pages/updates/updates.page.scss
Normal file
12
frontend/projects/ui/src/app/pages/updates/updates.page.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
ion-avatar {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
margin-left: 64px;
|
||||
}
|
||||
|
||||
.name:only-child {
|
||||
display: none;
|
||||
}
|
||||
95
frontend/projects/ui/src/app/pages/updates/updates.page.ts
Normal file
95
frontend/projects/ui/src/app/pages/updates/updates.page.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Component, Inject } from '@angular/core'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
DataModel,
|
||||
PackageDataEntry,
|
||||
PackageState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||
import {
|
||||
AbstractMarketplaceService,
|
||||
Marketplace,
|
||||
MarketplaceManifest,
|
||||
MarketplacePkg,
|
||||
} from '@start9labs/marketplace'
|
||||
import { Emver } from '@start9labs/shared'
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { combineLatest, Observable } from 'rxjs'
|
||||
import { PrimaryRendering } from '../../services/pkg-status-rendering.service'
|
||||
|
||||
interface UpdatesData {
|
||||
hosts: Record<string, string>
|
||||
marketplace: Marketplace
|
||||
localPkgs: Record<string, PackageDataEntry>
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'updates',
|
||||
templateUrl: 'updates.page.html',
|
||||
styleUrls: ['updates.page.scss'],
|
||||
})
|
||||
export class UpdatesPage {
|
||||
queued: Record<string, boolean> = {}
|
||||
|
||||
readonly data$: Observable<UpdatesData> = combineLatest({
|
||||
hosts: this.marketplaceService.getKnownHosts$(),
|
||||
marketplace: this.marketplaceService.getMarketplace$(),
|
||||
localPkgs: this.patch.watch$('package-data'),
|
||||
errors: this.marketplaceService.getRequestErrors$(),
|
||||
})
|
||||
|
||||
readonly PackageState = PackageState
|
||||
readonly rendering = PrimaryRendering[PackageState.Installing]
|
||||
|
||||
constructor(
|
||||
@Inject(AbstractMarketplaceService)
|
||||
private readonly marketplaceService: MarketplaceService,
|
||||
private readonly api: ApiService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
|
||||
async update(id: string, url: string): Promise<void> {
|
||||
this.queued[id] = true
|
||||
this.api.installPackage({ id, 'marketplace-url': url })
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({
|
||||
name: 'filterUpdates',
|
||||
})
|
||||
export class FilterUpdatesPipe implements PipeTransform {
|
||||
constructor(private readonly emver: Emver) {}
|
||||
|
||||
transform(
|
||||
pkgs: MarketplacePkg[],
|
||||
local: Record<string, PackageDataEntry> = {},
|
||||
url: string,
|
||||
): MarketplacePkg[] {
|
||||
return pkgs.filter(
|
||||
({ manifest }) =>
|
||||
marketplaceSame(manifest, local, url) &&
|
||||
versionLower(manifest, local, this.emver),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function marketplaceSame(
|
||||
{ id }: MarketplaceManifest,
|
||||
local: Record<string, PackageDataEntry>,
|
||||
url: string,
|
||||
): boolean {
|
||||
return local[id]?.installed?.['marketplace-url'] === url
|
||||
}
|
||||
|
||||
export function versionLower(
|
||||
{ version, id }: MarketplaceManifest,
|
||||
local: Record<string, PackageDataEntry>,
|
||||
emver: Emver,
|
||||
): boolean {
|
||||
return (
|
||||
local[id].state === PackageState.Installing ||
|
||||
emver.compare(version, local[id].installed?.manifest.version || '') === 1
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user