From fa06f7b6e8f0911f5bb0d3d0a7a96f2795fac3a3 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 2 Jan 2025 10:29:39 +0000 Subject: [PATCH 01/14] Minor bug fix to ensure getPathSteps builds correctly. This will fix the test details --- packages/builder/src/stores/builder/automations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/stores/builder/automations.ts b/packages/builder/src/stores/builder/automations.ts index c43a856cc4..9b20b4cd03 100644 --- a/packages/builder/src/stores/builder/automations.ts +++ b/packages/builder/src/stores/builder/automations.ts @@ -291,8 +291,8 @@ const automationActions = (store: AutomationStore) => ({ let result: (AutomationStep | AutomationTrigger)[] = [] pathWay.forEach(path => { const { stepIdx, branchIdx } = path - let last = result ? result[result.length - 1] : [] - if (!result) { + let last = result.length ? result[result.length - 1] : [] + if (!result.length) { // Preceeding steps. result = steps.slice(0, stepIdx + 1) return From 8047ff99c6a7685b84d432d82ee80ef5cd9b2468 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 10:59:14 +0000 Subject: [PATCH 02/14] Flags / Layout / Preview stores to TS --- packages/builder/src/stores/builder/flags.js | 27 ------ packages/builder/src/stores/builder/flags.ts | 41 +++++++++ .../stores/builder/{layouts.js => layouts.ts} | 20 +++-- .../builder/src/stores/builder/preview.js | 80 ----------------- .../builder/src/stores/builder/preview.ts | 90 +++++++++++++++++++ 5 files changed, 144 insertions(+), 114 deletions(-) delete mode 100644 packages/builder/src/stores/builder/flags.js create mode 100644 packages/builder/src/stores/builder/flags.ts rename packages/builder/src/stores/builder/{layouts.js => layouts.ts} (77%) delete mode 100644 packages/builder/src/stores/builder/preview.js create mode 100644 packages/builder/src/stores/builder/preview.ts diff --git a/packages/builder/src/stores/builder/flags.js b/packages/builder/src/stores/builder/flags.js deleted file mode 100644 index 21236ffb01..0000000000 --- a/packages/builder/src/stores/builder/flags.js +++ /dev/null @@ -1,27 +0,0 @@ -import { writable } from "svelte/store" -import { API } from "@/api" - -export function createFlagsStore() { - const { subscribe, set } = writable({}) - - const actions = { - fetch: async () => { - const flags = await API.getFlags() - set(flags) - }, - updateFlag: async (flag, value) => { - await API.updateFlag(flag, value) - await actions.fetch() - }, - toggleUiFeature: async feature => { - await API.toggleUiFeature(feature) - }, - } - - return { - subscribe, - ...actions, - } -} - -export const flags = createFlagsStore() diff --git a/packages/builder/src/stores/builder/flags.ts b/packages/builder/src/stores/builder/flags.ts new file mode 100644 index 0000000000..cff3ac4d12 --- /dev/null +++ b/packages/builder/src/stores/builder/flags.ts @@ -0,0 +1,41 @@ +import { get } from "svelte/store" +import { API } from "@/api" +import { GetUserFlagsResponse } from "@budibase/types" +import { BudiStore } from "../BudiStore" + +interface FlagsState { + flags: GetUserFlagsResponse +} + +const INITIAL_FLAGS_STATE: FlagsState = { + flags: {}, +} + +export class FlagsStore extends BudiStore { + constructor() { + super(INITIAL_FLAGS_STATE) + + this.fetch = this.fetch.bind(this) + this.updateFlag = this.updateFlag.bind(this) + this.toggleUiFeature = this.toggleUiFeature.bind(this) + } + + async fetch() { + const flags = await API.getFlags() + this.update(state => ({ + ...state, + flags, + })) + } + + async updateFlag(flag: string, value: any) { + await API.updateFlag(flag, value) + await this.fetch() + } + + async toggleUiFeature(feature: string) { + await API.toggleUiFeature(feature) + } +} + +export const flags = new FlagsStore() diff --git a/packages/builder/src/stores/builder/layouts.js b/packages/builder/src/stores/builder/layouts.ts similarity index 77% rename from packages/builder/src/stores/builder/layouts.js rename to packages/builder/src/stores/builder/layouts.ts index 7617e203f0..0fd8efc8c0 100644 --- a/packages/builder/src/stores/builder/layouts.js +++ b/packages/builder/src/stores/builder/layouts.ts @@ -2,13 +2,19 @@ import { derived, get } from "svelte/store" import { componentStore } from "@/stores/builder" import { API } from "@/api" import { BudiStore } from "../BudiStore" +import { Layout } from "@budibase/types" -export const INITIAL_LAYOUT_STATE = { +interface LayoutState { + layouts: Layout[] + selectedLayoutId: string | null +} + +export const INITIAL_LAYOUT_STATE: LayoutState = { layouts: [], selectedLayoutId: null, } -export class LayoutStore extends BudiStore { +export class LayoutStore extends BudiStore { constructor() { super(INITIAL_LAYOUT_STATE) @@ -22,14 +28,14 @@ export class LayoutStore extends BudiStore { this.store.set({ ...INITIAL_LAYOUT_STATE }) } - syncAppLayouts(pkg) { + syncAppLayouts(pkg: { layouts: Layout[] }) { this.update(state => ({ ...state, layouts: [...pkg.layouts], })) } - select(layoutId) { + select(layoutId: string) { // Check this layout exists const state = get(this.store) const componentState = get(componentStore) @@ -48,18 +54,18 @@ export class LayoutStore extends BudiStore { // Select new layout this.update(state => { - state.selectedLayoutId = layout._id + state.selectedLayoutId = layout._id! return state }) componentStore.select(layout.props?._id) } - async deleteLayout(layout) { + async deleteLayout(layout: Layout) { if (!layout?._id) { return } - await API.deleteLayout(layout._id, layout._rev) + await API.deleteLayout(layout._id, layout._rev!) this.update(state => { state.layouts = state.layouts.filter(x => x._id !== layout._id) return state diff --git a/packages/builder/src/stores/builder/preview.js b/packages/builder/src/stores/builder/preview.js deleted file mode 100644 index 4923185ee7..0000000000 --- a/packages/builder/src/stores/builder/preview.js +++ /dev/null @@ -1,80 +0,0 @@ -import { writable, get } from "svelte/store" - -const INITIAL_PREVIEW_STATE = { - previewDevice: "desktop", - previewEventHandler: null, - showPreview: false, - selectedComponentContext: null, -} - -export const createPreviewStore = () => { - const store = writable({ - ...INITIAL_PREVIEW_STATE, - }) - - const setDevice = device => { - store.update(state => { - state.previewDevice = device - return state - }) - } - - // Potential evt names "eject-block", "dragging-new-component" - const sendEvent = (name, payload) => { - const { previewEventHandler } = get(store) - previewEventHandler?.(name, payload) - } - - const registerEventHandler = handler => { - store.update(state => { - state.previewEventHandler = handler - return state - }) - } - - const startDrag = component => { - sendEvent("dragging-new-component", { - dragging: true, - component, - }) - } - - const stopDrag = () => { - sendEvent("dragging-new-component", { - dragging: false, - }) - } - - //load preview? - const showPreview = isVisible => { - store.update(state => { - state.showPreview = isVisible - return state - }) - } - - const setSelectedComponentContext = context => { - store.update(state => { - state.selectedComponentContext = context - return state - }) - } - - const requestComponentContext = () => { - sendEvent("request-context") - } - - return { - subscribe: store.subscribe, - setDevice, - sendEvent, //may not be required - registerEventHandler, - startDrag, - stopDrag, - showPreview, - setSelectedComponentContext, - requestComponentContext, - } -} - -export const previewStore = createPreviewStore() diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts new file mode 100644 index 0000000000..fe14c02535 --- /dev/null +++ b/packages/builder/src/stores/builder/preview.ts @@ -0,0 +1,90 @@ +import { get } from "svelte/store" +import { BudiStore } from "../BudiStore" + +type PreviewDevice = "desktop" | "tablet" | "mobile" +type PreviewEventHandler = (name: string, payload?: any) => void +type ComponentContext = Record + +interface PreviewState { + previewDevice: PreviewDevice + previewEventHandler: PreviewEventHandler | null + showPreview: boolean + selectedComponentContext: ComponentContext | null +} + +const INITIAL_PREVIEW_STATE: PreviewState = { + previewDevice: "desktop", + previewEventHandler: null, + showPreview: false, + selectedComponentContext: null, +} + +export class PreviewStore extends BudiStore { + constructor() { + super(INITIAL_PREVIEW_STATE) + + this.setDevice = this.setDevice.bind(this) + this.sendEvent = this.sendEvent.bind(this) + this.registerEventHandler = this.registerEventHandler.bind(this) + this.startDrag = this.startDrag.bind(this) + this.stopDrag = this.stopDrag.bind(this) + this.showPreview = this.showPreview.bind(this) + this.setSelectedComponentContext = + this.setSelectedComponentContext.bind(this) + this.requestComponentContext = this.requestComponentContext.bind(this) + } + + setDevice(device: PreviewDevice) { + this.update(state => ({ + ...state, + previewDevice: device, + })) + } + + // Potential evt names "eject-block", "dragging-new-component" + sendEvent(name: string, payload?: any) { + const { previewEventHandler } = get(this.store) + previewEventHandler?.(name, payload) + } + + registerEventHandler(handler: PreviewEventHandler) { + this.update(state => ({ + ...state, + previewEventHandler: handler, + })) + } + + startDrag(component: any) { + this.sendEvent("dragging-new-component", { + dragging: true, + component, + }) + } + + stopDrag() { + this.sendEvent("dragging-new-component", { + dragging: false, + }) + } + + //load preview? + showPreview(isVisible: boolean) { + this.update(state => ({ + ...state, + showPreview: isVisible, + })) + } + + setSelectedComponentContext(context: ComponentContext) { + this.update(state => ({ + ...state, + selectedComponentContext: context, + })) + } + + requestComponentContext() { + this.sendEvent("request-context") + } +} + +export const previewStore = new PreviewStore() From 7c36d8dac56a64f7b612f99af016f862d346daac Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 11:04:30 +0000 Subject: [PATCH 03/14] convert selectedLayout derived store --- .../builder/src/stores/builder/layouts.ts | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/stores/builder/layouts.ts b/packages/builder/src/stores/builder/layouts.ts index 0fd8efc8c0..41e61e3611 100644 --- a/packages/builder/src/stores/builder/layouts.ts +++ b/packages/builder/src/stores/builder/layouts.ts @@ -1,7 +1,7 @@ import { derived, get } from "svelte/store" import { componentStore } from "@/stores/builder" import { API } from "@/api" -import { BudiStore } from "../BudiStore" +import { BudiStore, DerivedBudiStore } from "../BudiStore" import { Layout } from "@budibase/types" interface LayoutState { @@ -9,6 +9,10 @@ interface LayoutState { selectedLayoutId: string | null } +interface DerivedLayoutState extends LayoutState { + selectedLayout: Layout | null +} + export const INITIAL_LAYOUT_STATE: LayoutState = { layouts: [], selectedLayoutId: null, @@ -62,10 +66,10 @@ export class LayoutStore extends BudiStore { } async deleteLayout(layout: Layout) { - if (!layout?._id) { + if (!layout?._id || !layout?._rev) { return } - await API.deleteLayout(layout._id, layout._rev!) + await API.deleteLayout(layout._id, layout._rev) this.update(state => { state.layouts = state.layouts.filter(x => x._id !== layout._id) return state @@ -75,6 +79,24 @@ export class LayoutStore extends BudiStore { export const layoutStore = new LayoutStore() -export const selectedLayout = derived(layoutStore, $store => { - return $store.layouts?.find(layout => layout._id === $store.selectedLayoutId) -}) +export class SelectedLayoutStore extends DerivedBudiStore< + LayoutState, + DerivedLayoutState +> { + constructor(layoutStore: LayoutStore) { + const makeDerivedStore = () => { + return derived(layoutStore, $store => { + return { + ...$store, + selectedLayout: + $store.layouts?.find( + layout => layout._id === $store.selectedLayoutId + ) || null, + } + }) + } + super(INITIAL_LAYOUT_STATE, makeDerivedStore) + } +} + +export const selectedLayout = new SelectedLayoutStore(layoutStore) From 11e55bfbd72d653adea51e3ba3ed4026b0922c5f Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 11:19:20 +0000 Subject: [PATCH 04/14] move types --- packages/builder/src/stores/builder/preview.ts | 2 +- packages/types/src/ui/stores/index.ts | 1 + packages/types/src/ui/stores/preview.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 packages/types/src/ui/stores/preview.ts diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts index fe14c02535..0ec2811e15 100644 --- a/packages/builder/src/stores/builder/preview.ts +++ b/packages/builder/src/stores/builder/preview.ts @@ -1,7 +1,7 @@ import { get } from "svelte/store" import { BudiStore } from "../BudiStore" +import { PreviewDevice } from "@budibase/types" -type PreviewDevice = "desktop" | "tablet" | "mobile" type PreviewEventHandler = (name: string, payload?: any) => void type ComponentContext = Record diff --git a/packages/types/src/ui/stores/index.ts b/packages/types/src/ui/stores/index.ts index 8dae68862e..1b82a06388 100644 --- a/packages/types/src/ui/stores/index.ts +++ b/packages/types/src/ui/stores/index.ts @@ -1,3 +1,4 @@ export * from "./integration" export * from "./automations" export * from "./grid" +export * from "./preview" diff --git a/packages/types/src/ui/stores/preview.ts b/packages/types/src/ui/stores/preview.ts new file mode 100644 index 0000000000..4d09366ff5 --- /dev/null +++ b/packages/types/src/ui/stores/preview.ts @@ -0,0 +1 @@ +export type PreviewDevice = "desktop" | "tablet" | "mobile" From 2f1b7811c3eebb265f3717be94350106fff37277 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 11:25:22 +0000 Subject: [PATCH 05/14] unused import --- packages/builder/src/stores/builder/flags.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/stores/builder/flags.ts b/packages/builder/src/stores/builder/flags.ts index cff3ac4d12..6d2150f49b 100644 --- a/packages/builder/src/stores/builder/flags.ts +++ b/packages/builder/src/stores/builder/flags.ts @@ -1,4 +1,3 @@ -import { get } from "svelte/store" import { API } from "@/api" import { GetUserFlagsResponse } from "@budibase/types" import { BudiStore } from "../BudiStore" From 4ee99f807c47ff442cccfddac2d784a8bccd85cb Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 12:22:46 +0000 Subject: [PATCH 06/14] revert derivedstore implementation --- packages/builder/src/stores/builder/flags.ts | 1 + .../builder/src/stores/builder/layouts.ts | 30 +++---------------- .../builder/src/stores/builder/preview.ts | 2 +- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/stores/builder/flags.ts b/packages/builder/src/stores/builder/flags.ts index 6d2150f49b..cff3ac4d12 100644 --- a/packages/builder/src/stores/builder/flags.ts +++ b/packages/builder/src/stores/builder/flags.ts @@ -1,3 +1,4 @@ +import { get } from "svelte/store" import { API } from "@/api" import { GetUserFlagsResponse } from "@budibase/types" import { BudiStore } from "../BudiStore" diff --git a/packages/builder/src/stores/builder/layouts.ts b/packages/builder/src/stores/builder/layouts.ts index 41e61e3611..af7b17fcc7 100644 --- a/packages/builder/src/stores/builder/layouts.ts +++ b/packages/builder/src/stores/builder/layouts.ts @@ -1,7 +1,7 @@ import { derived, get } from "svelte/store" import { componentStore } from "@/stores/builder" import { API } from "@/api" -import { BudiStore, DerivedBudiStore } from "../BudiStore" +import { BudiStore } from "../BudiStore" import { Layout } from "@budibase/types" interface LayoutState { @@ -9,10 +9,6 @@ interface LayoutState { selectedLayoutId: string | null } -interface DerivedLayoutState extends LayoutState { - selectedLayout: Layout | null -} - export const INITIAL_LAYOUT_STATE: LayoutState = { layouts: [], selectedLayoutId: null, @@ -79,24 +75,6 @@ export class LayoutStore extends BudiStore { export const layoutStore = new LayoutStore() -export class SelectedLayoutStore extends DerivedBudiStore< - LayoutState, - DerivedLayoutState -> { - constructor(layoutStore: LayoutStore) { - const makeDerivedStore = () => { - return derived(layoutStore, $store => { - return { - ...$store, - selectedLayout: - $store.layouts?.find( - layout => layout._id === $store.selectedLayoutId - ) || null, - } - }) - } - super(INITIAL_LAYOUT_STATE, makeDerivedStore) - } -} - -export const selectedLayout = new SelectedLayoutStore(layoutStore) +export const selectedLayout = derived(layoutStore, $store => { + return $store.layouts?.find(layout => layout._id === $store.selectedLayoutId) +}) diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts index 0ec2811e15..fe14c02535 100644 --- a/packages/builder/src/stores/builder/preview.ts +++ b/packages/builder/src/stores/builder/preview.ts @@ -1,7 +1,7 @@ import { get } from "svelte/store" import { BudiStore } from "../BudiStore" -import { PreviewDevice } from "@budibase/types" +type PreviewDevice = "desktop" | "tablet" | "mobile" type PreviewEventHandler = (name: string, payload?: any) => void type ComponentContext = Record From 6d95076d28373dbfe5a79a36a7ba5940771647fd Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Jan 2025 12:59:25 +0000 Subject: [PATCH 07/14] lint --- packages/builder/src/stores/builder/flags.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/stores/builder/flags.ts b/packages/builder/src/stores/builder/flags.ts index cff3ac4d12..6d2150f49b 100644 --- a/packages/builder/src/stores/builder/flags.ts +++ b/packages/builder/src/stores/builder/flags.ts @@ -1,4 +1,3 @@ -import { get } from "svelte/store" import { API } from "@/api" import { GetUserFlagsResponse } from "@budibase/types" import { BudiStore } from "../BudiStore" From 4353e56200aaafc9bd8c1396faef4256145d1f1a Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Jan 2025 14:00:24 +0000 Subject: [PATCH 08/14] Bump version to 3.2.30 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 5e0bc09825..000f9eda16 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.29", + "version": "3.2.30", "npmClient": "yarn", "concurrency": 20, "command": { From 46a132a220fe585214a1ca1ecdf8dfdc82ea337e Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Jan 2025 14:31:42 +0000 Subject: [PATCH 09/14] Bump version to 3.2.31 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 000f9eda16..155f4f5ba3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.30", + "version": "3.2.31", "npmClient": "yarn", "concurrency": 20, "command": { From aba5fbfd89b9b6b6ebc3c5274add460470a9a5cd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Jan 2025 16:04:17 +0100 Subject: [PATCH 10/14] Fix user cell search --- .../src/components/grid/cells/BBReferenceCell.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/cells/BBReferenceCell.svelte b/packages/frontend-core/src/components/grid/cells/BBReferenceCell.svelte index 5d98ba903b..fe6d8945ba 100644 --- a/packages/frontend-core/src/components/grid/cells/BBReferenceCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/BBReferenceCell.svelte @@ -26,7 +26,7 @@ : RelationshipType.MANY_TO_MANY, } - async function searchFunction(searchParams) { + async function searchFunction(_tableId, searchParams) { if ( subtype !== BBReferenceFieldSubType.USER && subtype !== BBReferenceFieldSubType.USERS From 94adbb15adc43dc2fa8b71ad5ca18f326a598e96 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Jan 2025 15:17:22 +0000 Subject: [PATCH 11/14] Bump version to 3.2.32 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 155f4f5ba3..dde9cf03a0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.31", + "version": "3.2.32", "npmClient": "yarn", "concurrency": 20, "command": { From b4b805ac1c94cfc5594f1e50a695a5441acb55f3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 3 Jan 2025 11:02:09 +0100 Subject: [PATCH 12/14] Fix queries when returning nulls --- packages/server/src/threads/query.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index facdd20642..3ba4995b2c 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -174,7 +174,9 @@ class QueryRunner { } // needs to an array for next step - if (!Array.isArray(rows)) { + if (rows === null) { + rows = [] + } else if (!Array.isArray(rows)) { rows = [rows] } From 9c7beeeeaf846dfdb71f942ed686d1bbe0e199ea Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 3 Jan 2025 10:42:59 +0000 Subject: [PATCH 13/14] user and websocket stores converted to typescript --- packages/builder/src/stores/builder/users.js | 62 --------- packages/builder/src/stores/builder/users.ts | 72 ++++++++++ .../builder/src/stores/builder/websocket.js | 95 -------------- .../builder/src/stores/builder/websocket.ts | 124 ++++++++++++++++++ .../src/utils/{websocket.js => websocket.ts} | 16 ++- packages/types/src/ui/stores/grid/user.ts | 1 + 6 files changed, 208 insertions(+), 162 deletions(-) delete mode 100644 packages/builder/src/stores/builder/users.js create mode 100644 packages/builder/src/stores/builder/users.ts delete mode 100644 packages/builder/src/stores/builder/websocket.js create mode 100644 packages/builder/src/stores/builder/websocket.ts rename packages/frontend-core/src/utils/{websocket.js => websocket.ts} (78%) diff --git a/packages/builder/src/stores/builder/users.js b/packages/builder/src/stores/builder/users.js deleted file mode 100644 index 0794c63289..0000000000 --- a/packages/builder/src/stores/builder/users.js +++ /dev/null @@ -1,62 +0,0 @@ -import { writable, get, derived } from "svelte/store" - -export const createUserStore = () => { - const store = writable([]) - - const init = users => { - store.set(users) - } - - const updateUser = user => { - const $users = get(store) - if (!$users.some(x => x.sessionId === user.sessionId)) { - store.set([...$users, user]) - } else { - store.update(state => { - const index = state.findIndex(x => x.sessionId === user.sessionId) - state[index] = user - return state.slice() - }) - } - } - - const removeUser = sessionId => { - store.update(state => { - return state.filter(x => x.sessionId !== sessionId) - }) - } - - const reset = () => { - store.set([]) - } - - return { - ...store, - actions: { - init, - updateUser, - removeUser, - reset, - }, - } -} - -export const userStore = createUserStore() - -export const userSelectedResourceMap = derived(userStore, $userStore => { - let map = {} - $userStore.forEach(user => { - const resource = user.builderMetadata?.selectedResourceId - if (resource) { - if (!map[resource]) { - map[resource] = [] - } - map[resource].push(user) - } - }) - return map -}) - -export const isOnlyUser = derived(userStore, $userStore => { - return $userStore.length < 2 -}) diff --git a/packages/builder/src/stores/builder/users.ts b/packages/builder/src/stores/builder/users.ts new file mode 100644 index 0000000000..5f0c4a3701 --- /dev/null +++ b/packages/builder/src/stores/builder/users.ts @@ -0,0 +1,72 @@ +import { get, derived } from "svelte/store" +import { BudiStore } from "../BudiStore" +import { UIUser } from "@budibase/types" + +export class UserStore extends BudiStore { + actions: { + init: (users: UIUser[]) => void + updateUser: (user: UIUser) => void + removeUser: (sessionId: string) => void + reset: () => void + } + + constructor() { + super([]) + this.actions = { + init: this.init.bind(this), + updateUser: this.updateUser.bind(this), + removeUser: this.removeUser.bind(this), + reset: this.reset.bind(this), + } + } + + init(users: UIUser[]) { + this.store.set(users) + } + + updateUser(user: UIUser) { + const $users = get(this.store) + if (!$users.some(x => x.sessionId === user.sessionId)) { + this.store.set([...$users, user]) + } else { + this.update(state => { + const index = state.findIndex(x => x.sessionId === user.sessionId) + state[index] = user + return state.slice() + }) + } + } + + removeUser(sessionId: string) { + this.update(state => { + return state.filter(x => x.sessionId !== sessionId) + }) + } + + reset() { + this.store.set([]) + } +} + +export const userStore = new UserStore() + +export const userSelectedResourceMap = derived( + userStore, + ($userStore): Record => { + let map: Record = {} + $userStore.forEach(user => { + const resource = user.builderMetadata?.selectedResourceId + if (resource) { + if (!map[resource]) { + map[resource] = [] + } + map[resource].push(user) + } + }) + return map + } +) + +export const isOnlyUser = derived(userStore, $userStore => { + return $userStore.length < 2 +}) diff --git a/packages/builder/src/stores/builder/websocket.js b/packages/builder/src/stores/builder/websocket.js deleted file mode 100644 index 3944c8cba5..0000000000 --- a/packages/builder/src/stores/builder/websocket.js +++ /dev/null @@ -1,95 +0,0 @@ -import { createWebsocket } from "@budibase/frontend-core" -import { - automationStore, - userStore, - appStore, - themeStore, - navigationStore, - deploymentStore, - snippets, - datasources, - tables, - roles, -} from "@/stores/builder" -import { get } from "svelte/store" -import { auth, appsStore } from "@/stores/portal" -import { screenStore } from "./screens" -import { SocketEvent, BuilderSocketEvent, helpers } from "@budibase/shared-core" -import { notifications } from "@budibase/bbui" - -export const createBuilderWebsocket = appId => { - const socket = createWebsocket("/socket/builder") - - // Built-in events - socket.on("connect", () => { - socket.emit(BuilderSocketEvent.SelectApp, { appId }, ({ users }) => { - userStore.actions.init(users) - }) - }) - socket.on("connect_error", err => { - console.error("Failed to connect to builder websocket:", err.message) - }) - socket.on("disconnect", () => { - userStore.actions.reset() - }) - - // User events - socket.onOther(SocketEvent.UserUpdate, ({ user }) => { - userStore.actions.updateUser(user) - }) - socket.onOther(SocketEvent.UserDisconnect, ({ sessionId }) => { - userStore.actions.removeUser(sessionId) - }) - socket.onOther(BuilderSocketEvent.LockTransfer, ({ userId }) => { - if (userId === get(auth)?.user?._id) { - appStore.update(state => ({ - ...state, - hasLock: true, - })) - } - }) - - // Data section events - socket.onOther(BuilderSocketEvent.TableChange, ({ id, table }) => { - tables.replaceTable(id, table) - }) - socket.onOther(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => { - datasources.replaceDatasource(id, datasource) - }) - - // Role events - socket.onOther(BuilderSocketEvent.RoleChange, ({ id, role }) => { - roles.replace(id, role) - }) - - // Design section events - socket.onOther(BuilderSocketEvent.ScreenChange, ({ id, screen }) => { - screenStore.replace(id, screen) - }) - - // App events - socket.onOther(BuilderSocketEvent.AppMetadataChange, ({ metadata }) => { - appStore.syncMetadata(metadata) - themeStore.syncMetadata(metadata) - navigationStore.syncMetadata(metadata) - snippets.syncMetadata(metadata) - }) - socket.onOther( - BuilderSocketEvent.AppPublishChange, - async ({ user, published }) => { - await appsStore.load() - if (published) { - await deploymentStore.load() - } - const verb = published ? "published" : "unpublished" - notifications.success(`${helpers.getUserLabel(user)} ${verb} this app`) - } - ) - - // Automation events - socket.onOther(BuilderSocketEvent.AutomationChange, ({ id, automation }) => { - automationStore.actions.replace(id, automation) - }) - - return socket -} diff --git a/packages/builder/src/stores/builder/websocket.ts b/packages/builder/src/stores/builder/websocket.ts new file mode 100644 index 0000000000..49b8067dd8 --- /dev/null +++ b/packages/builder/src/stores/builder/websocket.ts @@ -0,0 +1,124 @@ +import { createWebsocket } from "@budibase/frontend-core" +import { + automationStore, + userStore, + appStore, + themeStore, + navigationStore, + deploymentStore, + snippets, + datasources, + tables, + roles, +} from "@/stores/builder" +import { get } from "svelte/store" +import { auth, appsStore } from "@/stores/portal" +import { screenStore } from "./screens" +import { SocketEvent, BuilderSocketEvent, helpers } from "@budibase/shared-core" +import { notifications } from "@budibase/bbui" +import { Automation, Datasource, Role, Table, UIUser } from "@budibase/types" + +export const createBuilderWebsocket = (appId: string) => { + const socket = createWebsocket("/socket/builder") + + // Built-in events + socket.on("connect", () => { + socket.emit( + BuilderSocketEvent.SelectApp, + { appId }, + ({ users }: { users: UIUser[] }) => { + userStore.actions.init(users) + } + ) + }) + socket.on("connect_error", err => { + console.error("Failed to connect to builder websocket:", err.message) + }) + socket.on("disconnect", () => { + userStore.actions.reset() + }) + + // User events + socket.onOther(SocketEvent.UserUpdate, ({ user }: { user: UIUser }) => { + userStore.actions.updateUser(user) + }) + socket.onOther( + SocketEvent.UserDisconnect, + ({ sessionId }: { sessionId: string }) => { + userStore.actions.removeUser(sessionId) + } + ) + socket.onOther( + BuilderSocketEvent.LockTransfer, + ({ userId }: { userId: string }) => { + if (userId === get(auth)?.user?._id) { + appStore.update(state => ({ + ...state, + hasLock: true, + })) + } + } + ) + + // Data section events + socket.onOther( + BuilderSocketEvent.TableChange, + ({ id, table }: { id: string; table: Table }) => { + tables.replaceTable(id, table) + } + ) + socket.onOther( + BuilderSocketEvent.DatasourceChange, + ({ id, datasource }: { id: string; datasource: Datasource }) => { + datasources.replaceDatasource(id, datasource) + } + ) + + // Role events + socket.onOther( + BuilderSocketEvent.RoleChange, + ({ id, role }: { id: string; role: Role }) => { + roles.replace(id, role) + } + ) + + // Design section events + socket.onOther( + BuilderSocketEvent.ScreenChange, + ({ id, screen }: { id: string; screen: Screen }) => { + screenStore.replace(id, screen) + } + ) + + // App events + socket.onOther( + BuilderSocketEvent.AppMetadataChange, + ({ metadata }: { metadata: any }) => { + appStore.syncMetadata(metadata) + themeStore.syncMetadata(metadata) + navigationStore.syncMetadata(metadata) + snippets.syncMetadata(metadata) + } + ) + socket.onOther( + BuilderSocketEvent.AppPublishChange, + async ({ user, published }: { user: UIUser; published: boolean }) => { + await appsStore.load() + if (published) { + await deploymentStore.load() + } + const verb = published ? "published" : "unpublished" + notifications.success(`${helpers.getUserLabel(user)} ${verb} this app`) + } + ) + + // Automation events + socket.onOther( + BuilderSocketEvent.AutomationChange, + ({ id, automation }: { id: string; automation: Automation }) => { + automationStore.actions.replace(id, automation) + } + ) + + return socket +} diff --git a/packages/frontend-core/src/utils/websocket.js b/packages/frontend-core/src/utils/websocket.ts similarity index 78% rename from packages/frontend-core/src/utils/websocket.js rename to packages/frontend-core/src/utils/websocket.ts index dee679eaef..475b14176f 100644 --- a/packages/frontend-core/src/utils/websocket.js +++ b/packages/frontend-core/src/utils/websocket.ts @@ -1,12 +1,18 @@ -import { io } from "socket.io-client" +import { io, Socket } from "socket.io-client" import { SocketEvent, SocketSessionTTL } from "@budibase/shared-core" import { APISessionID } from "../api" const DefaultOptions = { heartbeat: true, } +export interface ExtendedSocket extends Socket { + onOther: (event: string, callback: (data: any) => void) => void +} -export const createWebsocket = (path, options = DefaultOptions) => { +export const createWebsocket = ( + path: string, + options = DefaultOptions +): ExtendedSocket => { if (!path) { throw "A websocket path must be provided" } @@ -32,10 +38,10 @@ export const createWebsocket = (path, options = DefaultOptions) => { // Disable polling and rely on websocket only, as HTTP transport // will only work with sticky sessions which we don't have transports: ["websocket"], - }) + }) as ExtendedSocket // Set up a heartbeat that's half of the session TTL - let interval + let interval: NodeJS.Timeout | undefined if (heartbeat) { interval = setInterval(() => { socket.emit(SocketEvent.Heartbeat) @@ -48,7 +54,7 @@ export const createWebsocket = (path, options = DefaultOptions) => { // Helper utility to ignore events that were triggered due to API // changes made by us - socket.onOther = (event, callback) => { + socket.onOther = (event: string, callback: (data: any) => void) => { socket.on(event, data => { if (data?.apiSessionId !== APISessionID) { callback(data) diff --git a/packages/types/src/ui/stores/grid/user.ts b/packages/types/src/ui/stores/grid/user.ts index b6eb529805..9625b0bb80 100644 --- a/packages/types/src/ui/stores/grid/user.ts +++ b/packages/types/src/ui/stores/grid/user.ts @@ -3,4 +3,5 @@ import { User } from "@budibase/types" export interface UIUser extends User { sessionId: string gridMetadata?: { focusedCellId?: string } + builderMetadata?: { selectedResourceId?: string } } From 8a4f42003c8702841ebc15a28884ec39b77abd04 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 3 Jan 2025 11:31:48 +0000 Subject: [PATCH 14/14] flatten actions --- packages/builder/src/stores/builder/users.ts | 21 ++++--------------- .../builder/src/stores/builder/websocket.ts | 8 +++---- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/packages/builder/src/stores/builder/users.ts b/packages/builder/src/stores/builder/users.ts index 5f0c4a3701..5350b2af90 100644 --- a/packages/builder/src/stores/builder/users.ts +++ b/packages/builder/src/stores/builder/users.ts @@ -3,31 +3,18 @@ import { BudiStore } from "../BudiStore" import { UIUser } from "@budibase/types" export class UserStore extends BudiStore { - actions: { - init: (users: UIUser[]) => void - updateUser: (user: UIUser) => void - removeUser: (sessionId: string) => void - reset: () => void - } - constructor() { super([]) - this.actions = { - init: this.init.bind(this), - updateUser: this.updateUser.bind(this), - removeUser: this.removeUser.bind(this), - reset: this.reset.bind(this), - } } init(users: UIUser[]) { - this.store.set(users) + this.set(users) } updateUser(user: UIUser) { - const $users = get(this.store) + const $users = get(this) if (!$users.some(x => x.sessionId === user.sessionId)) { - this.store.set([...$users, user]) + this.set([...$users, user]) } else { this.update(state => { const index = state.findIndex(x => x.sessionId === user.sessionId) @@ -44,7 +31,7 @@ export class UserStore extends BudiStore { } reset() { - this.store.set([]) + this.set([]) } } diff --git a/packages/builder/src/stores/builder/websocket.ts b/packages/builder/src/stores/builder/websocket.ts index 49b8067dd8..bd9e2c8d4d 100644 --- a/packages/builder/src/stores/builder/websocket.ts +++ b/packages/builder/src/stores/builder/websocket.ts @@ -27,7 +27,7 @@ export const createBuilderWebsocket = (appId: string) => { BuilderSocketEvent.SelectApp, { appId }, ({ users }: { users: UIUser[] }) => { - userStore.actions.init(users) + userStore.init(users) } ) }) @@ -35,17 +35,17 @@ export const createBuilderWebsocket = (appId: string) => { console.error("Failed to connect to builder websocket:", err.message) }) socket.on("disconnect", () => { - userStore.actions.reset() + userStore.reset() }) // User events socket.onOther(SocketEvent.UserUpdate, ({ user }: { user: UIUser }) => { - userStore.actions.updateUser(user) + userStore.updateUser(user) }) socket.onOther( SocketEvent.UserDisconnect, ({ sessionId }: { sessionId: string }) => { - userStore.actions.removeUser(sessionId) + userStore.removeUser(sessionId) } ) socket.onOther(