diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte
index 2035ec4d39..3b0e74d882 100644
--- a/packages/frontend-core/src/components/grid/layout/Grid.svelte
+++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte
@@ -1,5 +1,5 @@
{
})
socket.on("row-update", data => {
if (data.id) {
- rows.actions.refreshRow(data.id)
+ rows.actions.replaceRow(data.id, data.row)
}
})
socket.on("user-update", user => {
diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js
index b6dc8c05d0..ee5ac6c1d2 100644
--- a/packages/frontend-core/src/components/grid/stores/rows.js
+++ b/packages/frontend-core/src/components/grid/stores/rows.js
@@ -268,27 +268,25 @@ export const deriveStores = context => {
return res?.rows?.[0]
}
- // Refreshes a specific row, handling updates, addition or deletion
- const refreshRow = async id => {
- // Fetch row from the server again
- const newRow = await fetchRow(id)
-
+ // Replaces a row in state with the newly defined row, handling updates,
+ // addition and deletion
+ const replaceRow = (id, row) => {
// Get index of row to check if it exists
const $rows = get(rows)
const $rowLookupMap = get(rowLookupMap)
const index = $rowLookupMap[id]
// Process as either an update, addition or deletion
- if (newRow) {
+ if (row) {
if (index != null) {
// An existing row was updated
rows.update(state => {
- state[index] = { ...newRow }
+ state[index] = { ...row }
return state
})
} else {
// A new row was created
- handleNewRows([newRow])
+ handleNewRows([row])
}
} else if (index != null) {
// A row was removed
@@ -296,6 +294,15 @@ export const deriveStores = context => {
}
}
+ // Refreshes a specific row
+ const refreshRow = async id => {
+ // Fetch row from the server again
+ const row = await fetchRow(id)
+
+ // Update local state
+ replaceRow(id, row)
+ }
+
// Refreshes all data
const refreshData = () => {
get(fetch)?.getInitialData()
@@ -455,6 +462,7 @@ export const deriveStores = context => {
hasRow,
loadNextPage,
refreshRow,
+ replaceRow,
refreshData,
refreshTableDefinition,
},
diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts
index 349c4e72e7..55d2d27cce 100644
--- a/packages/server/src/api/controllers/row/index.ts
+++ b/packages/server/src/api/controllers/row/index.ts
@@ -4,6 +4,7 @@ import * as external from "./external"
import { isExternalTable } from "../../../integrations/utils"
import { Ctx } from "@budibase/types"
import * as utils from "./utils"
+import { gridSocket } from "../../../websockets"
function pickApi(tableId: any) {
if (isExternalTable(tableId)) {
@@ -12,21 +13,9 @@ function pickApi(tableId: any) {
return internal
}
-function getTableId(ctx: any) {
- if (ctx.request.body && ctx.request.body.tableId) {
- return ctx.request.body.tableId
- }
- if (ctx.params && ctx.params.tableId) {
- return ctx.params.tableId
- }
- if (ctx.params && ctx.params.viewName) {
- return ctx.params.viewName
- }
-}
-
export async function patch(ctx: any): Promise
{
const appId = ctx.appId
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
const body = ctx.request.body
// if it doesn't have an _id then its save
if (body && !body._id) {
@@ -47,6 +36,7 @@ export async function patch(ctx: any): Promise {
ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
ctx.message = `${table.name} updated successfully.`
ctx.body = row
+ gridSocket?.emitRowUpdate(ctx, row)
} catch (err) {
ctx.throw(400, err)
}
@@ -54,7 +44,7 @@ export async function patch(ctx: any): Promise {
export const save = async (ctx: any) => {
const appId = ctx.appId
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
const body = ctx.request.body
// if it has an ID already then its a patch
if (body && body._id) {
@@ -69,23 +59,24 @@ export const save = async (ctx: any) => {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
ctx.message = `${table.name} saved successfully`
ctx.body = row
+ gridSocket?.emitRowUpdate(ctx, row)
}
export async function fetchView(ctx: any) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetchView(ctx), {
datasourceId: tableId,
})
}
export async function fetch(ctx: any) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetch(ctx), {
datasourceId: tableId,
})
}
export async function find(ctx: any) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).find(ctx), {
datasourceId: tableId,
})
@@ -94,7 +85,7 @@ export async function find(ctx: any) {
export async function destroy(ctx: any) {
const appId = ctx.appId
const inputs = ctx.request.body
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
let response, row
if (inputs.rows) {
let { rows } = await quotas.addQuery(
@@ -107,6 +98,7 @@ export async function destroy(ctx: any) {
response = rows
for (let row of rows) {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
+ gridSocket?.emitRowDeletion(ctx, row._id)
}
} else {
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), {
@@ -116,6 +108,7 @@ export async function destroy(ctx: any) {
response = resp.response
row = resp.row
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
+ gridSocket?.emitRowDeletion(ctx, row._id)
}
ctx.status = 200
// for automations include the row that was deleted
@@ -124,7 +117,7 @@ export async function destroy(ctx: any) {
}
export async function search(ctx: any) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.status = 200
ctx.body = await quotas.addQuery(() => pickApi(tableId).search(ctx), {
datasourceId: tableId,
@@ -132,7 +125,7 @@ export async function search(ctx: any) {
}
export async function validate(ctx: Ctx) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
// external tables are hard to validate currently
if (isExternalTable(tableId)) {
ctx.body = { valid: true }
@@ -145,7 +138,7 @@ export async function validate(ctx: Ctx) {
}
export async function fetchEnrichedRow(ctx: any) {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(
() => pickApi(tableId).fetchEnrichedRow(ctx),
{
@@ -155,7 +148,7 @@ export async function fetchEnrichedRow(ctx: any) {
}
export const exportRows = async (ctx: any) => {
- const tableId = getTableId(ctx)
+ const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).exportRows(ctx), {
datasourceId: tableId,
})
diff --git a/packages/server/src/api/controllers/row/utils.ts b/packages/server/src/api/controllers/row/utils.ts
index f6a87dd24c..f1edbf538b 100644
--- a/packages/server/src/api/controllers/row/utils.ts
+++ b/packages/server/src/api/controllers/row/utils.ts
@@ -154,3 +154,15 @@ export function cleanExportRows(
return cleanRows
}
+
+export function getTableId(ctx: any) {
+ if (ctx.request.body && ctx.request.body.tableId) {
+ return ctx.request.body.tableId
+ }
+ if (ctx.params && ctx.params.tableId) {
+ return ctx.params.tableId
+ }
+ if (ctx.params && ctx.params.viewName) {
+ return ctx.params.viewName
+ }
+}
diff --git a/packages/server/src/websockets/grid.ts b/packages/server/src/websockets/grid.ts
index 886784cd2c..bb23ec8e18 100644
--- a/packages/server/src/websockets/grid.ts
+++ b/packages/server/src/websockets/grid.ts
@@ -3,6 +3,8 @@ import Socket from "./websocket"
import { permissions } from "@budibase/backend-core"
import http from "http"
import Koa from "koa"
+import { getTableId } from "../api/controllers/row/utils"
+import { Row } from "@budibase/types"
export default class GridSocket extends Socket {
constructor(app: Koa, server: http.Server) {
@@ -52,4 +54,14 @@ export default class GridSocket extends Socket {
})
})
}
+
+ emitRowUpdate(ctx: any, row: Row) {
+ const tableId = getTableId(ctx)
+ this.io.in(tableId).emit("row-update", { id: row._id, row })
+ }
+
+ emitRowDeletion(ctx: any, id: string) {
+ const tableId = getTableId(ctx)
+ this.io.in(tableId).emit("row-update", { id, row: null })
+ }
}