From 54aad2ff1ef55c993ba316b1d948fb9febfdc5ae Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Oct 2021 11:05:48 +0100 Subject: [PATCH 1/4] Update field disabled state when disabled prop changes --- .../src/components/app/forms/Field.svelte | 20 ++++++++++++------- .../src/components/app/forms/InnerForm.svelte | 15 ++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/app/forms/Field.svelte b/packages/client/src/components/app/forms/Field.svelte index ca607e7315..823e520764 100644 --- a/packages/client/src/components/app/forms/Field.svelte +++ b/packages/client/src/components/app/forms/Field.svelte @@ -22,7 +22,7 @@ // Register field with form const formApi = formContext?.formApi - const labelPosition = fieldGroupContext?.labelPosition || "above" + const labelPos = fieldGroupContext?.labelPosition || "above" const formField = formApi?.registerField( field, type, @@ -38,17 +38,23 @@ fieldApi = value?.fieldApi fieldSchema = value?.fieldSchema }) - onDestroy(() => unsubscribe && unsubscribe()) + onDestroy(() => unsubscribe?.()) - // Keep validation rules up to date + // Keep field state up to date with props which might change due to + // conditional UI $: updateValidation(validation) + $: updateDisabled(disabled) + + // Determine label class from position + $: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}` + const updateValidation = validation => { fieldApi?.updateValidation(validation) } - // Extract label position from field group context - $: labelPositionClass = - labelPosition === "above" ? "" : `spectrum-FieldLabel--${labelPosition}` + const updateDisabled = disabled => { + fieldApi?.setDisabled(disabled) + } @@ -56,7 +62,7 @@ diff --git a/packages/client/src/components/app/forms/InnerForm.svelte b/packages/client/src/components/app/forms/InnerForm.svelte index 3cae94c3d0..fbf3d2db9f 100644 --- a/packages/client/src/components/app/forms/InnerForm.svelte +++ b/packages/client/src/components/app/forms/InnerForm.svelte @@ -248,10 +248,25 @@ } } + // Updates the disabled state of a certain field + const setDisabled = fieldDisabled => { + const fieldInfo = getField(field) + + // Auto columns are always disabled + const isAutoColumn = !!schema?.[field]?.autocolumn + + // Update disabled state + fieldInfo.update(state => { + state.fieldState.disabled = disabled || fieldDisabled || isAutoColumn + return state + }) + } + return { setValue, clearValue, updateValidation, + setDisabled, validate: () => { // Validate the field by force setting the same value again const { fieldState } = get(getField(field)) From 262b2383888b9967726123c940a9cf1f0808fe40 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 26 Oct 2021 19:12:55 +0100 Subject: [PATCH 2/4] Invalidate related tables automatically via client library and fix issue with data source invalidation from modals --- packages/client/src/api/queries.js | 2 +- packages/client/src/api/rows.js | 8 +-- packages/client/src/stores/dataSource.js | 73 ++++++++++++++---------- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/packages/client/src/api/queries.js b/packages/client/src/api/queries.js index 8db41ecf5c..8735d9587b 100644 --- a/packages/client/src/api/queries.js +++ b/packages/client/src/api/queries.js @@ -20,7 +20,7 @@ export const executeQuery = async ({ queryId, parameters }) => { notificationStore.actions.error("An error has occurred") } else if (!query.readable) { notificationStore.actions.success("Query executed successfully") - dataSourceStore.actions.invalidateDataSource(query.datasourceId) + await dataSourceStore.actions.invalidateDataSource(query.datasourceId) } return res } diff --git a/packages/client/src/api/rows.js b/packages/client/src/api/rows.js index 6e1ac31279..acd083454d 100644 --- a/packages/client/src/api/rows.js +++ b/packages/client/src/api/rows.js @@ -31,7 +31,7 @@ export const saveRow = async row => { : notificationStore.actions.success("Row saved") // Refresh related datasources - dataSourceStore.actions.invalidateDataSource(row.tableId) + await dataSourceStore.actions.invalidateDataSource(row.tableId) return res } @@ -52,7 +52,7 @@ export const updateRow = async row => { : notificationStore.actions.success("Row updated") // Refresh related datasources - dataSourceStore.actions.invalidateDataSource(row.tableId) + await dataSourceStore.actions.invalidateDataSource(row.tableId) return res } @@ -76,7 +76,7 @@ export const deleteRow = async ({ tableId, rowId, revId }) => { : notificationStore.actions.success("Row deleted") // Refresh related datasources - dataSourceStore.actions.invalidateDataSource(tableId) + await dataSourceStore.actions.invalidateDataSource(tableId) return res } @@ -99,7 +99,7 @@ export const deleteRows = async ({ tableId, rows }) => { : notificationStore.actions.success(`${rows.length} row(s) deleted`) // Refresh related datasources - dataSourceStore.actions.invalidateDataSource(tableId) + await dataSourceStore.actions.invalidateDataSource(tableId) return res } diff --git a/packages/client/src/stores/dataSource.js b/packages/client/src/stores/dataSource.js index 963548158d..256571018d 100644 --- a/packages/client/src/stores/dataSource.js +++ b/packages/client/src/stores/dataSource.js @@ -1,4 +1,5 @@ import { writable, get } from "svelte/store" +import { fetchTableDefinition } from "../api" export const createDataSourceStore = () => { const store = writable([]) @@ -9,43 +10,32 @@ export const createDataSourceStore = () => { return } - // Create a list of all relevant dataSource IDs which would require that - // this dataSource is refreshed - let dataSourceIds = [] + // Extract the relevant datasource ID for this datasource + let dataSourceId = null // Extract table ID if (dataSource.type === "table" || dataSource.type === "view") { - if (dataSource.tableId) { - dataSourceIds.push(dataSource.tableId) - } + dataSourceId = dataSource.tableId } - // Extract both table IDs from both sides of the relationship + // Only one side of the relationship is required as a trigger, as it will + // automatically invalidate related table IDs else if (dataSource.type === "link") { - if (dataSource.rowTableId) { - dataSourceIds.push(dataSource.rowTableId) - } - if (dataSource.tableId) { - dataSourceIds.push(dataSource.tableId) - } + dataSourceId = dataSource.tableId || dataSource.rowTableId } // Extract the dataSource ID (not the query ID) for queries else if (dataSource.type === "query") { - if (dataSource.dataSourceId) { - dataSourceIds.push(dataSource.dataSourceId) - } + dataSourceId = dataSource.dataSourceId } // Store configs for each relevant dataSource ID - if (dataSourceIds.length) { + if (dataSourceId) { store.update(state => { - dataSourceIds.forEach(id => { - state.push({ - dataSourceId: id, - instanceId, - refresh, - }) + state.push({ + dataSourceId, + instanceId, + refresh, }) return state }) @@ -62,13 +52,10 @@ export const createDataSourceStore = () => { // Invalidates a specific dataSource ID by refreshing all instances // which depend on data from that dataSource - const invalidateDataSource = dataSourceId => { - const relatedInstances = get(store).filter(instance => { - return instance.dataSourceId === dataSourceId - }) - relatedInstances?.forEach(instance => { - instance.refresh() - }) + const invalidateDataSource = async dataSourceId => { + if (!dataSourceId) { + return + } // Emit this as a window event, so parent screens which are iframing us in // can also invalidate the same datasource @@ -77,6 +64,32 @@ export const createDataSourceStore = () => { detail: { dataSourceId }, }) ) + + let invalidations = [dataSourceId] + + // Fetch related table IDs from table schema + const definition = await fetchTableDefinition(dataSourceId) + const schema = definition?.schema + if (schema) { + Object.values(schema).forEach(fieldSchema => { + if (fieldSchema.type === "link" && fieldSchema.tableId) { + invalidations.push(fieldSchema.tableId) + } + }) + } + + // Remove and dupes + invalidations = [...new Set(invalidations)] + + // Invalidate all sources + invalidations.forEach(id => { + const relatedInstances = get(store).filter(instance => { + return instance.dataSourceId === id + }) + relatedInstances?.forEach(instance => { + instance.refresh() + }) + }) } return { From 2af3888d8abc3d59d12bad1735b587a0eccd1b2c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Oct 2021 08:37:51 +0100 Subject: [PATCH 3/4] Don't invalidate users table due to autocolumn fields in schema when hot reloading data in the client library --- packages/client/src/stores/dataSource.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/client/src/stores/dataSource.js b/packages/client/src/stores/dataSource.js index 256571018d..8fb9614a88 100644 --- a/packages/client/src/stores/dataSource.js +++ b/packages/client/src/stores/dataSource.js @@ -1,5 +1,6 @@ import { writable, get } from "svelte/store" import { fetchTableDefinition } from "../api" +import { TableNames } from "../constants" export const createDataSourceStore = () => { const store = writable([]) @@ -72,13 +73,17 @@ export const createDataSourceStore = () => { const schema = definition?.schema if (schema) { Object.values(schema).forEach(fieldSchema => { - if (fieldSchema.type === "link" && fieldSchema.tableId) { + if ( + fieldSchema.type === "link" && + fieldSchema.tableId && + !fieldSchema.autocolumn + ) { invalidations.push(fieldSchema.tableId) } }) } - // Remove and dupes + // Remove any dupes invalidations = [...new Set(invalidations)] // Invalidate all sources From 25bafc15d2556c6b909cf6d8d3676bc785ce6b5c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 27 Oct 2021 14:24:36 +0100 Subject: [PATCH 4/4] Use explicity onDestroy calls rather than an onMount return value to ensure callbacks are actually executed to fix data provider cleanup issues --- .../context/DeviceBindingsProvider.svelte | 11 +++++----- .../src/components/context/Provider.svelte | 6 ++--- .../src/components/preview/DNDHandler.svelte | 22 +++++++++---------- packages/client/src/stores/dataSource.js | 1 - 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/client/src/components/context/DeviceBindingsProvider.svelte b/packages/client/src/components/context/DeviceBindingsProvider.svelte index a6dc64fe74..cdc19c74c2 100644 --- a/packages/client/src/components/context/DeviceBindingsProvider.svelte +++ b/packages/client/src/components/context/DeviceBindingsProvider.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/context/Provider.svelte b/packages/client/src/components/context/Provider.svelte index 77efb707d8..19a5c3a462 100644 --- a/packages/client/src/components/context/Provider.svelte +++ b/packages/client/src/components/context/Provider.svelte @@ -1,5 +1,5 @@ diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 8a824f6131..474df4a674 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -8,7 +8,7 @@ diff --git a/packages/client/src/stores/dataSource.js b/packages/client/src/stores/dataSource.js index 8fb9614a88..c60039aa8a 100644 --- a/packages/client/src/stores/dataSource.js +++ b/packages/client/src/stores/dataSource.js @@ -1,6 +1,5 @@ import { writable, get } from "svelte/store" import { fetchTableDefinition } from "../api" -import { TableNames } from "../constants" export const createDataSourceStore = () => { const store = writable([])