1
0
Fork 0
mirror of synced 2024-10-02 10:08:09 +13:00

Allow all users into the design section, enable multi dev collab on screens, improve routing

This commit is contained in:
Andrew Kingston 2023-07-03 11:14:07 +01:00
parent bc47f5b0b2
commit d80cca9a11
9 changed files with 89 additions and 64 deletions

View file

@ -353,6 +353,35 @@ export const getFrontendStore = () => {
}
return await sequentialScreenPatch(patchFn, screenId)
},
replace: async (screenId, screen) => {
if (!screenId) {
return
}
// Handle deletion
if (!screen) {
store.update(state => ({
...state,
screens: state.screens.filter(x => x._id !== screenId),
}))
return
}
// Add new datasource
const index = get(store).screens.findIndex(x => x._id === screen._id)
if (index === -1) {
store.update(state => ({
...state,
screens: [...state.screens, screen],
}))
}
// Update existing datasource
store.update(state => {
state.screens[index] = screen
return state
})
},
delete: async screens => {
const screensToDelete = Array.isArray(screens) ? screens : [screens]

View file

@ -31,7 +31,7 @@ export const createBuilderWebsocket = appId => {
})
socket.onOther(BuilderSocketEvent.LockTransfer, ({ userId }) => {
if (userId === get(auth)?.user?._id) {
notifications.success("You can now edit screens and automations")
notifications.success("You can now edit automations")
store.update(state => ({
...state,
hasLock: true,
@ -39,15 +39,18 @@ export const createBuilderWebsocket = appId => {
}
})
// Table events
// Data section events
socket.onOther(BuilderSocketEvent.TableChange, ({ id, table }) => {
tables.replaceTable(id, table)
})
// Datasource events
socket.onOther(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => {
datasources.replaceDatasource(id, datasource)
})
// Design section events
socket.onOther(BuilderSocketEvent.ScreenChange, ({ id, screen }) => {
store.actions.screens.replace(id, screen)
})
return socket
}

View file

@ -114,26 +114,24 @@ export const syncURLToState = options => {
// Updates the URL with new state values
const mapStateToUrl = state => {
let needsUpdate = false
const urlValue = cachedParams?.[urlParam]
const stateValue = state?.[stateKey]
if (stateValue !== urlValue) {
needsUpdate = true
log(`url.${urlParam} (${urlValue}) <= state.${stateKey} (${stateValue})`)
if (validate && fallbackUrl) {
if (!validate(stateValue)) {
log("Invalid state param!", stateValue)
redirectUrl(fallbackUrl)
return
}
// As the store updated, validate that the current state value is valid
if (validate && fallbackUrl) {
if (!validate(stateValue)) {
log("Invalid state param!", stateValue)
redirectUrl(fallbackUrl)
return
}
}
// Avoid updating the URL if not necessary to prevent a wasted render
// cycle
if (!needsUpdate) {
if (stateValue === urlValue) {
return
}
log(`url.${urlParam} (${urlValue}) <= state.${stateKey} (${stateValue})`)
// Navigate to the new URL
if (!get(isChangingPage)) {

View file

@ -151,31 +151,19 @@
on:click={() => $goto("../../portal/apps")}
/>
</span>
{#if $store.hasLock}
<Tabs {selected} size="M">
{#each $layout.children as { path, title }}
<TourWrap tourStepKey={`builder-${title}-section`}>
<Tab
quiet
selected={$isActive(path)}
on:click={topItemNavigate(path)}
title={capitalise(title)}
id={`builder-${title}-tab`}
/>
</TourWrap>
{/each}
</Tabs>
{:else}
<div class="secondary-editor">
<Icon name="LockClosed" />
<div
class="secondary-editor-body"
title="Another user is currently editing your screens and automations"
>
Another user is currently editing your screens and automations
</div>
</div>
{/if}
<Tabs {selected} size="M">
{#each $layout.children as { path, title }}
<TourWrap tourStepKey={`builder-${title}-section`}>
<Tab
quiet
selected={$isActive(path)}
on:click={topItemNavigate(path)}
title={capitalise(title)}
id={`builder-${title}-tab`}
/>
</TourWrap>
{/each}
</Tabs>
</div>
<div class="topcenternav">
<Heading size="XS">{$store.name}</Heading>

View file

@ -8,15 +8,6 @@
import { onDestroy, onMount } from "svelte"
import { syncURLToState } from "helpers/urlStateSync"
import * as routify from "@roxi/routify"
import { store } from "builderStore"
import { redirect } from "@roxi/routify"
// Prevent access for other users than the lock holder
$: {
if (!$store.hasLock) {
$redirect("../data")
}
}
// Keep URL and state in sync for selected screen ID
const stopSyncing = syncURLToState({

View file

@ -1,14 +1,2 @@
<script>
import { store } from "builderStore"
import { redirect } from "@roxi/routify"
// Prevent access for other users than the lock holder
$: {
if (!$store.hasLock) {
$redirect("../data")
}
}
</script>
<!-- routify:options index=2 -->
<slot />

View file

@ -8,6 +8,7 @@ import {
} from "@budibase/backend-core"
import { updateAppPackage } from "./application"
import { Plugin, ScreenProps, BBContext } from "@budibase/types"
import { builderSocket } from "../../websockets"
export async function fetch(ctx: BBContext) {
const db = context.getAppDB()
@ -87,13 +88,17 @@ export async function save(ctx: BBContext) {
if (eventFn) {
await eventFn(screen)
}
ctx.message = `Screen ${screen.name} saved.`
ctx.body = {
const savedScreen = {
...screen,
_id: response.id,
_rev: response.rev,
}
ctx.message = `Screen ${screen.name} saved.`
ctx.body = {
...savedScreen,
pluginAdded,
}
builderSocket?.emitScreenUpdate(ctx, savedScreen)
}
export async function destroy(ctx: BBContext) {
@ -108,6 +113,7 @@ export async function destroy(ctx: BBContext) {
message: "Screen deleted successfully",
}
ctx.status = 200
builderSocket?.emitScreenDeletion(ctx, id)
}
function findPlugins(component: ScreenProps, foundPlugins: string[]) {

View file

@ -3,7 +3,13 @@ import { BaseSocket } from "./websocket"
import { permissions, events } from "@budibase/backend-core"
import http from "http"
import Koa from "koa"
import { Datasource, Table, SocketSession, ContextUser } from "@budibase/types"
import {
Datasource,
Table,
SocketSession,
ContextUser,
Screen,
} from "@budibase/types"
import { gridSocket } from "./index"
import { clearLock, updateLock } from "../utilities/redis"
import { Socket } from "socket.io"
@ -101,4 +107,18 @@ export default class BuilderSocket extends BaseSocket {
datasource: null,
})
}
emitScreenUpdate(ctx: any, screen: Screen) {
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.ScreenChange, {
id: screen._id,
screen,
})
}
emitScreenDeletion(ctx: any, id: string) {
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.ScreenChange, {
id,
screen: null,
})
}
}

View file

@ -86,6 +86,8 @@ export enum BuilderSocketEvent {
TableChange = "TableChange",
DatasourceChange = "DatasourceChange",
LockTransfer = "LockTransfer",
ScreenChange = "ScreenChange",
AppMetadataChange = "AppMetadataChange",
}
export const SocketSessionTTL = 60