Allow all users into the design section, enable multi dev collab on screens, improve routing
This commit is contained in:
parent
bc47f5b0b2
commit
d80cca9a11
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,8 @@ export enum BuilderSocketEvent {
|
|||
TableChange = "TableChange",
|
||||
DatasourceChange = "DatasourceChange",
|
||||
LockTransfer = "LockTransfer",
|
||||
ScreenChange = "ScreenChange",
|
||||
AppMetadataChange = "AppMetadataChange",
|
||||
}
|
||||
|
||||
export const SocketSessionTTL = 60
|
||||
|
|
Loading…
Reference in New Issue