mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
fixes/multi-editor: users should be able to edit different version of the app at real time without sync
This commit is contained in:
parent
7fbeee7d7e
commit
77730b07ca
3 changed files with 30 additions and 20 deletions
|
|
@ -125,6 +125,7 @@ const EditorComponent = (props) => {
|
|||
appDefinitionDiff,
|
||||
appDiffOptions,
|
||||
events,
|
||||
areOthersOnSameVersionAndPage,
|
||||
} = useAppInfo();
|
||||
|
||||
const [currentPageId, setCurrentPageId] = useState(null);
|
||||
|
|
@ -283,30 +284,32 @@ const EditorComponent = (props) => {
|
|||
updateEditorState({
|
||||
canUndo: false,
|
||||
canRedo: false,
|
||||
// currentVersion: uuid(),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* When a new update is received over-the-websocket connection
|
||||
* the useEffect in Container.jsx is triggered, but already appDef had been updated
|
||||
* to avoid ymap observe going into a infinite loop a check is added where if the
|
||||
* current appDef is equal to the newAppDef then we do not trigger a realtimeSave
|
||||
* Initializes real-time saving of application definitions if multiplayer editing is enabled.
|
||||
* Monitors changes in the 'appDef' property of the provided 'ymap' object and triggers a real-time save
|
||||
* when all conditions are met.
|
||||
*/
|
||||
const initRealtimeSave = () => {
|
||||
// Check if multiplayer editing is enabled; if not, return early
|
||||
if (!config.ENABLE_MULTIPLAYER_EDITING) return null;
|
||||
|
||||
// Observe changes in the 'appDef' property of the 'ymap' object
|
||||
props.ymap?.observe(() => {
|
||||
const ymapUpdates = props.ymap?.get('appDef');
|
||||
|
||||
// Check if there is a new session and if others are on the same version and page
|
||||
if (!ymapUpdates.currentSessionId || ymapUpdates.currentSessionId === currentSessionId) return;
|
||||
// if (!isEqual(props.editingVersion?.id, props.ymap?.get('appDef').editingVersionId)) return;
|
||||
if (isEqual(appDefinition, ymapUpdates.newDefinition)) return;
|
||||
console.log('-----arpit real time ', {
|
||||
x: ymapUpdates.currentSessionId,
|
||||
y: currentSessionId,
|
||||
});
|
||||
|
||||
// Check if others are on the same version and page
|
||||
if (!ymapUpdates.areOthersOnSameVersionAndPage) return;
|
||||
|
||||
// Check if the new application definition is different from the current one
|
||||
if (isEqual(appDefinition, ymapUpdates.newDefinition)) return;
|
||||
|
||||
// Trigger real-time save with specific options
|
||||
realtimeSave(props.ymap?.get('appDef').newDefinition, {
|
||||
skipAutoSave: true,
|
||||
skipYmapUpdate: true,
|
||||
|
|
@ -741,13 +744,6 @@ const EditorComponent = (props) => {
|
|||
};
|
||||
|
||||
const appDefinitionChanged = async (newDefinition, opts = {}) => {
|
||||
if (config.ENABLE_MULTIPLAYER_EDITING && !opts.skipYmapUpdate) {
|
||||
props.ymap?.set('appDef', {
|
||||
newDefinition,
|
||||
editingVersionId: props.editingVersion?.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (opts?.versionChanged) {
|
||||
setCurrentPageId(newDefinition.homePageId);
|
||||
|
||||
|
|
@ -812,11 +808,12 @@ const EditorComponent = (props) => {
|
|||
computeComponentState(updatedAppDefinition.pages[currentPageId]?.components);
|
||||
}
|
||||
|
||||
if (!opts?.skipYmapUpdate && opts?.currentSessionId !== currentSessionId) {
|
||||
if (config.ENABLE_MULTIPLAYER_EDITING && !opts?.skipYmapUpdate && opts?.currentSessionId !== currentSessionId) {
|
||||
props.ymap?.set('appDef', {
|
||||
newDefinition: updatedAppDefinition,
|
||||
editingVersionId: props.editingVersion?.id,
|
||||
currentSessionId,
|
||||
areOthersOnSameVersionAndPage,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import Popover from '@/_ui/Popover';
|
||||
import Avatar from '@/_ui/Avatar';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { useOthers, useSelf } from '@y-presence/react';
|
||||
import { useAppDataActions, useAppInfo } from '@/_stores/appDataStore';
|
||||
|
||||
const MAX_DISPLAY_USERS = 2;
|
||||
const RealtimeAvatars = ({ darkMode }) => {
|
||||
|
|
@ -17,6 +18,17 @@ const RealtimeAvatars = ({ darkMode }) => {
|
|||
const getAvatarText = (presence) => presence.firstName?.charAt(0) + presence.lastName?.charAt(0);
|
||||
const getAvatarTitle = (presence) => `${presence.firstName} ${presence.lastName}`;
|
||||
|
||||
const { updateState } = useAppDataActions();
|
||||
const { areOthersOnSameVersionAndPage, currentVersionId } = useAppInfo();
|
||||
|
||||
useEffect(() => {
|
||||
const areActiveUsersOnSameVersionAndPage = othersOnSameVersionAndPage.length > 0;
|
||||
const shouldUpdateState = areActiveUsersOnSameVersionAndPage !== areOthersOnSameVersionAndPage;
|
||||
|
||||
if (shouldUpdateState) updateState({ areOthersOnSameVersionAndPage: areActiveUsersOnSameVersionAndPage });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify({ others, self, currentVersionId })]);
|
||||
|
||||
const popoverContent = () => {
|
||||
return othersOnSameVersionAndPage
|
||||
.slice(MAX_DISPLAY_USERS, othersOnSameVersionAndPage.length)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const initialState = {
|
|||
appDiffOptions: {},
|
||||
isSaving: false,
|
||||
appId: null,
|
||||
areOthersOnSameVersionAndPage: false,
|
||||
};
|
||||
|
||||
export const useAppDataStore = create(
|
||||
|
|
|
|||
Loading…
Reference in a new issue