2020-11-06 13:30:30 +01:00
|
|
|
import { get, writable } from "svelte/store"
|
2020-11-06 13:31:47 +01:00
|
|
|
import { cloneDeep } from "lodash/fp"
|
|
|
|
import {
|
|
|
|
createProps,
|
|
|
|
getBuiltin,
|
|
|
|
makePropsSafe,
|
|
|
|
} from "components/userInterface/pagesParsing/createProps"
|
2020-11-19 12:15:29 +01:00
|
|
|
import { allScreens, backendUiStore, selectedPage } from "builderStore"
|
2020-11-04 17:13:50 +01:00
|
|
|
import { generate_screen_css } from "../generate_css"
|
|
|
|
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
|
|
|
import api from "../api"
|
|
|
|
import { DEFAULT_PAGES_OBJECT } from "../../constants"
|
|
|
|
import getNewComponentName from "../getNewComponentName"
|
|
|
|
import analytics from "analytics"
|
2020-11-06 13:31:47 +01:00
|
|
|
import {
|
|
|
|
findChildComponentType,
|
|
|
|
generateNewIdsForComponent,
|
|
|
|
getComponentDefinition,
|
|
|
|
getParent,
|
|
|
|
} from "../storeUtils"
|
2020-11-04 17:13:50 +01:00
|
|
|
|
|
|
|
const INITIAL_FRONTEND_STATE = {
|
|
|
|
apps: [],
|
|
|
|
name: "",
|
|
|
|
description: "",
|
|
|
|
pages: DEFAULT_PAGES_OBJECT,
|
|
|
|
mainUi: {},
|
|
|
|
unauthenticatedUi: {},
|
|
|
|
components: [],
|
|
|
|
currentPreviewItem: null,
|
|
|
|
currentComponentInfo: null,
|
|
|
|
currentFrontEndType: "none",
|
|
|
|
currentPageName: "",
|
|
|
|
currentComponentProps: null,
|
|
|
|
errors: [],
|
|
|
|
hasAppPackage: false,
|
|
|
|
libraries: null,
|
|
|
|
appId: "",
|
|
|
|
}
|
|
|
|
|
|
|
|
export const getFrontendStore = () => {
|
|
|
|
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
|
|
|
|
|
|
|
store.actions = {
|
|
|
|
// TODO: REFACTOR
|
|
|
|
initialise: async pkg => {
|
2020-11-05 15:38:44 +01:00
|
|
|
store.update(state => {
|
|
|
|
state.appId = pkg.application._id
|
|
|
|
return state
|
|
|
|
})
|
2020-11-04 17:13:50 +01:00
|
|
|
const screens = await api.get("/api/screens").then(r => r.json())
|
|
|
|
|
|
|
|
const mainScreens = screens.filter(screen =>
|
|
|
|
screen._id.includes(pkg.pages.main._id)
|
|
|
|
),
|
|
|
|
unauthScreens = screens.filter(screen =>
|
|
|
|
screen._id.includes(pkg.pages.unauthenticated._id)
|
|
|
|
)
|
|
|
|
pkg.pages = {
|
|
|
|
main: {
|
|
|
|
...pkg.pages.main,
|
|
|
|
_screens: mainScreens,
|
|
|
|
},
|
|
|
|
unauthenticated: {
|
|
|
|
...pkg.pages.unauthenticated,
|
|
|
|
_screens: unauthScreens,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the app has just been created
|
|
|
|
// we need to build the CSS and save
|
|
|
|
if (pkg.justCreated) {
|
|
|
|
for (let pageName of ["main", "unauthenticated"]) {
|
|
|
|
const page = pkg.pages[pageName]
|
|
|
|
store.actions.screens.regenerateCss(page)
|
|
|
|
for (let screen of page._screens) {
|
|
|
|
store.actions.screens.regenerateCss(screen)
|
|
|
|
}
|
|
|
|
|
|
|
|
await api.post(`/api/pages/${page._id}`, {
|
|
|
|
page: {
|
|
|
|
componentLibraries: pkg.application.componentLibraries,
|
|
|
|
...page,
|
|
|
|
},
|
|
|
|
screens: page._screens,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
|
|
|
|
pkg.justCreated = false
|
|
|
|
|
|
|
|
const components = await fetchComponentLibDefinitions(pkg.application._id)
|
|
|
|
|
|
|
|
store.update(state => ({
|
|
|
|
...state,
|
|
|
|
libraries: pkg.application.componentLibraries,
|
|
|
|
components,
|
|
|
|
name: pkg.application.name,
|
|
|
|
description: pkg.application.description,
|
|
|
|
appId: pkg.application._id,
|
|
|
|
pages: pkg.pages,
|
|
|
|
hasAppPackage: true,
|
|
|
|
builtins: [getBuiltin("##builtin/screenslot")],
|
|
|
|
appInstance: pkg.application.instance,
|
|
|
|
}))
|
|
|
|
|
|
|
|
await backendUiStore.actions.database.select(pkg.application.instance)
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
selectPageOrScreen: type => {
|
|
|
|
store.update(state => {
|
|
|
|
state.currentFrontEndType = type
|
|
|
|
|
2020-11-19 12:15:29 +01:00
|
|
|
const page = get(selectedPage)
|
|
|
|
|
|
|
|
const pageOrScreen = type === "page" ? page : page._screens[0]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
|
|
|
state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
|
|
|
|
state.currentPreviewItem = pageOrScreen
|
|
|
|
state.currentView = "detail"
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
screens: {
|
2020-11-18 23:04:18 +01:00
|
|
|
select: screenId => {
|
2020-11-04 17:13:50 +01:00
|
|
|
store.update(state => {
|
2020-11-18 23:04:18 +01:00
|
|
|
const screen = get(allScreens).find(screen => screen._id === screenId)
|
2020-11-04 17:13:50 +01:00
|
|
|
state.currentPreviewItem = screen
|
|
|
|
state.currentFrontEndType = "screen"
|
|
|
|
state.currentView = "detail"
|
|
|
|
|
|
|
|
store.actions.screens.regenerateCssForCurrentScreen()
|
|
|
|
const safeProps = makePropsSafe(
|
|
|
|
state.components[screen.props._component],
|
|
|
|
screen.props
|
|
|
|
)
|
|
|
|
screen.props = safeProps
|
|
|
|
state.currentComponentInfo = safeProps
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
create: async screen => {
|
|
|
|
let savePromise
|
|
|
|
store.update(state => {
|
|
|
|
state.currentPreviewItem = screen
|
|
|
|
state.currentComponentInfo = screen.props
|
|
|
|
state.currentFrontEndType = "screen"
|
|
|
|
|
|
|
|
if (state.currentPreviewItem) {
|
|
|
|
store.actions.screens.regenerateCss(state.currentPreviewItem)
|
|
|
|
}
|
|
|
|
|
|
|
|
savePromise = store.actions.screens.save(screen)
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
|
|
|
|
await savePromise
|
|
|
|
},
|
|
|
|
save: async screen => {
|
2020-11-19 12:15:29 +01:00
|
|
|
const page = get(selectedPage)
|
|
|
|
const currentPageScreens = page._screens
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-09 11:28:49 +01:00
|
|
|
const creatingNewScreen = screen._id === undefined
|
|
|
|
|
2020-11-04 17:13:50 +01:00
|
|
|
let savePromise
|
2020-11-19 12:15:29 +01:00
|
|
|
const response = await api.post(`/api/screens/${page._id}`, screen)
|
2020-11-04 17:13:50 +01:00
|
|
|
const json = await response.json()
|
|
|
|
screen._rev = json.rev
|
|
|
|
screen._id = json.id
|
2020-11-19 12:15:29 +01:00
|
|
|
const foundScreen = page._screens.findIndex(el => el._id === screen._id)
|
2020-11-09 09:33:22 +01:00
|
|
|
if (foundScreen !== -1) {
|
2020-11-19 12:15:29 +01:00
|
|
|
page._screens.splice(foundScreen, 1)
|
2020-11-05 12:44:18 +01:00
|
|
|
}
|
2020-11-19 12:15:29 +01:00
|
|
|
page._screens.push(screen)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
|
|
|
// TODO: should carry out all server updates to screen in a single call
|
|
|
|
store.update(state => {
|
2020-11-19 12:15:29 +01:00
|
|
|
page._screens = currentPageScreens
|
2020-11-09 11:28:49 +01:00
|
|
|
|
|
|
|
if (creatingNewScreen) {
|
|
|
|
state.currentPreviewItem = screen
|
|
|
|
const safeProps = makePropsSafe(
|
|
|
|
state.components[screen.props._component],
|
|
|
|
screen.props
|
|
|
|
)
|
|
|
|
state.currentComponentInfo = safeProps
|
|
|
|
screen.props = safeProps
|
|
|
|
}
|
2020-11-13 11:29:54 +01:00
|
|
|
savePromise = store.actions.pages.save()
|
2020-11-09 11:28:49 +01:00
|
|
|
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-11-09 11:28:49 +01:00
|
|
|
if (savePromise) await savePromise
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
regenerateCss: screen => {
|
|
|
|
screen._css = generate_screen_css([screen.props])
|
|
|
|
},
|
|
|
|
regenerateCssForCurrentScreen: () => {
|
|
|
|
const { currentPreviewItem } = get(store)
|
|
|
|
if (currentPreviewItem) {
|
|
|
|
store.actions.screens.regenerateCss(currentPreviewItem)
|
|
|
|
}
|
|
|
|
},
|
2020-11-19 12:15:29 +01:00
|
|
|
delete: async screens => {
|
2020-11-04 17:13:50 +01:00
|
|
|
let deletePromise
|
|
|
|
|
2020-11-19 12:15:29 +01:00
|
|
|
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
|
|
|
|
2020-11-04 17:13:50 +01:00
|
|
|
store.update(state => {
|
2020-11-19 12:15:29 +01:00
|
|
|
const currentPage = get(selectedPage)
|
|
|
|
|
|
|
|
for (let screenToDelete of screensToDelete) {
|
2020-11-04 17:13:50 +01:00
|
|
|
// Remove screen from current page as well
|
|
|
|
// TODO: Should be done server side
|
2020-11-19 12:15:29 +01:00
|
|
|
currentPage._screens = currentPage._screens.filter(
|
|
|
|
scr => scr._id !== screenToDelete._id
|
|
|
|
)
|
2020-11-19 17:41:29 +01:00
|
|
|
|
2020-11-04 17:13:50 +01:00
|
|
|
deletePromise = api.delete(
|
|
|
|
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
await deletePromise
|
|
|
|
},
|
|
|
|
},
|
|
|
|
preview: {
|
2020-11-09 16:55:36 +01:00
|
|
|
saveSelected: async () => {
|
2020-11-04 17:13:50 +01:00
|
|
|
const state = get(store)
|
2020-11-09 16:55:36 +01:00
|
|
|
if (state.currentFrontEndType !== "page") {
|
|
|
|
await store.actions.screens.save(state.currentPreviewItem)
|
|
|
|
}
|
|
|
|
await store.actions.pages.save()
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
|
|
|
pages: {
|
|
|
|
select: pageName => {
|
|
|
|
store.update(state => {
|
|
|
|
const currentPage = state.pages[pageName]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
state.currentFrontEndType = "page"
|
|
|
|
state.currentView = "detail"
|
|
|
|
state.currentPageName = pageName
|
|
|
|
|
|
|
|
// This is the root of many problems.
|
|
|
|
// Uncaught (in promise) TypeError: Cannot read property '_component' of undefined
|
|
|
|
// it appears that the currentPage sometimes has _props instead of props
|
|
|
|
// why
|
|
|
|
const safeProps = makePropsSafe(
|
|
|
|
state.components[currentPage.props._component],
|
|
|
|
currentPage.props
|
|
|
|
)
|
|
|
|
state.currentComponentInfo = safeProps
|
|
|
|
currentPage.props = safeProps
|
|
|
|
state.currentPreviewItem = state.pages[pageName]
|
|
|
|
store.actions.screens.regenerateCssForCurrentScreen()
|
|
|
|
|
2020-11-05 18:47:27 +01:00
|
|
|
for (let screen of get(allScreens)) {
|
2020-11-04 18:09:45 +01:00
|
|
|
screen._css = generate_screen_css([screen.props])
|
|
|
|
}
|
|
|
|
|
|
|
|
return state
|
|
|
|
})
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-04 18:09:45 +01:00
|
|
|
save: async page => {
|
|
|
|
const storeContents = get(store)
|
|
|
|
const pageName = storeContents.currentPageName || "main"
|
|
|
|
const pageToSave = page || storeContents.pages[pageName]
|
|
|
|
|
|
|
|
// TODO: revisit. This sends down a very weird payload
|
2020-11-13 11:29:54 +01:00
|
|
|
const response = await api.post(`/api/pages/${pageToSave._id}`, {
|
|
|
|
page: {
|
|
|
|
componentLibraries: storeContents.pages.componentLibraries,
|
|
|
|
...pageToSave,
|
|
|
|
},
|
|
|
|
screens: pageToSave._screens,
|
|
|
|
})
|
|
|
|
|
|
|
|
const json = await response.json()
|
|
|
|
|
|
|
|
if (!json.ok) throw new Error("Error updating page")
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
2020-11-13 11:29:54 +01:00
|
|
|
state.pages[pageName]._rev = json.rev
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
select: component => {
|
|
|
|
store.update(state => {
|
|
|
|
const componentDef = component._component.startsWith("##")
|
|
|
|
? component
|
|
|
|
: state.components[component._component]
|
|
|
|
state.currentComponentInfo = makePropsSafe(componentDef, component)
|
|
|
|
state.currentView = "component"
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
create: (componentToAdd, presetProps) => {
|
|
|
|
store.update(state => {
|
|
|
|
function findSlot(component_array) {
|
2020-11-19 12:15:29 +01:00
|
|
|
for (let component of component_array) {
|
|
|
|
if (component._component === "##builtin/screenslot") {
|
2020-11-04 18:09:45 +01:00
|
|
|
return true
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-19 12:15:29 +01:00
|
|
|
if (component._children) findSlot(component)
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
return false
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
if (
|
|
|
|
componentToAdd.startsWith("##") &&
|
|
|
|
findSlot(state.pages[state.currentPageName].props._children)
|
|
|
|
) {
|
|
|
|
return state
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const component = getComponentDefinition(state, componentToAdd)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const instanceId = get(backendUiStore).selectedDatabase._id
|
|
|
|
const instanceName = getNewComponentName(component, state)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-06 13:31:47 +01:00
|
|
|
const newComponent = createProps(component, {
|
|
|
|
...presetProps,
|
|
|
|
_instanceId: instanceId,
|
|
|
|
_instanceName: instanceName,
|
|
|
|
})
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const currentComponent =
|
|
|
|
state.components[state.currentComponentInfo._component]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const targetParent = currentComponent.children
|
|
|
|
? state.currentComponentInfo
|
|
|
|
: getParent(
|
2020-11-04 17:13:50 +01:00
|
|
|
state.currentPreviewItem.props,
|
2020-11-04 18:09:45 +01:00
|
|
|
state.currentComponentInfo
|
2020-11-04 17:13:50 +01:00
|
|
|
)
|
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Don't continue if there's no parent
|
|
|
|
if (!targetParent) {
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
targetParent._children = targetParent._children.concat(
|
|
|
|
newComponent.props
|
|
|
|
)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
|
|
|
|
state.currentView = "component"
|
|
|
|
state.currentComponentInfo = newComponent.props
|
|
|
|
analytics.captureEvent("Added Component", {
|
|
|
|
name: newComponent.props._component,
|
|
|
|
})
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
copy: (component, cut = false) => {
|
|
|
|
store.update(state => {
|
2020-11-06 13:30:30 +01:00
|
|
|
state.componentToPaste = cloneDeep(component)
|
2020-11-04 18:09:45 +01:00
|
|
|
state.componentToPaste.isCut = cut
|
|
|
|
if (cut) {
|
2020-11-04 17:13:50 +01:00
|
|
|
const parent = getParent(
|
|
|
|
state.currentPreviewItem.props,
|
2020-11-04 18:09:45 +01:00
|
|
|
component._id
|
2020-11-04 17:13:50 +01:00
|
|
|
)
|
2020-11-04 18:09:45 +01:00
|
|
|
parent._children = parent._children.filter(
|
2020-11-18 23:04:18 +01:00
|
|
|
child => child._id !== component._id
|
2020-11-04 18:09:45 +01:00
|
|
|
)
|
|
|
|
store.actions.components.select(parent)
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
paste: (targetComponent, mode) => {
|
|
|
|
store.update(state => {
|
|
|
|
if (!state.componentToPaste) return state
|
|
|
|
|
|
|
|
const componentToPaste = cloneDeep(state.componentToPaste)
|
|
|
|
// retain the same ids as things may be referencing this component
|
|
|
|
if (componentToPaste.isCut) {
|
|
|
|
// in case we paste a second time
|
|
|
|
state.componentToPaste.isCut = false
|
|
|
|
} else {
|
|
|
|
generateNewIdsForComponent(componentToPaste, state)
|
|
|
|
}
|
|
|
|
delete componentToPaste.isCut
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
if (mode === "inside") {
|
|
|
|
targetComponent._children.push(componentToPaste)
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const parent = getParent(
|
|
|
|
state.currentPreviewItem.props,
|
|
|
|
targetComponent
|
|
|
|
)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const targetIndex = parent._children.indexOf(targetComponent)
|
|
|
|
const index = mode === "above" ? targetIndex : targetIndex + 1
|
|
|
|
parent._children.splice(index, 0, cloneDeep(componentToPaste))
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.actions.screens.regenerateCssForCurrentScreen()
|
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
store.actions.components.select(componentToPaste)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
updateStyle: (type, name, value) => {
|
|
|
|
store.update(state => {
|
|
|
|
if (!state.currentComponentInfo._styles) {
|
|
|
|
state.currentComponentInfo._styles = {}
|
|
|
|
}
|
|
|
|
state.currentComponentInfo._styles[type][name] = value
|
|
|
|
|
|
|
|
store.actions.screens.regenerateCssForCurrentScreen()
|
|
|
|
|
|
|
|
// save without messing with the store
|
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
updateProp: (name, value) => {
|
|
|
|
store.update(state => {
|
|
|
|
let current_component = state.currentComponentInfo
|
|
|
|
current_component[name] = value
|
|
|
|
|
|
|
|
state.currentComponentInfo = current_component
|
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
findRoute: component => {
|
|
|
|
// Gets all the components to needed to construct a path.
|
|
|
|
const tempStore = get(store)
|
|
|
|
let pathComponents = []
|
|
|
|
let parent = component
|
|
|
|
let root = false
|
|
|
|
while (!root) {
|
|
|
|
parent = getParent(tempStore.currentPreviewItem.props, parent)
|
|
|
|
if (!parent) {
|
|
|
|
root = true
|
|
|
|
} else {
|
|
|
|
pathComponents.push(parent)
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Remove root entry since it's the screen or page layout.
|
|
|
|
// Reverse array since we need the correct order of the IDs
|
|
|
|
const reversedComponents = pathComponents.reverse().slice(1)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Add component
|
|
|
|
const allComponents = [...reversedComponents, component]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Map IDs
|
|
|
|
const IdList = allComponents.map(c => c._id)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Construct ID Path:
|
2020-11-06 13:30:30 +01:00
|
|
|
return IdList.join("/")
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-05 12:44:18 +01:00
|
|
|
links: {
|
|
|
|
save: async (url, title) => {
|
|
|
|
let savePromise
|
|
|
|
store.update(state => {
|
|
|
|
// Try to extract a nav component from the master screen
|
|
|
|
const nav = findChildComponentType(
|
|
|
|
state.pages.main,
|
|
|
|
"@budibase/standard-components/Navigation"
|
|
|
|
)
|
|
|
|
if (nav) {
|
|
|
|
let newLink
|
|
|
|
|
|
|
|
// Clone an existing link if one exists
|
|
|
|
if (nav._children && nav._children.length) {
|
|
|
|
// Clone existing link style
|
|
|
|
newLink = cloneDeep(nav._children[0])
|
|
|
|
|
|
|
|
// Manipulate IDs to ensure uniqueness
|
|
|
|
generateNewIdsForComponent(newLink, state, false)
|
|
|
|
|
|
|
|
// Set our new props
|
|
|
|
newLink._instanceName = `${title} Link`
|
|
|
|
newLink.url = url
|
|
|
|
newLink.text = title
|
|
|
|
} else {
|
|
|
|
// Otherwise create vanilla new link
|
|
|
|
const component = getComponentDefinition(
|
|
|
|
state,
|
|
|
|
"@budibase/standard-components/link"
|
|
|
|
)
|
|
|
|
const instanceId = get(backendUiStore).selectedDatabase._id
|
|
|
|
newLink = createProps(component, {
|
|
|
|
url,
|
|
|
|
text: title,
|
|
|
|
_instanceName: `${title} Link`,
|
|
|
|
_instanceId: instanceId,
|
|
|
|
}).props
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save page and regenerate all CSS because otherwise weird things happen
|
|
|
|
nav._children = [...nav._children, newLink]
|
|
|
|
state.currentPageName = "main"
|
|
|
|
store.actions.screens.regenerateCss(state.pages.main)
|
|
|
|
for (let screen of state.pages.main._screens) {
|
|
|
|
store.actions.screens.regenerateCss(screen)
|
|
|
|
}
|
|
|
|
savePromise = store.actions.pages.save()
|
|
|
|
}
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
await savePromise
|
|
|
|
},
|
|
|
|
},
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
|
|
|
|
return store
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|