Update and improve state <-> URL binding

This commit is contained in:
Andrew Kingston 2021-01-15 14:23:27 +00:00
parent 117dfcda29
commit 087647080c
13 changed files with 121 additions and 135 deletions

View File

@ -13,12 +13,7 @@ import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api" import api from "../api"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import analytics from "analytics" import analytics from "analytics"
import { import { findComponentType, findComponentParent } from "../storeUtils"
findComponentType,
findComponentParent,
findComponentPath,
findComponent,
} from "../storeUtils"
import { uuid } from "../uuid" import { uuid } from "../uuid"
const INITIAL_FRONTEND_STATE = { const INITIAL_FRONTEND_STATE = {
@ -487,21 +482,6 @@ export const getFrontendStore = () => {
}) })
store.actions.preview.saveSelected() store.actions.preview.saveSelected()
}, },
findRoute: component => {
const selectedAsset = get(currentAsset)
if (!component || !selectedAsset) {
return "/"
}
// Get the path to this component
const path = findComponentPath(selectedAsset.props, component._id) || []
// Remove root entry since it's the screen or layout
return path
.slice(1)
.map(component => component._id)
.join("/")
},
links: { links: {
save: async (url, title) => { save: async (url, title) => {
const layout = get(mainLayout) const layout = get(mainLayout)

View File

@ -46,11 +46,7 @@
popover.show() popover.show()
} else { } else {
// Add this component // Add this component
const newComponent = store.actions.components.create(item.component) store.actions.components.create(item.component)
if (newComponent) {
const path = store.actions.components.findRoute(newComponent)
$goto(`./${$currentAssetId}/${path}`)
}
popover.hide() popover.hide()
} }
} }

View File

