Add support for confirmable action handling by client library
This commit is contained in:
parent
064690c85a
commit
1879fbeee3
|
@ -3,6 +3,7 @@
|
|||
import { setContext, onMount } from "svelte"
|
||||
import Component from "./Component.svelte"
|
||||
import NotificationDisplay from "./NotificationDisplay.svelte"
|
||||
import ConfirmationDisplay from "./ConfirmationDisplay.svelte"
|
||||
import Provider from "./Provider.svelte"
|
||||
import SDK from "../sdk"
|
||||
import {
|
||||
|
@ -70,6 +71,7 @@
|
|||
{/key}
|
||||
</div>
|
||||
<NotificationDisplay />
|
||||
<ConfirmationDisplay />
|
||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||
{#key $builderStore.selectedComponentId}
|
||||
{#if $builderStore.inBuilder}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import { confirmationStore } from "../store"
|
||||
import { Modal, ModalContent } from "@budibase/bbui"
|
||||
</script>
|
||||
|
||||
{#if $confirmationStore.showConfirmation}
|
||||
<Modal fixed on:cancel={confirmationStore.actions.cancel}>
|
||||
<ModalContent
|
||||
title="Confirm Action"
|
||||
onConfirm={confirmationStore.actions.confirm}
|
||||
>
|
||||
{$confirmationStore.text}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
{/if}
|
|
@ -1,40 +1,11 @@
|
|||
import * as API from "../api"
|
||||
import { writable, get } from "svelte/store"
|
||||
import { initialise } from "./initialise"
|
||||
import { routeStore } from "./routes"
|
||||
import { builderStore } from "./builder"
|
||||
import { TableNames } from "../constants"
|
||||
|
||||
const createAuthStore = () => {
|
||||
const store = writable(null)
|
||||
|
||||
const goToDefaultRoute = () => {
|
||||
// Setting the active route forces an update of the active screen ID,
|
||||
// even if we're on the same URL
|
||||
routeStore.actions.setActiveRoute("/")
|
||||
|
||||
// Navigating updates the URL to reflect this route
|
||||
routeStore.actions.navigate("/")
|
||||
}
|
||||
|
||||
// Logs a user in
|
||||
const logIn = async ({ email, password }) => {
|
||||
const auth = await API.logIn({ email, password })
|
||||
if (auth.success) {
|
||||
await fetchUser()
|
||||
await initialise()
|
||||
goToDefaultRoute()
|
||||
}
|
||||
}
|
||||
|
||||
// Logs a user out
|
||||
const logOut = async () => {
|
||||
store.set(null)
|
||||
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||
await initialise()
|
||||
goToDefaultRoute()
|
||||
}
|
||||
|
||||
// Fetches the user object if someone is logged in and has reloaded the page
|
||||
const fetchUser = async () => {
|
||||
// Fetch the first user if inside the builder
|
||||
|
@ -54,7 +25,7 @@ const createAuthStore = () => {
|
|||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { logIn, logOut, fetchUser },
|
||||
actions: { fetchUser },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
|
||||
const initialState = {
|
||||
showConfirmation: false,
|
||||
text: null,
|
||||
callback: null,
|
||||
}
|
||||
|
||||
const createConfirmationStore = () => {
|
||||
const store = writable(initialState)
|
||||
|
||||
const showConfirmation = (text, callback) => {
|
||||
store.set({
|
||||
showConfirmation: true,
|
||||
text,
|
||||
callback,
|
||||
})
|
||||
}
|
||||
const confirm = async () => {
|
||||
const state = get(store)
|
||||
if (!state.showConfirmation || !state.callback) {
|
||||
return
|
||||
}
|
||||
store.set(initialState)
|
||||
await state.callback()
|
||||
}
|
||||
const cancel = () => {
|
||||
store.set(initialState)
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { showConfirmation, confirm, cancel },
|
||||
}
|
||||
}
|
||||
|
||||
export const confirmationStore = createConfirmationStore()
|
|
@ -4,6 +4,7 @@ export { routeStore } from "./routes"
|
|||
export { screenStore } from "./screens"
|
||||
export { builderStore } from "./builder"
|
||||
export { dataSourceStore } from "./dataSource"
|
||||
export { confirmationStore } from "./confirmation"
|
||||
|
||||
// Context stores are layered and duplicated, so it is not a singleton
|
||||
export { createContextStore } from "./context"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { get } from "svelte/store"
|
||||
import { routeStore, builderStore, authStore } from "../store"
|
||||
import { routeStore, builderStore, confirmationStore } from "../store"
|
||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||
import { ActionTypes } from "../constants"
|
||||
|
||||
|
@ -68,15 +68,6 @@ const refreshDatasourceHandler = async (action, context) => {
|
|||
)
|
||||
}
|
||||
|
||||
const loginHandler = async action => {
|
||||
const { email, password } = action.parameters
|
||||
await authStore.actions.logIn({ email, password })
|
||||
}
|
||||
|
||||
const logoutHandler = async () => {
|
||||
await authStore.actions.logOut()
|
||||
}
|
||||
|
||||
const handlerMap = {
|
||||
["Save Row"]: saveRowHandler,
|
||||
["Delete Row"]: deleteRowHandler,
|
||||
|
@ -85,13 +76,19 @@ const handlerMap = {
|
|||
["Trigger Automation"]: triggerAutomationHandler,
|
||||
["Validate Form"]: validateFormHandler,
|
||||
["Refresh Datasource"]: refreshDatasourceHandler,
|
||||
["Log In"]: loginHandler,
|
||||
["Log Out"]: logoutHandler,
|
||||
}
|
||||
|
||||
const confirmTextMap = {
|
||||
["Delete Row"]: "Are you sure you want to delete this row?",
|
||||
["Save Row"]: "Are you sure you want to save this row?",
|
||||
["Execute Query"]: "Are you sure you want to execute this query?",
|
||||
["Trigger Automation"]: "Are you sure you want to trigger this automation?",
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of actions and returns a function which will execute the
|
||||
* actions in the current context.
|
||||
* A handler returning `false` is a flag to stop execution of handlers
|
||||
*/
|
||||
export const enrichButtonActions = (actions, context) => {
|
||||
// Prevent button actions in the builder preview
|
||||
|
@ -102,15 +99,40 @@ export const enrichButtonActions = (actions, context) => {
|
|||
return async () => {
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
try {
|
||||
const result = await handlers[i](actions[i], context)
|
||||
// A handler returning `false` is a flag to stop execution of handlers
|
||||
if (result === false) {
|
||||
const action = actions[i]
|
||||
const callback = async () => handlers[i](action, context)
|
||||
|
||||
// If this action is confirmable, show confirmation and await a
|
||||
// callback to execute further actions
|
||||
if (action.parameters?.confirm) {
|
||||
const defaultText = confirmTextMap[action["##eventHandlerType"]]
|
||||
const confirmText = action.parameters?.confirmText || defaultText
|
||||
confirmationStore.actions.showConfirmation(confirmText, async () => {
|
||||
// When confirmed, execute this action immediately,
|
||||
// then execute the rest of the actions in the chain
|
||||
const result = await callback()
|
||||
if (result !== false) {
|
||||
const next = enrichButtonActions(actions.slice(i + 1), context)
|
||||
await next()
|
||||
}
|
||||
})
|
||||
|
||||
// Stop enriching actions when encountering a confirmable actions,
|
||||
// as the callback continues the action chain
|
||||
return
|
||||
}
|
||||
|
||||
// For non-confirmable actions, execute this immediately
|
||||
else {
|
||||
const result = await callback()
|
||||
if (result === false) {
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error while executing button handler")
|
||||
console.error(error)
|
||||
// Stop executing on an error
|
||||
// Stop executing further actions on error
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue