diff --git a/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.html b/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.html index 443f84fff..d214de22d 100644 --- a/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.html +++ b/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.html @@ -8,10 +8,10 @@ - + -
+

{{ note.key | displayEmver }}

diff --git a/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.ts b/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.ts index 8d1acf450..aaa52d4b4 100644 --- a/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.ts +++ b/ui/src/app/pages/marketplace-routes/app-release-notes/app-release-notes.page.ts @@ -20,9 +20,8 @@ export class AppReleaseNotes { ngOnInit () { this.pkgId = this.route.snapshot.paramMap.get('pkgId') - const version = this.route.snapshot.paramMap.get('version') - if (!this.marketplaceService.pkgs[this.pkgId]?.['release-notes']) { - this.marketplaceService.getPkg(this.pkgId, version) + if (!this.marketplaceService.releaseNotes[this.pkgId]) { + this.marketplaceService.getReleaseNotes(this.pkgId) } } diff --git a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts index d96e6274e..bbdf23dac 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts +++ b/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts @@ -20,7 +20,7 @@ export class MarketplaceListPage { pageLoading = true pkgsLoading = true - category = 'all' + category = 'featured' query: string data: MarketplaceData @@ -47,15 +47,19 @@ export class MarketplaceListPage { async ngOnInit () { try { - const [data, eos, pkgs] = await Promise.all([ + const [data, eos] = await Promise.all([ this.marketplaceApiService.getMarketplaceData({ }), this.marketplaceApiService.getEos({ }), this.getPkgs(), ]) this.data = data - this.data.categories.unshift(this.category) + this.data.categories.push(this.category) + this.data.categories.unshift('updates') + if (data.categories.includes(this.category)) { + data.categories = data.categories.filter(cat => this.category !== cat) + } + data.categories.unshift(this.category) this.eos = eos - this.pkgs = pkgs } catch (e) { console.error(e) this.errToast.present(e.message) @@ -74,8 +78,7 @@ export class MarketplaceListPage { } async doInfinite (e: any): Promise { - const pkgs = await this.getPkgs() - this.pkgs = this.pkgs.concat(pkgs) + await this.getPkgs(true) e.target.complete() } @@ -83,7 +86,7 @@ export class MarketplaceListPage { this.query = e.target.value || undefined this.page = 1 this.pkgsLoading = true - this.pkgs = await this.getPkgs() + await this.getPkgs() } async updateEos (): Promise { @@ -97,17 +100,26 @@ export class MarketplaceListPage { ) } - private async getPkgs (): Promise { + private async getPkgs (doInfinite = false): Promise { try { - const pkgs = await this.marketplaceService.getPkgs( - this.category !== 'all' ? this.category : undefined, - this.query, - this.page, - this.perPage, - ) - this.needInfinite = pkgs.length >= this.perPage - this.page++ - return pkgs + if (this.category === 'updates') { + this.pkgs = this.marketplaceService.updates + if (this.pkgs.length) { + this.pkgsLoading = false + } + await this.marketplaceService.getUpdates(this.patch.data['package-data']) + this.pkgs = this.marketplaceService.updates + } else { + const pkgs = await this.marketplaceService.getPkgs( + this.category !== 'all' ? this.category : undefined, + this.query, + this.page, + this.perPage, + ) + this.needInfinite = pkgs.length >= this.perPage + this.page++ + this.pkgs = doInfinite ? this.pkgs.concat(pkgs) : pkgs + } } catch (e) { console.error(e) this.errToast.present(e.message) @@ -117,9 +129,10 @@ export class MarketplaceListPage { } async switchCategory (category: string): Promise { + this.pkgs = [] this.category = category this.pkgsLoading = true this.page = 1 - this.pkgs = await this.getPkgs() + await this.getPkgs() } } diff --git a/ui/src/app/pages/marketplace-routes/marketplace.service.ts b/ui/src/app/pages/marketplace-routes/marketplace.service.ts index 9a2a5aff7..463b731b2 100644 --- a/ui/src/app/pages/marketplace-routes/marketplace.service.ts +++ b/ui/src/app/pages/marketplace-routes/marketplace.service.ts @@ -1,20 +1,41 @@ import { Injectable } from '@angular/core' import { MarketplacePkg } from 'src/app/services/api/api.types' import { MarketplaceApiService } from 'src/app/services/api/marketplace/marketplace-api.service' +import { Emver } from 'src/app/services/emver.service' +import { PackageDataEntry } from 'src/app/services/patch-db/data-model' @Injectable({ providedIn: 'root', }) export class MarketplaceService { pkgs: { [id: string]: MarketplacePkg } = { } + updates: MarketplacePkg[] = null releaseNotes: { [id: string]: { [version: string]: string } } constructor ( private readonly marketplaceApiService: MarketplaceApiService, + private readonly emver: Emver, ) { } + async getUpdates (pkgData: { [id: string]: PackageDataEntry}) : Promise { + const idAndCurrentVersions = Object.keys(pkgData).map(key => ({ id: key, version: pkgData[key].manifest.version })) + console.log(JSON.stringify(idAndCurrentVersions)) + const latestPkgs = (await this.marketplaceApiService.getMarketplacePkgs({ + ids: idAndCurrentVersions, + })) + + const updates = latestPkgs.filter(latestPkg => { + const latestVersion = latestPkg.manifest.version + const curVersion = pkgData[latestPkg.manifest.id]?.manifest.version + return !!curVersion && this.emver.compare(latestVersion, curVersion) === 1 + }) + + this.updates = updates + return updates + } + async getPkgs (category: string, query: string, page: number, perPage: number) : Promise { const pkgs = await this.marketplaceApiService.getMarketplacePkgs({ category: category !== 'all' ? category : undefined, @@ -30,7 +51,7 @@ export class MarketplaceService { } async getPkg (id: string, version?: string): Promise { - const pkg = (await this.marketplaceApiService.getMarketplacePkgs({ id, version }))[0] + const pkg = (await this.marketplaceApiService.getMarketplacePkgs({ ids: [{ id, version }]}))[0] if (pkg) { this.pkgs[id] = pkg } else { diff --git a/ui/src/app/pages/server-routes/privacy/privacy.page.html b/ui/src/app/pages/server-routes/privacy/privacy.page.html index dfbce4048..d0bad14db 100644 --- a/ui/src/app/pages/server-routes/privacy/privacy.page.html +++ b/ui/src/app/pages/server-routes/privacy/privacy.page.html @@ -15,20 +15,20 @@ Use Tor {{ patch.data['server-info']['eos-marketplace'] === config.start9Marketplace.tor }} - + Auto Check for Updates {{ patch.data.ui['auto-check-updates'] }} - + diff --git a/ui/src/app/services/api/api.types.ts b/ui/src/app/services/api/api.types.ts index bc16e057f..db768072d 100644 --- a/ui/src/app/services/api/api.types.ts +++ b/ui/src/app/services/api/api.types.ts @@ -168,10 +168,7 @@ export module RR { export type GetMarketplaceEOSRes = MarketplaceEOS export type GetMarketplacePackagesReq = { - ids?: string[] - id?: string - // iff id - version?: string + ids?: { id: string, version: string }[] // iff !id category?: string query?: string @@ -182,13 +179,17 @@ export module RR { export type GetReleaseNotesReq = { id: string } export type GetReleaseNotesRes = { [version: string]: string} + + export type GetLatestVersionReq = { ids: string[] } + export type GetLatestVersionRes = { [id: string]: string} + } export type WithExpire = { 'expire-id'?: string } & T export type WithRevision = { response: T, revision?: Revision } export interface MarketplaceData { - categories: string[] + categories: string[], } export interface MarketplaceEOS { diff --git a/ui/src/app/services/api/embassy/embassy-api.service.ts b/ui/src/app/services/api/embassy/embassy-api.service.ts index 575494e8b..12ca20593 100644 --- a/ui/src/app/services/api/embassy/embassy-api.service.ts +++ b/ui/src/app/services/api/embassy/embassy-api.service.ts @@ -60,10 +60,10 @@ export abstract class ApiService implements Source, Http { () => this.setEosMarketplaceRaw(isTor), )() - protected abstract setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise - setPackageMarketplace = (params: RR.SetPackageMarketplaceReq) => this.syncResponse( - () => this.setPackageMarketplaceRaw(params), - )() + // protected abstract setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise + // setPackageMarketplace = (params: RR.SetPackageMarketplaceReq) => this.syncResponse( + // () => this.setPackageMarketplaceRaw(params), + // )() // password abstract updatePassword (params: RR.UpdatePasswordReq): Promise diff --git a/ui/src/app/services/api/embassy/embassy-live-api.service.ts b/ui/src/app/services/api/embassy/embassy-live-api.service.ts index f80eebb87..1a0826fea 100644 --- a/ui/src/app/services/api/embassy/embassy-live-api.service.ts +++ b/ui/src/app/services/api/embassy/embassy-live-api.service.ts @@ -78,9 +78,9 @@ export class LiveApiService extends ApiService { return this.http.rpcRequest({ method: 'marketplace.eos.set', params }) } - async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise { - return this.http.rpcRequest({ method: 'marketplace.package.set', params }) - } + // async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise { + // return this.http.rpcRequest({ method: 'marketplace.package.set', params }) + // } // password async updatePassword (params: RR.UpdatePasswordReq): Promise { diff --git a/ui/src/app/services/api/embassy/embassy-mock-api.service.ts b/ui/src/app/services/api/embassy/embassy-mock-api.service.ts index 1c5f54d1f..515a7c65b 100644 --- a/ui/src/app/services/api/embassy/embassy-mock-api.service.ts +++ b/ui/src/app/services/api/embassy/embassy-mock-api.service.ts @@ -138,17 +138,17 @@ export class MockApiService extends ApiService { return this.http.rpcRequest({ method: 'db.patch', params: { patch } }) } - async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise { - await pauseFor(2000) - const patch = [ - { - op: PatchOp.REPLACE, - path: '/server-info/package-marketplace', - value: params.url, - }, - ] - return this.http.rpcRequest({ method: 'db.patch', params: { patch } }) - } + // async setPackageMarketplaceRaw (params: RR.SetPackageMarketplaceReq): Promise { + // await pauseFor(2000) + // const patch = [ + // { + // op: PatchOp.REPLACE, + // path: '/server-info/package-marketplace', + // value: params.url, + // }, + // ] + // return this.http.rpcRequest({ method: 'db.patch', params: { patch } }) + // } // password async updatePassword (params: RR.UpdatePasswordReq): Promise { diff --git a/ui/src/app/services/api/marketplace/marketplace-api.service.ts b/ui/src/app/services/api/marketplace/marketplace-api.service.ts index 9baea6ee6..eeb7f98b5 100644 --- a/ui/src/app/services/api/marketplace/marketplace-api.service.ts +++ b/ui/src/app/services/api/marketplace/marketplace-api.service.ts @@ -17,12 +17,17 @@ export abstract class MarketplaceApiService { abstract getReleaseNotes (params: RR.GetReleaseNotesReq): Promise - getMarketplaceURL (type: 'eos' | 'package'): string { + abstract getLatestVersion (params: RR.GetLatestVersionReq): Promise + + getMarketplaceURL (type: 'eos' | 'package', defaultToTor = false): string { + const packageMarketplace = this.patch.data['server-info']['package-marketplace'] + if (defaultToTor && !packageMarketplace) { + return this.config.start9Marketplace.tor + } const eosMarketplace = this.patch.data['server-info']['eos-marketplace'] || this.config.start9Marketplace.clearnet if (type === 'eos') { return eosMarketplace } else { - const packageMarketplace = this.patch.data['server-info']['package-marketplace'] return packageMarketplace || eosMarketplace } } diff --git a/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts b/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts index 54478e045..51a0bf128 100644 --- a/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts +++ b/ui/src/app/services/api/marketplace/marketplace-live-api.service.ts @@ -25,10 +25,21 @@ export class MarketplaceLiveApiService extends MarketplaceApiService { } async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise { - return this.http.simpleGet(this.getMarketplaceURL('package'), params) + const url = this.getMarketplaceURL('package', params.ids?.length > 1) + const threadParams = { + ...params, + ids: JSON.stringify(params.ids), + } + + return this.http.simpleGet(url, threadParams) } async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise { return this.http.simpleGet(this.getMarketplaceURL('package'), params) } + + async getLatestVersion (params: RR.GetLatestVersionReq): Promise { + const url = this.getMarketplaceURL('package', params.ids?.length > 1) + return this.http.simpleGet(url, params) + } } diff --git a/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts b/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts index 7803b8106..201b345f7 100644 --- a/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts +++ b/ui/src/app/services/api/marketplace/marketplace-mock-api.service.ts @@ -6,9 +6,11 @@ import { HttpService } from '../../http.service' import { MarketplaceApiService } from './marketplace-api.service' import { PatchDbService } from '../../patch-db/patch-db.service' import { ConfigService } from '../../config.service' +import { Storage } from '@ionic/storage' @Injectable() export class MarketplaceMockApiService extends MarketplaceApiService { + CONTENT_KEY = 'marketplace-cache' constructor ( private readonly http: HttpService, @@ -38,18 +40,22 @@ export class MarketplaceMockApiService extends MarketplaceApiService { categories: ['featured', 'bitcoin', 'lightning', 'data', 'messaging', 'social', 'alt coin'], } } - url = `${url}/marketplace/data` + url = `${url}/data` return this.http.simpleGet(url) } async getMarketplacePkgs (params: RR.GetMarketplacePackagesReq): Promise { - let url = this.getMarketplaceURL('package') + let url = this.getMarketplaceURL('package', params.ids?.length > 1) + const threadParams = { + ...params, + ids: JSON.stringify(params.ids), + } if (this.useLocal(url)) { await pauseFor(2000) return Mock.AvailableList } - url = `${url}/marketplace/packages` - return this.http.simpleGet(url, params) + url = `${url}/packages` + return this.http.simpleGet(url, threadParams) } async getReleaseNotes (params: RR.GetReleaseNotesReq): Promise { @@ -58,8 +64,21 @@ export class MarketplaceMockApiService extends MarketplaceApiService { await pauseFor(2000) return Mock.ReleaseNotes } - url = `${url}/marketplace/release-notes` - return this.http.simpleGet(url) + url = `${url}/release-notes` + return this.http.simpleGet(url, params) + } + + async getLatestVersion (params: RR.GetLatestVersionReq): Promise { + let url = this.getMarketplaceURL('package', params.ids?.length > 1) + if (this.useLocal(url)) { + await pauseFor(2000) + return params.ids.reduce((obj, id) => { + obj[id] = this.patch.data['package-data']?.[id]?.manifest.version.replace('0', '1') + return obj + }, { }) + } + url = `${url}/latest-version` + return this.http.simpleGet(url) } private useLocal (url: string): boolean { diff --git a/ui/src/app/services/server-config.service.ts b/ui/src/app/services/server-config.service.ts index 6408f17e4..8ef590047 100644 --- a/ui/src/app/services/server-config.service.ts +++ b/ui/src/app/services/server-config.service.ts @@ -43,12 +43,12 @@ export class ServerConfigService { eosMarketplace: async (enabled: boolean) => { return this.apiService.setEosMarketplace(enabled) }, - packageMarketplace: async (url: string) => { - return this.apiService.setPackageMarketplace({ url }) - }, - password: async (password: string) => { - return this.apiService.updatePassword({ password }) - }, + // packageMarketplace: async (url: string) => { + // return this.apiService.setPackageMarketplace({ url }) + // }, + // password: async (password: string) => { + // return this.apiService.updatePassword({ password }) + // }, } } @@ -76,27 +76,27 @@ const serverConfig: ConfigSpec = { description: `Use Start9's Tor Hidden Service Marketplace (instead of clearnet).`, default: false, }, - packageMarketplace: { - type: 'string', - name: 'Package Marketplace', - description: `Use for alternative embassy marketplace. Leave empty to use start9's marketplace.`, - nullable: true, - // @TODO regex for URL - // pattern: '', - patternDescription: 'Must be a valid URL.', - masked: false, - copyable: false, - }, - password: { - type: 'string', - name: 'Change Password', - description: `Your Embassy's master password, used for authentication and disk encryption.`, - nullable: false, - // @TODO regex for 12 chars - // pattern: '', - patternDescription: 'Must contain at least 12 characters.', - changeWarning: 'If you forget your master password, there is absolutely no way to recover your data. This can result in loss of money! Keep in mind, old backups will still be encrypted by the password used to encrypt them.', - masked: false, - copyable: false, - }, + // packageMarketplace: { + // type: 'string', + // name: 'Package Marketplace', + // description: `Use for alternative embassy marketplace. Leave empty to use start9's marketplace.`, + // nullable: true, + // // @TODO regex for URL + // // pattern: '', + // patternDescription: 'Must be a valid URL.', + // masked: false, + // copyable: false, + // }, + // password: { + // type: 'string', + // name: 'Change Password', + // description: `Your Embassy's master password, used for authentication and disk encryption.`, + // nullable: false, + // // @TODO regex for 12 chars + // // pattern: '', + // patternDescription: 'Must contain at least 12 characters.', + // changeWarning: 'If you forget your master password, there is absolutely no way to recover your data. This can result in loss of money! Keep in mind, old backups will still be encrypted by the password used to encrypt them.', + // masked: false, + // copyable: false, + // }, } diff --git a/ui/src/app/services/startup-alerts.service.ts b/ui/src/app/services/startup-alerts.service.ts index c71e5be15..6f8813ace 100644 --- a/ui/src/app/services/startup-alerts.service.ts +++ b/ui/src/app/services/startup-alerts.service.ts @@ -5,10 +5,11 @@ import { WizardBaker } from '../components/install-wizard/prebaked-wizards' import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page' import { displayEmver } from '../pipes/emver.pipe' import { RR } from './api/api.types' -import { MarketplaceApiService } from './api/marketplace/marketplace-api.service' import { PatchDbService } from './patch-db/patch-db.service' import { ConfigService } from './config.service' import { Emver } from './emver.service' +import { MarketplaceService } from '../pages/marketplace-routes/marketplace.service' +import { MarketplaceApiService } from './api/marketplace/marketplace-api.service' @Injectable({ providedIn: 'root', @@ -21,6 +22,7 @@ export class StartupAlertsService { private readonly navCtrl: NavController, private readonly config: ConfigService, private readonly modalCtrl: ModalController, + private readonly marketplaceService: MarketplaceService, private readonly marketplaceApi: MarketplaceApiService, private readonly emver: Emver, private readonly wizardBaker: WizardBaker, @@ -63,6 +65,7 @@ export class StartupAlertsService { let checkRes: any try { checkRes = await c.check() + console.log('CHECK RES', checkRes) } catch (e) { console.error(`Exception in ${c.name} check:`, e) return true @@ -99,15 +102,8 @@ export class StartupAlertsService { } private async appsCheck (): Promise { - const pkgs = await this.marketplaceApi.getMarketplacePkgs({ - ids: Object.keys(this.patch.data['package-data']).filter(id => { - return !!this.patch.data['package-data'][id].installed - }), - }) - return !!pkgs.find(pkg => { - const versionInstalled = this.patch.data['package-data'][pkg.manifest.id].manifest.version - return this.emver.compare(versionInstalled, pkg.manifest.version) === -1 - }) + const updates = await this.marketplaceService.getUpdates(this.patch.data['package-data']) + return !!updates.length } private async displayOsWelcome (): Promise {