@ -1,5 +1,4 @@
<script> <script>
import { goto } from "@sveltech/routify"
import { get } from "svelte/store" import { get } from "svelte/store"
import { store, currentAsset } from "builderStore" import { store, currentAsset } from "builderStore"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
@ -27,8 +26,6 @@
const selectComponent = component => { const selectComponent = component => {
store.actions.components.select(component) store.actions.components.select(component)
const path = store.actions.components.findRoute(component)
$goto(`./${$store.currentFrontEndType}/${path}`)
} }
const moveUpComponent = () => { const moveUpComponent = () => {

View File

@ -15,8 +15,6 @@
const selectComponent = component => { const selectComponent = component => {
store.actions.components.select(component) store.actions.components.select(component)
const path = store.actions.components.findRoute(component)
$goto(`./${$currentAssetId}/${path}`)
} }
const dragstart = component => e => { const dragstart = component => e => {

View File

@ -24,9 +24,7 @@
$: selectedScreen = $currentAsset $: selectedScreen = $currentAsset
const changeScreen = screenId => { const changeScreen = screenId => {
// select the route
store.actions.screens.select(screenId) store.actions.screens.select(screenId)
$goto(`./${screenId}`)
} }
</script> </script>

View File

@ -28,7 +28,7 @@
let modal let modal
let routes = {} let routes = {}
let tab = $params.assetType $: tab = $params.assetType
const navigate = ({ detail }) => { const navigate = ({ detail }) => {
if (!detail) { if (!detail) {

View File

@ -19,7 +19,6 @@
const selectLayout = () => { const selectLayout = () => {
store.actions.layouts.select(layout._id) store.actions.layouts.select(layout._id)
$goto(`./${layout._id}`)
} }
</script> </script>

View File

@ -8,8 +8,7 @@
async function save() { async function save() {
try { try {
const layout = await store.actions.layouts.save({ name }) await store.actions.layouts.save({ name })
$goto(`./${layout._id}`)
notifier.success(`Layout ${name} created successfully`) notifier.success(`Layout ${name} created successfully`)
} catch (err) { } catch (err) {
notifier.danger(`Error creating layout ${name}.`) notifier.danger(`Error creating layout ${name}.`)

View File

@ -63,7 +63,7 @@
draftScreen.props._component = baseComponent draftScreen.props._component = baseComponent
draftScreen.routing = { route, roleId } draftScreen.routing = { route, roleId }
const createdScreen = await store.actions.screens.create(draftScreen) await store.actions.screens.create(draftScreen)
if (createLink) { if (createLink) {
await store.actions.components.links.save(route, name) await store.actions.components.links.save(route, name)
} }
@ -75,8 +75,6 @@
template: template.id || template.name, template: template.id || template.name,
}) })
} }
$goto(`./${createdScreen._id}`)
} }
const routeExists = (route, roleId) => { const routeExists = (route, roleId) => {

View File

@ -35,7 +35,6 @@
$currentAsset.props, $currentAsset.props,
$store.selectedComponentId $store.selectedComponentId
) )
$: console.log(bindableProperties)
$: links = bindableProperties $: links = bindableProperties
.filter(x => x.fieldSchema?.type === "link") .filter(x => x.fieldSchema?.type === "link")

View File

@ -1,13 +1,16 @@
<script> <script>
import { onMount } from "svelte"
import { get } from "svelte/store"
import { params, leftover, goto } from "@sveltech/routify" import { params, leftover, goto } from "@sveltech/routify"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import { store, allScreens } from "builderStore" import { store, allScreens, currentAsset } from "builderStore"
import { findComponent, findComponentPath } from "builderStore/storeUtils"
// Get any leftover params not caught by Routifys params store. let initialised = false
const componentIds = $leftover.split("/").filter(id => id !== "")
const currentAssetId = decodeURI($params.asset)
// Hydrate state from query param on mount
onMount(() => {
const assetId = decodeURI($params.asset)
let assetList let assetList
let actions let actions
@ -20,45 +23,70 @@
actions = store.actions.layouts actions = store.actions.layouts
} }
// select the screen or layout in the UI // Find and select the current asset
actions.select(currentAssetId) const asset = assetList.find(asset => asset._id === assetId)
if (asset) {
actions.select(assetId)
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it. // Select the component ID if one is present in the URL
if ($leftover) { const selectedComponentId = $leftover.split("/").pop()
// Get the correct screen children. if (selectedComponentId) {
const assetChildren = const component = findComponent(asset.props, selectedComponentId)
assetList.find( if (component) {
asset => store.actions.components.select(component)
asset._id === $params.asset || }
asset._id === decodeURIComponent($params.asset) }
)?.props._children ?? []
findComponent(componentIds, assetChildren)
} }
// }
// Find Component with ID and continue initialised = true
function findComponent(ids, children) {
// Setup stuff
let componentToSelect
let currentChildren = children
// Loop through each ID
ids.forEach(id => {
// Find ID
const component = currentChildren.find(child => child._id === id)
// If it does not exist, ignore (use last valid route)
if (!component) return
componentToSelect = component
// Update childrens array to selected components children
currentChildren = componentToSelect._children
}) })
// Select Component! // Updates the route params in the URL to the specified values
if (componentToSelect) store.actions.components.select(componentToSelect) const updateParams = (assetType, asset, componentId) => {
// Wait until the initial state rehydration to avoid a wasted update
if (!initialised) {
return
} }
// Extract current URL params
const currentParams = get(params)
const currentLeftover = get(leftover)
const paramAssetType = currentParams.assetType
const paramAssetId = currentParams.asset
const paramComponentId = currentLeftover.split("/").pop()
// Only update params if the params actually changed
if (
assetType !== paramAssetType ||
asset?._id !== paramAssetId ||
componentId !== paramComponentId
) {
// Build and navigate to a valid URL
let url = "../../"
if ([FrontendTypes.SCREEN, FrontendTypes.LAYOUT].includes(assetType)) {
url += `${assetType}`
if (asset?._id) {
url += `/${asset._id}`
if (componentId) {
const componentPath = findComponentPath(asset.props, componentId)
const componentURL = componentPath
.slice(1)
.map(comp => comp._id)
.join("/")
url += `/${componentURL}`
}
}
}
$goto(url)
}
}
// Automatically keep URL up to date with state
$: updateParams(
$store.currentFrontEndType,
$currentAsset,
$store.selectedComponentId
)
</script> </script>
<slot /> <slot />

View File

@ -11,25 +11,11 @@
import ComponentSelectionList from "components/design/AppPreview/ComponentSelectionList.svelte" import ComponentSelectionList from "components/design/AppPreview/ComponentSelectionList.svelte"
import FrontendNavigatePane from "components/design/NavigationPanel/FrontendNavigatePane.svelte" import FrontendNavigatePane from "components/design/NavigationPanel/FrontendNavigatePane.svelte"
$: instance = $store.appInstance
async function selectDatabase(database) {
backendUiStore.actions.database.select(database)
}
onMount(async () => { onMount(async () => {
if ($store.appInstance && !$backendUiStore.database) { if ($store.appInstance && !$backendUiStore.database) {
await selectDatabase($store.appInstance) backendUiStore.actions.database.select($store.appInstance)
} }
}) })
let confirmDeleteDialog
let componentToDelete = ""
let settingsView
const settings = () => {
settingsView.show()
}
</script> </script>
<!-- routify:options index=1 --> <!-- routify:options index=1 -->

View File

@ -1,12 +1,21 @@
<script> <script>
import { onMount } from "svelte"
import { goto } from "@sveltech/routify"
import { store, allScreens } from "builderStore" import { store, allScreens } from "builderStore"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import { goto, params } from "@sveltech/routify" import { params } from "@sveltech/routify"
// Go to first layout onMount(() => {
if ($params.assetType === FrontendTypes.LAYOUT) {
// Try to use previously selected layout first
let id let id
// Get valid asset type
let assetType = $params.assetType
if (![FrontendTypes.LAYOUT, FrontendTypes.SCREEN].includes(assetType)) {
assetType = FrontendTypes.SCREEN
}
// Get ID or first correct asset type
if (assetType === FrontendTypes.LAYOUT) {
if ( if (
$store.selectedLayoutId && $store.selectedLayoutId &&
$store.layouts.find(layout => layout._id === $store.selectedLayoutId) $store.layouts.find(layout => layout._id === $store.selectedLayoutId)
@ -15,13 +24,7 @@
} else { } else {
id = $store.layouts[0]?._id id = $store.layouts[0]?._id
} }
$goto(`../${id}`) } else if (assetType === FrontendTypes.SCREEN) {
}
// Go to first screen
if ($params.assetType === FrontendTypes.SCREEN) {
// Try to use previously selected layout first
let id
if ( if (
$store.selectedScreenId && $store.selectedScreenId &&
$allScreens.find(screen => screen._id === $store.selectedScreenId) $allScreens.find(screen => screen._id === $store.selectedScreenId)
@ -30,8 +33,13 @@
} else { } else {
id = $allScreens[0]?._id id = $allScreens[0]?._id
} }
$goto(`../${id}`)
} }
// Send correct URL which will then update state
if (id) {
$goto(`../../${assetType}/${id}`)
}
})
</script> </script>
<!-- routify:options index=false --> <!-- routify:options index=false -->