From 88afb756f5fa6d29df3a1bdcc9051feee8ebb3d3 Mon Sep 17 00:00:00 2001 From: Lucy C <12953208+elvece@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:02:32 -0600 Subject: [PATCH] show available marketplace updates in menu (#1613) * show service updates in menu --- .../list/categories/categories.component.html | 4 +- .../list/categories/categories.component.ts | 2 +- .../src/services/marketplace.service.ts | 2 +- frontend/projects/ui/src/app/app.providers.ts | 2 + .../app/global/services/patch-data.service.ts | 14 ++- .../global/services/unread-toast.service.ts | 16 +-- .../ui/src/app/app/menu/menu.component.html | 20 +-- .../ui/src/app/app/menu/menu.component.scss | 4 - .../ui/src/app/app/menu/menu.component.ts | 22 +++- .../ui/src/app/app/menu/menu.module.ts | 1 - .../marketplace-list/marketplace-list.page.ts | 6 +- .../preferences/preferences.page.html | 38 +++--- .../preferences/preferences.page.ts | 6 - .../services/api/embassy-mock-api.service.ts | 2 +- .../ui/src/app/services/api/mock-patch.ts | 9 ++ .../src/app/services/marketplace.service.ts | 119 ++++++++++++------ .../src/app/services/server-config.service.ts | 2 +- 17 files changed, 162 insertions(+), 107 deletions(-) diff --git a/frontend/projects/marketplace/src/pages/list/categories/categories.component.html b/frontend/projects/marketplace/src/pages/list/categories/categories.component.html index 765ff32b0..61cbab592 100644 --- a/frontend/projects/marketplace/src/pages/list/categories/categories.component.html +++ b/frontend/projects/marketplace/src/pages/list/categories/categories.component.html @@ -7,5 +7,7 @@ (click)="switchCategory(cat)" > {{ cat }} -   ({{ updatesAvailable }}) + +   ({{ updatesAvailable }}) + diff --git a/frontend/projects/marketplace/src/pages/list/categories/categories.component.ts b/frontend/projects/marketplace/src/pages/list/categories/categories.component.ts index 9a70ac376..3647bd23b 100644 --- a/frontend/projects/marketplace/src/pages/list/categories/categories.component.ts +++ b/frontend/projects/marketplace/src/pages/list/categories/categories.component.ts @@ -23,7 +23,7 @@ export class CategoriesComponent { category = '' @Input() - updatesAvailable? = 0 + updatesAvailable = 0 @Output() readonly categoryChange = new EventEmitter() diff --git a/frontend/projects/marketplace/src/services/marketplace.service.ts b/frontend/projects/marketplace/src/services/marketplace.service.ts index 7521e5832..12d97f150 100644 --- a/frontend/projects/marketplace/src/services/marketplace.service.ts +++ b/frontend/projects/marketplace/src/services/marketplace.service.ts @@ -7,7 +7,7 @@ export abstract class AbstractMarketplaceService { abstract getReleaseNotes(id: string): Observable> - abstract getCategories(): Observable + abstract getCategories(): Observable> abstract getPackages(): Observable diff --git a/frontend/projects/ui/src/app/app.providers.ts b/frontend/projects/ui/src/app/app.providers.ts index ae62cba18..702de3c57 100644 --- a/frontend/projects/ui/src/app/app.providers.ts +++ b/frontend/projects/ui/src/app/app.providers.ts @@ -14,10 +14,12 @@ import { GlobalErrorHandler } from './services/global-error-handler.service' import { AuthService } from './services/auth.service' import { LocalStorageService } from './services/local-storage.service' import { DataModel } from './services/patch-db/data-model' +import { FilterPackagesPipe } from '../../../marketplace/src/pipes/filter-packages.pipe' const { useMocks } = require('../../../../config.json') as WorkspaceConfig export const APP_PROVIDERS: Provider[] = [ + FilterPackagesPipe, FormBuilder, IonNav, { diff --git a/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts b/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts index 7e6bf0caf..f47f2a97c 100644 --- a/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts +++ b/frontend/projects/ui/src/app/app/global/services/patch-data.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core' +import { Inject, Injectable } from '@angular/core' import { ModalController } from '@ionic/angular' import { Observable, of } from 'rxjs' import { filter, share, switchMap, take, tap } from 'rxjs/operators' @@ -11,6 +11,8 @@ import { OSWelcomePage } from 'src/app/modals/os-welcome/os-welcome.page' import { ConfigService } from 'src/app/services/config.service' import { ApiService } from 'src/app/services/api/embassy-api.service' import { PatchMonitorService } from './patch-monitor.service' +import { MarketplaceService } from 'src/app/services/marketplace.service' +import { AbstractMarketplaceService } from '../../../../../../marketplace/src/services/marketplace.service' // Get data from PatchDb after is starts and act upon it @Injectable({ @@ -24,8 +26,8 @@ export class PatchDataService extends Observable { filter(obj => !isEmptyObject(obj)), take(1), tap(({ ui }) => { - // check for updates to EOS - this.checkForEosUpdate(ui) + // check for updates to EOS and services + this.checkForUpdates(ui) // show eos welcome message this.showEosWelcome(ui['ack-welcome']) }), @@ -42,13 +44,17 @@ export class PatchDataService extends Observable { private readonly config: ConfigService, private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, + @Inject(AbstractMarketplaceService) + private readonly marketplaceService: MarketplaceService, ) { super(subscriber => this.stream$.subscribe(subscriber)) } - private checkForEosUpdate(ui: UIData): void { + private checkForUpdates(ui: UIData): void { if (ui['auto-check-updates'] !== false) { this.eosService.getEOS() + this.marketplaceService.getPackages().pipe(take(1)).subscribe() + this.marketplaceService.getCategories().pipe(take(1)).subscribe() } } diff --git a/frontend/projects/ui/src/app/app/global/services/unread-toast.service.ts b/frontend/projects/ui/src/app/app/global/services/unread-toast.service.ts index 3daee2a55..3e2dee51e 100644 --- a/frontend/projects/ui/src/app/app/global/services/unread-toast.service.ts +++ b/frontend/projects/ui/src/app/app/global/services/unread-toast.service.ts @@ -1,19 +1,11 @@ import { Injectable } from '@angular/core' import { Router } from '@angular/router' -import { - LoadingController, - ToastController, - ToastOptions, -} from '@ionic/angular' -import { EMPTY, merge, Observable, ObservableInput } from 'rxjs' +import { ToastController, ToastOptions } from '@ionic/angular' +import { EMPTY, Observable, ObservableInput } from 'rxjs' import { filter, pairwise, switchMap, tap } from 'rxjs/operators' -import { ErrorToastService } from '@start9labs/shared' - import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' -import { ConfigService } from 'src/app/services/config.service' -import { ApiService } from 'src/app/services/api/embassy-api.service' import { PatchDataService } from './patch-data.service' -import { DataModel, ServerInfo } from 'src/app/services/patch-db/data-model' +import { DataModel } from 'src/app/services/patch-db/data-model' // Watch unread notification count to display toast @Injectable() @@ -64,8 +56,6 @@ export class UnreadToastService extends Observable { private readonly router: Router, private readonly patchData: PatchDataService, private readonly patch: PatchDbService, - private readonly config: ConfigService, - private readonly embassyApi: ApiService, private readonly toastCtrl: ToastController, ) { super(subscriber => this.stream$.subscribe(subscriber)) diff --git a/frontend/projects/ui/src/app/app/menu/menu.component.html b/frontend/projects/ui/src/app/app/menu/menu.component.html index 5cb829dc8..5de85cec1 100644 --- a/frontend/projects/ui/src/app/app/menu/menu.component.html +++ b/frontend/projects/ui/src/app/app/menu/menu.component.html @@ -5,9 +5,7 @@ + {{ updateCount }} + + - {{ count }} + {{ notificaitonCount }} diff --git a/frontend/projects/ui/src/app/app/menu/menu.component.scss b/frontend/projects/ui/src/app/app/menu/menu.component.scss index 799b220ba..7e921edcb 100644 --- a/frontend/projects/ui/src/app/app/menu/menu.component.scss +++ b/frontend/projects/ui/src/app/app/menu/menu.component.scss @@ -25,10 +25,6 @@ } } -.badge { - margin-right: 3%; -} - .snek { position: absolute; bottom: 90px; diff --git a/frontend/projects/ui/src/app/app/menu/menu.component.ts b/frontend/projects/ui/src/app/app/menu/menu.component.ts index 28229fa45..f8f9e5949 100644 --- a/frontend/projects/ui/src/app/app/menu/menu.component.ts +++ b/frontend/projects/ui/src/app/app/menu/menu.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' import { AlertController } from '@ionic/angular' import { ConfigService } from '../../services/config.service' import { LocalStorageService } from '../../services/local-storage.service' @@ -6,6 +6,10 @@ import { EOSService } from '../../services/eos.service' import { ApiService } from '../../services/api/embassy-api.service' import { AuthService } from '../../services/auth.service' import { PatchDbService } from '../../services/patch-db/patch-db.service' +import { Observable } from 'rxjs' +import { map } from 'rxjs/operators' +import { AbstractMarketplaceService } from '@start9labs/marketplace' +import { MarketplaceService } from 'src/app/services/marketplace.service' @Component({ selector: 'app-menu', @@ -42,19 +46,29 @@ export class MenuComponent { }, ] - readonly notification$ = this.patch.watch$( + readonly notificationCount$ = this.patch.watch$( 'server-info', 'unread-notification-count', ) + readonly showEOSUpdate$ = this.eosService.showUpdate$ + + readonly showDevTools$ = this.localStorageService.showDevTools$ + + readonly updateCount$: Observable = this.marketplaceService + .getUpdates() + .pipe(map(pkgs => pkgs.length)) + constructor( private readonly config: ConfigService, private readonly alertCtrl: AlertController, private readonly embassyApi: ApiService, private readonly authService: AuthService, private readonly patch: PatchDbService, - public readonly localStorageService: LocalStorageService, - public readonly eosService: EOSService, + private readonly localStorageService: LocalStorageService, + private readonly eosService: EOSService, + @Inject(AbstractMarketplaceService) + private readonly marketplaceService: MarketplaceService, ) {} get href(): string { diff --git a/frontend/projects/ui/src/app/app/menu/menu.module.ts b/frontend/projects/ui/src/app/app/menu/menu.module.ts index baa2bedbb..0dc074849 100644 --- a/frontend/projects/ui/src/app/app/menu/menu.module.ts +++ b/frontend/projects/ui/src/app/app/menu/menu.module.ts @@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common' import { NgModule } from '@angular/core' import { RouterModule } from '@angular/router' import { IonicModule } from '@ionic/angular' - import { MenuComponent } from './menu.component' import { SnekModule } from '../snek/snek.module' diff --git a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts index 28205e6f7..8325fe1cb 100644 --- a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts +++ b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts @@ -24,11 +24,7 @@ export class MarketplaceListPage { startWith({}), ) - readonly categories$ = this.marketplaceService - .getCategories() - .pipe( - map(categories => new Set(['featured', 'updates', ...categories, 'all'])), - ) + readonly categories$ = this.marketplaceService.getCategories() readonly pkgs$: Observable = this.patch .watch$('server-info') diff --git a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.html b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.html index a7ccdc317..742513c94 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.html +++ b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.html @@ -7,23 +7,25 @@ - - - General - - Device Name - {{ ui.name || 'Embassy-' + server.id }} - + + + + General + + Device Name + {{ ui.name || 'Embassy-' + server.id }} + - Marketplace - - Auto Check for Updates - - {{ ui['auto-check-updates'] !== false ? 'Enabled' : 'Disabled' }} - - - + Marketplace + + Auto Check for Updates + + {{ ui['auto-check-updates'] !== false ? 'Enabled' : 'Disabled' }} + + + + diff --git a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts index 89fb5392f..d418876e3 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/preferences/preferences.page.ts @@ -1,7 +1,6 @@ import { Component, ViewChild } from '@angular/core' import { PatchDbService } from 'src/app/services/patch-db/patch-db.service' import { - IonContent, LoadingController, ModalController, ToastController, @@ -20,7 +19,6 @@ import { LocalStorageService } from '../../../services/local-storage.service' styleUrls: ['./preferences.page.scss'], }) export class PreferencesPage { - @ViewChild(IonContent) content: IonContent clicks = 0 readonly ui$ = this.patch.watch$('ui') @@ -36,10 +34,6 @@ export class PreferencesPage { readonly serverConfig: ServerConfigService, ) {} - ngAfterViewInit() { - this.content.scrollToPoint(undefined, 1) - } - async presentModalName(placeholder: string): Promise { const options: GenericInputOptions = { title: 'Edit Device Name', diff --git a/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 99173f9bf..3e8232ce7 100644 --- a/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/frontend/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -223,7 +223,7 @@ export class MockApiService extends ApiService { if (path === '/package/v0/info') { return { - name: 'Dark9', + name: 'Dark69', categories: [ 'featured', 'bitcoin', diff --git a/frontend/projects/ui/src/app/services/api/mock-patch.ts b/frontend/projects/ui/src/app/services/api/mock-patch.ts index 4621432bf..1888de045 100644 --- a/frontend/projects/ui/src/app/services/api/mock-patch.ts +++ b/frontend/projects/ui/src/app/services/api/mock-patch.ts @@ -14,6 +14,15 @@ export const mockPatchData: DataModel = { 'auto-check-updates': true, 'pkg-order': [], 'ack-welcome': '1.0.0', + marketplace: { + 'selected-id': '1234', + 'known-hosts': { + '1234': { + name: 'Dark9', + url: 'https://test-marketplace.com', + }, + }, + }, }, 'server-info': { id: 'abcdefgh', diff --git a/frontend/projects/ui/src/app/services/marketplace.service.ts b/frontend/projects/ui/src/app/services/marketplace.service.ts index 89a65eb3f..9b093890d 100644 --- a/frontend/projects/ui/src/app/services/marketplace.service.ts +++ b/frontend/projects/ui/src/app/services/marketplace.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@angular/core' -import { LoadingController } from '@ionic/angular' import { Emver, ErrorToastService } from '@start9labs/shared' import { MarketplacePkg, AbstractMarketplaceService, Marketplace, + FilterPackagesPipe, + MarketplaceData, } from '@start9labs/marketplace' -import { from, Observable, of } from 'rxjs' +import { from, Observable, of, Subject } from 'rxjs' import { RR } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ConfigService } from 'src/app/services/config.service' @@ -29,12 +30,13 @@ import { @Injectable() export class MarketplaceService extends AbstractMarketplaceService { private readonly notes = new Map>() + private readonly hasPackages$ = new Subject() - private readonly altMarketplaceData$: Observable< + private readonly uiMarketplaceData$: Observable< UIMarketplaceData | undefined > = this.patch.watch$('ui', 'marketplace').pipe(shareReplay(1)) - private readonly marketplace$ = this.altMarketplaceData$.pipe( + private readonly marketplace$ = this.uiMarketplaceData$.pipe( map(data => this.toMarketplace(data)), ) @@ -42,48 +44,80 @@ export class MarketplaceService extends AbstractMarketplaceService { .watch$('server-info') .pipe(take(1), shareReplay()) - private readonly categories$: Observable = this.marketplace$.pipe( - switchMap(({ url }) => - this.serverInfo$.pipe( - switchMap(({ id }) => - from(this.getMarketplaceData({ 'server-id': id }, url)), - ), - ), - ), - map(({ categories }) => categories), - shareReplay(1), - ) - - private readonly pkg$: Observable = - this.altMarketplaceData$.pipe( - switchMap(data => + private readonly registryData$: Observable = + this.uiMarketplaceData$.pipe( + switchMap(uiMarketplaceData => this.serverInfo$.pipe( - switchMap(info => + switchMap(({ id }) => from( - this.getMarketplacePkgs( - { page: 1, 'per-page': 100 }, - this.toMarketplace(data).url, - info['eos-version-compat'], + this.getMarketplaceData( + { 'server-id': id }, + this.toMarketplace(uiMarketplaceData).url, ), - ).pipe(tap(() => this.onPackages(data))), + ).pipe(tap(({ name }) => this.updateName(uiMarketplaceData, name))), ), ), ), - catchError(e => { - this.errToast.present(e) - - return of([]) - }), shareReplay(1), ) + private readonly categories$: Observable> = + this.registryData$.pipe( + map( + ({ categories }) => + new Set(['featured', 'updates', ...categories, 'all']), + ), + ) + + private readonly pkgs$: Observable = this.marketplace$.pipe( + switchMap(({ url }) => + this.serverInfo$.pipe( + switchMap(info => + from( + this.getMarketplacePkgs( + { page: 1, 'per-page': 100 }, + url, + info['eos-version-compat'], + ), + ).pipe(tap(() => this.hasPackages$.next(true))), + ), + ), + ), + catchError(e => { + this.errToast.present(e) + + return of([]) + }), + shareReplay(1), + ) + + private readonly updates$: Observable = + this.hasPackages$.pipe( + switchMap(() => + this.patch.watch$('package-data').pipe( + switchMap(localPkgs => + this.pkgs$.pipe( + map(pkgs => { + return this.filterPkgsPipe.transform( + pkgs, + '', + 'updates', + localPkgs, + ) + }), + ), + ), + ), + ), + ) + constructor( private readonly api: ApiService, private readonly patch: PatchDbService, private readonly config: ConfigService, - private readonly loadingCtrl: LoadingController, private readonly errToast: ErrorToastService, private readonly emver: Emver, + private readonly filterPkgsPipe: FilterPackagesPipe, ) { super() } @@ -93,15 +127,15 @@ export class MarketplaceService extends AbstractMarketplaceService { } getAltMarketplace(): Observable { - return this.altMarketplaceData$ + return this.uiMarketplaceData$ } - getCategories(): Observable { + getCategories(): Observable> { return this.categories$ } getPackages(): Observable { - return this.pkg$ + return this.pkgs$ } getPackage(id: string, version: string): Observable { @@ -133,6 +167,10 @@ export class MarketplaceService extends AbstractMarketplaceService { ) } + getUpdates(): Observable { + return this.updates$ + } + getReleaseNotes(id: string): Observable> { if (this.notes.has(id)) { return of(this.notes.get(id) || {}) @@ -216,15 +254,16 @@ export class MarketplaceService extends AbstractMarketplaceService { ) } - private onPackages(data?: UIMarketplaceData) { - const { name } = this.toMarketplace(data) - - if (!data?.['selected-id']) { + private updateName( + uiMarketplaceData: UIMarketplaceData | undefined, + name: string, + ) { + if (!uiMarketplaceData?.['selected-id']) { return } - const selectedId = data['selected-id'] - const knownHosts = data['known-hosts'] + const selectedId = uiMarketplaceData['selected-id'] + const knownHosts = uiMarketplaceData['known-hosts'] if (knownHosts[selectedId].name !== name) { this.api.setDbValue({ diff --git a/frontend/projects/ui/src/app/services/server-config.service.ts b/frontend/projects/ui/src/app/services/server-config.service.ts index cba6b2d22..e4e6b57c1 100644 --- a/frontend/projects/ui/src/app/services/server-config.service.ts +++ b/frontend/projects/ui/src/app/services/server-config.service.ts @@ -113,7 +113,7 @@ export const serverConfig: ConfigSpec = { type: 'boolean', name: 'Auto Check for Updates', description: - 'If enabled, EmbassyOS will automatically check for updates of itself. Updating will still require your approval and action. Updates will never be performed automatically.', + 'If enabled, EmbassyOS will automatically check for updates of itself and installed services. Updating will still require your approval and action. Updates will never be performed automatically.', default: true, }, }