From 5272bc4cd961eac164ae861a98cc421575f9ac4c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Dec 2024 16:46:21 +0000 Subject: [PATCH] Convert portal apps store to TS --- .../src/stores/{BudiStore.js => BudiStore.ts} | 17 +++- .../src/stores/portal/{apps.js => apps.ts} | 99 ++++++++++++------- 2 files changed, 77 insertions(+), 39 deletions(-) rename packages/builder/src/stores/{BudiStore.js => BudiStore.ts} (63%) rename packages/builder/src/stores/portal/{apps.js => apps.ts} (67%) diff --git a/packages/builder/src/stores/BudiStore.js b/packages/builder/src/stores/BudiStore.ts similarity index 63% rename from packages/builder/src/stores/BudiStore.js rename to packages/builder/src/stores/BudiStore.ts index e854aa6588..19b2c394fb 100644 --- a/packages/builder/src/stores/BudiStore.js +++ b/packages/builder/src/stores/BudiStore.ts @@ -1,8 +1,17 @@ -import { writable } from "svelte/store" +import { writable, Writable } from "svelte/store" -export default class BudiStore { - constructor(init, opts) { - const store = writable({ ...init }) +interface BudiStoreOpts { + debug?: boolean +} + +export default class BudiStore implements Writable { + store: Writable + subscribe: Writable["subscribe"] + update: Writable["update"] + set: Writable["set"] + + constructor(init: T, opts?: BudiStoreOpts) { + const store = writable({ ...init }) /** * Internal Svelte store diff --git a/packages/builder/src/stores/portal/apps.js b/packages/builder/src/stores/portal/apps.ts similarity index 67% rename from packages/builder/src/stores/portal/apps.js rename to packages/builder/src/stores/portal/apps.ts index c543722604..4e673fc129 100644 --- a/packages/builder/src/stores/portal/apps.js +++ b/packages/builder/src/stores/portal/apps.ts @@ -1,19 +1,39 @@ import { derived } from "svelte/store" +// @ts-ignore import { AppStatus } from "constants" import { API } from "api" import { auth } from "./auth" -import BudiStore from "../BudiStore" // move this +import BudiStore from "../BudiStore" +import { App, UpdateAppRequest, User } from "@budibase/types" -// properties that should always come from the dev app, not the deployed -const DEV_PROPS = ["updatedBy", "updatedAt"] - -export const INITIAL_APPS_STATE = { - apps: [], +interface AppIdentifierMetadata { + devId?: string + devRev?: string + prodId?: string + prodRev?: string } -export class AppsStore extends BudiStore { +interface AppUIMetadata { + deployed: boolean + lockedYou: boolean + lockedOther: boolean + favourite: boolean +} + +interface StoreApp extends App, AppIdentifierMetadata {} + +interface EnrichedApp extends StoreApp, AppUIMetadata {} + +interface PortalAppsStore { + apps: StoreApp[] + sortBy?: string +} + +export class AppsStore extends BudiStore { constructor() { - super({ ...INITIAL_APPS_STATE }) + super({ + apps: [], + }) this.extractAppId = this.extractAppId.bind(this) this.getProdAppID = this.getProdAppID.bind(this) @@ -22,12 +42,12 @@ export class AppsStore extends BudiStore { this.save = this.save.bind(this) } - extractAppId(id) { - const split = id?.split("_") || [] + extractAppId(appId?: string) { + const split = appId?.split("_") || [] return split.length ? split[split.length - 1] : null } - getProdAppID(appId) { + getProdAppID(appId: string) { if (!appId) { return appId } @@ -47,15 +67,15 @@ export class AppsStore extends BudiStore { return `app${separator}${rest}` } - updateSort(sortBy) { + async updateSort(sortBy: string) { this.update(state => ({ ...state, sortBy, })) - this.updateUserSort(sortBy) + await this.updateUserSort(sortBy) } - async updateUserSort(sortBy) { + async updateUserSort(sortBy: string) { try { await auth.updateSelf({ appSort: sortBy }) } catch (err) { @@ -64,16 +84,19 @@ export class AppsStore extends BudiStore { } async load() { - const json = await API.getApps() + const json = (await API.getApps()) as App[] if (Array.isArray(json)) { // Merge apps into one sensible list - let appMap = {} + let appMap: Record = {} let devApps = json.filter(app => app.status === AppStatus.DEV) let deployedApps = json.filter(app => app.status === AppStatus.DEPLOYED) // First append all dev app version devApps.forEach(app => { const id = this.extractAppId(app.appId) + if (!id) { + return + } appMap[id] = { ...app, devId: app.appId, @@ -84,20 +107,22 @@ export class AppsStore extends BudiStore { // Then merge with all prod app versions deployedApps.forEach(app => { const id = this.extractAppId(app.appId) + if (!id) { + return + } // Skip any deployed apps which don't have a dev counterpart if (!appMap[id]) { return } - let devProps = {} + // Extract certain properties from the dev app to override the prod app + let devProps: Pick = {} if (appMap[id]) { - const entries = Object.entries(appMap[id]).filter( - ([key]) => DEV_PROPS.indexOf(key) !== -1 - ) - entries.forEach(entry => { - devProps[entry[0]] = entry[1] - }) + devProps = { + updatedBy: appMap[id].updatedBy, + updatedAt: appMap[id].updatedAt, + } } appMap[id] = { ...appMap[id], @@ -111,7 +136,10 @@ export class AppsStore extends BudiStore { // Transform into an array and clean up const apps = Object.values(appMap) apps.forEach(app => { - app.appId = this.extractAppId(app.devId) + const appId = this.extractAppId(app.devId) + if (appId) { + app.appId = appId + } delete app._id delete app._rev }) @@ -127,7 +155,7 @@ export class AppsStore extends BudiStore { } } - async save(appId, value) { + async save(appId: string, value: UpdateAppRequest) { await API.saveAppMetadata(appId, value) this.update(state => { const updatedAppIndex = state.apps.findIndex( @@ -146,22 +174,23 @@ export class AppsStore extends BudiStore { export const appsStore = new AppsStore() export const sortBy = derived([appsStore, auth], ([$store, $auth]) => { - return $store.sortBy || $auth.user?.appSort || "name" + return $store.sortBy || ($auth.user as User | null)?.appSort || "name" }) // Centralise any logic that enriches the apps list export const enrichedApps = derived( [appsStore, auth, sortBy], ([$store, $auth, $sortBy]) => { - const enrichedApps = $store.apps - ? $store.apps.map(app => ({ - ...app, - deployed: app.status === AppStatus.DEPLOYED, - lockedYou: app.lockedBy && app.lockedBy.email === $auth.user?.email, - lockedOther: app.lockedBy && app.lockedBy.email !== $auth.user?.email, - favourite: $auth.user?.appFavourites?.includes(app.appId), - })) - : [] + const enrichedApps: EnrichedApp[] = $store.apps.map(app => { + const user = $auth.user as User | null + return { + ...app, + deployed: app.status === AppStatus.DEPLOYED, + lockedYou: app.lockedBy != null && app.lockedBy.email === user?.email, + lockedOther: app.lockedBy != null && app.lockedBy.email !== user?.email, + favourite: !!user?.appFavourites?.includes(app.appId), + } + }) if ($sortBy === "status") { return enrichedApps.sort((a, b) => {