Merge branch 'master' into BUDI-9020/fix-isolatedvm-query-issues
This commit is contained in:
commit
3419fc69a1
|
@ -2,7 +2,7 @@ import { derived, get } from "svelte/store"
|
|||
import { API } from "@/api"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import { generate } from "shortid"
|
||||
import { createHistoryStore } from "@/stores/builder/history"
|
||||
import { createHistoryStore, HistoryStore } from "@/stores/builder/history"
|
||||
import { licensing } from "@/stores/portal"
|
||||
import { tables, appStore } from "@/stores/builder"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
|
@ -1428,7 +1428,7 @@ const automationActions = (store: AutomationStore) => ({
|
|||
})
|
||||
|
||||
class AutomationStore extends BudiStore<AutomationState> {
|
||||
history: any
|
||||
history: HistoryStore<Automation>
|
||||
actions: ReturnType<typeof automationActions>
|
||||
|
||||
constructor() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { get } from "svelte/store"
|
||||
import { selectedScreen as selectedScreenStore } from "./screens"
|
||||
import { findComponentPath } from "@/helpers/components"
|
||||
import { Screen, Component } from "@budibase/types"
|
||||
import { Component, Screen } from "@budibase/types"
|
||||
import { BudiStore, PersistenceType } from "@/stores/BudiStore"
|
||||
|
||||
interface OpenNodesState {
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
import * as jsonpatch from "fast-json-patch/index.mjs"
|
||||
import { writable, derived, get } from "svelte/store"
|
||||
import { Document } from "@budibase/types"
|
||||
import * as jsonpatch from "fast-json-patch"
|
||||
import { writable, derived, get, Readable } from "svelte/store"
|
||||
|
||||
export const Operations = {
|
||||
Add: "Add",
|
||||
Delete: "Delete",
|
||||
Change: "Change",
|
||||
export const enum Operations {
|
||||
Add = "Add",
|
||||
Delete = "Delete",
|
||||
Change = "Change",
|
||||
}
|
||||
|
||||
interface Operator<T extends Document> {
|
||||
id?: number
|
||||
type: Operations
|
||||
doc: T
|
||||
forwardPatch?: jsonpatch.Operation[]
|
||||
backwardsPatch?: jsonpatch.Operation[]
|
||||
}
|
||||
|
||||
interface HistoryState<T extends Document> {
|
||||
history: Operator<T>[]
|
||||
position: number
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
export const initialState = {
|
||||
|
@ -13,14 +28,38 @@ export const initialState = {
|
|||
loading: false,
|
||||
}
|
||||
|
||||
export const createHistoryStore = ({
|
||||
export interface HistoryStore<T extends Document>
|
||||
extends Readable<
|
||||
HistoryState<T> & {
|
||||
canUndo: boolean
|
||||
canRedo: boolean
|
||||
}
|
||||
> {
|
||||
wrapSaveDoc: (
|
||||
fn: (doc: T) => Promise<T>
|
||||
) => (doc: T, operationId?: number) => Promise<T>
|
||||
wrapDeleteDoc: (
|
||||
fn: (doc: T) => Promise<void>
|
||||
) => (doc: T, operationId?: number) => Promise<void>
|
||||
|
||||
reset: () => void
|
||||
undo: () => Promise<void>
|
||||
redo: () => Promise<void>
|
||||
}
|
||||
|
||||
export const createHistoryStore = <T extends Document>({
|
||||
getDoc,
|
||||
selectDoc,
|
||||
beforeAction = () => {},
|
||||
afterAction = () => {},
|
||||
}) => {
|
||||
beforeAction,
|
||||
afterAction,
|
||||
}: {
|
||||
getDoc: (id: string) => T | undefined
|
||||
selectDoc: (id: string) => void
|
||||
beforeAction?: (operation?: Operator<T>) => void
|
||||
afterAction?: (operation?: Operator<T>) => void
|
||||
}): HistoryStore<T> => {
|
||||
// Use a derived store to check if we are able to undo or redo any operations
|
||||
const store = writable(initialState)
|
||||
const store = writable<HistoryState<T>>(initialState)
|
||||
const derivedStore = derived(store, $store => {
|
||||
return {
|
||||
...$store,
|
||||
|
@ -31,8 +70,8 @@ export const createHistoryStore = ({
|
|||
|
||||
// Wrapped versions of essential functions which we call ourselves when using
|
||||
// undo and redo
|
||||
let saveFn
|
||||
let deleteFn
|
||||
let saveFn: (doc: T, operationId?: number) => Promise<T>
|
||||
let deleteFn: (doc: T, operationId?: number) => Promise<void>
|
||||
|
||||
/**
|
||||
* Internal util to set the loading flag
|
||||
|
@ -66,7 +105,7 @@ export const createHistoryStore = ({
|
|||
* For internal use only.
|
||||
* @param operation the operation to save
|
||||
*/
|
||||
const saveOperation = operation => {
|
||||
const saveOperation = (operation: Operator<T>) => {
|
||||
store.update(state => {
|
||||
// Update history
|
||||
let history = state.history
|
||||
|
@ -93,15 +132,15 @@ export const createHistoryStore = ({
|
|||
* @param fn the save function
|
||||
* @returns {function} a wrapped version of the save function
|
||||
*/
|
||||
const wrapSaveDoc = fn => {
|
||||
saveFn = async (doc, operationId) => {
|
||||
const wrapSaveDoc = (fn: (doc: T) => Promise<T>) => {
|
||||
saveFn = async (doc: T, operationId?: number) => {
|
||||
// Only works on a single doc at a time
|
||||
if (!doc || Array.isArray(doc)) {
|
||||
return
|
||||
}
|
||||
startLoading()
|
||||
try {
|
||||
const oldDoc = getDoc(doc._id)
|
||||
const oldDoc = getDoc(doc._id!)
|
||||
const newDoc = jsonpatch.deepClone(await fn(doc))
|
||||
|
||||
// Store the change
|
||||
|
@ -141,8 +180,8 @@ export const createHistoryStore = ({
|
|||
* @param fn the delete function
|
||||
* @returns {function} a wrapped version of the delete function
|
||||
*/
|
||||
const wrapDeleteDoc = fn => {
|
||||
deleteFn = async (doc, operationId) => {
|
||||
const wrapDeleteDoc = (fn: (doc: T) => Promise<void>) => {
|
||||
deleteFn = async (doc: T, operationId?: number) => {
|
||||
// Only works on a single doc at a time
|
||||
if (!doc || Array.isArray(doc)) {
|
||||
return
|
||||
|
@ -201,7 +240,7 @@ export const createHistoryStore = ({
|
|||
// Undo ADD
|
||||
if (operation.type === Operations.Add) {
|
||||
// Try to get the latest doc version to delete
|
||||
const latestDoc = getDoc(operation.doc._id)
|
||||
const latestDoc = getDoc(operation.doc._id!)
|
||||
const doc = latestDoc || operation.doc
|
||||
await deleteFn(doc, operation.id)
|
||||
}
|
||||
|
@ -219,7 +258,7 @@ export const createHistoryStore = ({
|
|||
// Undo CHANGE
|
||||
else {
|
||||
// Get the current doc and apply the backwards patch on top of it
|
||||
let doc = jsonpatch.deepClone(getDoc(operation.doc._id))
|
||||
let doc = jsonpatch.deepClone(getDoc(operation.doc._id!))
|
||||
if (doc) {
|
||||
jsonpatch.applyPatch(
|
||||
doc,
|
||||
|
@ -283,7 +322,7 @@ export const createHistoryStore = ({
|
|||
// Redo DELETE
|
||||
else if (operation.type === Operations.Delete) {
|
||||
// Try to get the latest doc version to delete
|
||||
const latestDoc = getDoc(operation.doc._id)
|
||||
const latestDoc = getDoc(operation.doc._id!)
|
||||
const doc = latestDoc || operation.doc
|
||||
await deleteFn(doc, operation.id)
|
||||
}
|
||||
|
@ -291,7 +330,7 @@ export const createHistoryStore = ({
|
|||
// Redo CHANGE
|
||||
else {
|
||||
// Get the current doc and apply the forwards patch on top of it
|
||||
let doc = jsonpatch.deepClone(getDoc(operation.doc._id))
|
||||
let doc = jsonpatch.deepClone(getDoc(operation.doc._id!))
|
||||
if (doc) {
|
||||
jsonpatch.applyPatch(doc, jsonpatch.deepClone(operation.forwardPatch))
|
||||
await saveFn(doc, operation.id)
|
|
@ -10,7 +10,7 @@ import {
|
|||
navigationStore,
|
||||
selectedComponent,
|
||||
} from "@/stores/builder"
|
||||
import { createHistoryStore } from "@/stores/builder/history"
|
||||
import { createHistoryStore, HistoryStore } from "@/stores/builder/history"
|
||||
import { API } from "@/api"
|
||||
import { BudiStore } from "../BudiStore"
|
||||
import {
|
||||
|
@ -33,9 +33,9 @@ export const initialScreenState: ScreenState = {
|
|||
|
||||
// Review the nulls
|
||||
export class ScreenStore extends BudiStore<ScreenState> {
|
||||
history: any
|
||||
delete: any
|
||||
save: any
|
||||
history: HistoryStore<Screen>
|
||||
delete: (screens: Screen) => Promise<void>
|
||||
save: (screen: Screen) => Promise<Screen>
|
||||
|
||||
constructor() {
|
||||
super(initialScreenState)
|
||||
|
@ -280,7 +280,10 @@ export class ScreenStore extends BudiStore<ScreenState> {
|
|||
* supports deeply mutating the current doc rather than just appending data.
|
||||
*/
|
||||
sequentialScreenPatch = Utils.sequential(
|
||||
async (patchFn: (screen: Screen) => any, screenId: string) => {
|
||||
async (
|
||||
patchFn: (screen: Screen) => boolean,
|
||||
screenId: string
|
||||
): Promise<Screen | void> => {
|
||||
const state = get(this.store)
|
||||
const screen = state.screens.find(screen => screen._id === screenId)
|
||||
if (!screen) {
|
||||
|
@ -361,10 +364,10 @@ export class ScreenStore extends BudiStore<ScreenState> {
|
|||
* Any deleted screens will then have their routes/links purged
|
||||
*
|
||||
* Wrapped by {@link delete}
|
||||
* @param {Screen | Screen[]} screens
|
||||
* @param {Screen } screens
|
||||
*/
|
||||
async deleteScreen(screens: Screen | Screen[]) {
|
||||
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
||||
async deleteScreen(screen: Screen) {
|
||||
const screensToDelete = [screen]
|
||||
// Build array of promises to speed up bulk deletions
|
||||
let promises: Promise<DeleteScreenResponse>[] = []
|
||||
let deleteUrls: string[] = []
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { SearchFilterGroup, UISearchFilter } from "@budibase/types"
|
||||
|
||||
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||
export const sleep = (ms: number) =>
|
||||
new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
/**
|
||||
* Utility to wrap an async function and ensure all invocations happen
|
||||
|
@ -10,12 +12,18 @@ export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|||
* @param fn the async function to run
|
||||
* @return {Function} a sequential version of the function
|
||||
*/
|
||||
export const sequential = fn => {
|
||||
let queue = []
|
||||
return (...params) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
export const sequential = <
|
||||
TReturn,
|
||||
TFunction extends (...args: any[]) => Promise<TReturn>
|
||||
>(
|
||||
fn: TFunction
|
||||
): TFunction => {
|
||||
let queue: (() => Promise<void>)[] = []
|
||||
const result = (...params: Parameters<TFunction>) => {
|
||||
return new Promise<TReturn>((resolve, reject) => {
|
||||
queue.push(async () => {
|
||||
let data, error
|
||||
let data: TReturn | undefined
|
||||
let error: unknown
|
||||
try {
|
||||
data = await fn(...params)
|
||||
} catch (err) {
|
||||
|
@ -28,7 +36,7 @@ export const sequential = fn => {
|
|||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(data)
|
||||
resolve(data!)
|
||||
}
|
||||
})
|
||||
if (queue.length === 1) {
|
||||
|
@ -36,6 +44,7 @@ export const sequential = fn => {
|
|||
}
|
||||
})
|
||||
}
|
||||
return result as TFunction
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,9 +54,9 @@ export const sequential = fn => {
|
|||
* @param minDelay the minimum delay between invocations
|
||||
* @returns a debounced version of the callback
|
||||
*/
|
||||
export const debounce = (callback, minDelay = 1000) => {
|
||||
let timeout
|
||||
return async (...params) => {
|
||||
export const debounce = (callback: Function, minDelay = 1000) => {
|
||||
let timeout: ReturnType<typeof setTimeout>
|
||||
return async (...params: any[]) => {
|
||||
return new Promise(resolve => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
|
@ -70,11 +79,11 @@ export const debounce = (callback, minDelay = 1000) => {
|
|||
* @param minDelay
|
||||
* @returns {Function} a throttled version function
|
||||
*/
|
||||
export const throttle = (callback, minDelay = 1000) => {
|
||||
let lastParams
|
||||
export const throttle = (callback: Function, minDelay = 1000) => {
|
||||
let lastParams: any[]
|
||||
let stalled = false
|
||||
let pending = false
|
||||
const invoke = (...params) => {
|
||||
const invoke = (...params: any[]) => {
|
||||
lastParams = params
|
||||
if (stalled) {
|
||||
pending = true
|
||||
|
@ -98,10 +107,10 @@ export const throttle = (callback, minDelay = 1000) => {
|
|||
* @param callback the function to run
|
||||
* @returns {Function}
|
||||
*/
|
||||
export const domDebounce = callback => {
|
||||
export const domDebounce = (callback: Function) => {
|
||||
let active = false
|
||||
let lastParams
|
||||
return (...params) => {
|
||||
let lastParams: any[]
|
||||
return (...params: any[]) => {
|
||||
lastParams = params
|
||||
if (!active) {
|
||||
active = true
|
||||
|
@ -119,7 +128,17 @@ export const domDebounce = callback => {
|
|||
*
|
||||
* @param {any} props
|
||||
* */
|
||||
export const buildFormBlockButtonConfig = props => {
|
||||
export const buildFormBlockButtonConfig = (props?: {
|
||||
_id?: string
|
||||
actionType?: string
|
||||
dataSource?: { resourceId: string }
|
||||
notificationOverride?: boolean
|
||||
actionUrl?: string
|
||||
showDeleteButton?: boolean
|
||||
deleteButtonLabel?: string
|
||||
showSaveButton?: boolean
|
||||
saveButtonLabel?: string
|
||||
}) => {
|
||||
const {
|
||||
_id,
|
||||
actionType,
|
||||
|
@ -227,7 +246,11 @@ export const buildFormBlockButtonConfig = props => {
|
|||
|
||||
const defaultButtons = []
|
||||
|
||||
if (["Update", "Create"].includes(actionType) && showSaveButton !== false) {
|
||||
if (
|
||||
actionType &&
|
||||
["Update", "Create"].includes(actionType) &&
|
||||
showSaveButton !== false
|
||||
) {
|
||||
defaultButtons.push({
|
||||
text: saveText || "Save",
|
||||
_id: Helpers.uuid(),
|
||||
|
@ -251,7 +274,13 @@ export const buildFormBlockButtonConfig = props => {
|
|||
return defaultButtons
|
||||
}
|
||||
|
||||
export const buildMultiStepFormBlockDefaultProps = props => {
|
||||
export const buildMultiStepFormBlockDefaultProps = (props?: {
|
||||
_id: string
|
||||
stepCount: number
|
||||
currentStep: number
|
||||
actionType: string
|
||||
dataSource: { resourceId: string }
|
||||
}) => {
|
||||
const { _id, stepCount, currentStep, actionType, dataSource } = props || {}
|
||||
|
||||
// Sanity check
|
||||
|
@ -361,7 +390,7 @@ export const buildMultiStepFormBlockDefaultProps = props => {
|
|||
* @param {Object} filter UI filter
|
||||
* @returns {Object} parsed filter
|
||||
*/
|
||||
export function parseFilter(filter) {
|
||||
export function parseFilter(filter: UISearchFilter) {
|
||||
if (!filter?.groups) {
|
||||
return filter
|
||||
}
|
||||
|
@ -369,13 +398,13 @@ export function parseFilter(filter) {
|
|||
const update = cloneDeep(filter)
|
||||
|
||||
update.groups = update.groups
|
||||
.map(group => {
|
||||
group.filters = group.filters.filter(filter => {
|
||||
?.map(group => {
|
||||
group.filters = group.filters?.filter((filter: any) => {
|
||||
return filter.field && filter.operator
|
||||
})
|
||||
return group.filters.length ? group : null
|
||||
return group.filters?.length ? group : null
|
||||
})
|
||||
.filter(group => group)
|
||||
.filter((group): group is SearchFilterGroup => !!group)
|
||||
|
||||
return update
|
||||
}
|
Loading…
Reference in New Issue