mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(editor): Show avatars for users currently working on the same workflow (#7763)
This PR introduces the following changes: - New Vue stores: `collaborationStore` and `pushConnectionStore` - Front-end push connection handling overhaul: Keep only a singe connection open and handle it from the new store - Add user avatars in the editor header when there are multiple users working on the same workflow - Sending a heartbeat event to back-end service periodically to confirm user is still active - Back-end overhauls (authored by @tomi): - Implementing a cleanup procedure that removes inactive users - Refactoring collaboration service current implementation --------- Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
99a9ea497a
commit
77bc8ecd4b
@@ -230,6 +230,7 @@ import {
|
||||
AI_NODE_CREATOR_VIEW,
|
||||
DRAG_EVENT_DATA_KEY,
|
||||
UPDATE_WEBHOOK_ID_NODE_TYPES,
|
||||
TIME,
|
||||
} from '@/constants';
|
||||
import { copyPaste } from '@/mixins/copyPaste';
|
||||
import { externalHooks } from '@/mixins/externalHooks';
|
||||
@@ -329,6 +330,7 @@ import {
|
||||
useUIStore,
|
||||
useHistoryStore,
|
||||
useExternalSecretsStore,
|
||||
useCollaborationStore,
|
||||
} from '@/stores';
|
||||
import * as NodeViewUtils from '@/utils/nodeViewUtils';
|
||||
import { getAccountAge, getConnectionInfo, getNodeViewTab } from '@/utils';
|
||||
@@ -523,15 +525,18 @@ export default defineComponent({
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this.collaborationStore.notifyWorkflowClosed(this.currentWorkflow);
|
||||
next();
|
||||
}
|
||||
} else if (confirmModal === MODAL_CANCEL) {
|
||||
this.collaborationStore.notifyWorkflowClosed(this.currentWorkflow);
|
||||
this.workflowsStore.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
||||
this.resetWorkspace();
|
||||
this.uiStore.stateIsDirty = false;
|
||||
next();
|
||||
}
|
||||
} else {
|
||||
this.collaborationStore.notifyWorkflowClosed(this.currentWorkflow);
|
||||
next();
|
||||
}
|
||||
},
|
||||
@@ -554,6 +559,7 @@ export default defineComponent({
|
||||
useWorkflowsEEStore,
|
||||
useHistoryStore,
|
||||
useExternalSecretsStore,
|
||||
useCollaborationStore,
|
||||
),
|
||||
nativelyNumberSuffixedDefaults(): string[] {
|
||||
return this.nodeTypesStore.nativelyNumberSuffixedDefaults;
|
||||
@@ -717,6 +723,7 @@ export default defineComponent({
|
||||
suspendRecordingDetachedConnections: false,
|
||||
NODE_CREATOR_OPEN_SOURCES,
|
||||
eventsAttached: false,
|
||||
unloadTimeout: undefined as undefined | ReturnType<typeof setTimeout>,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -1064,6 +1071,7 @@ export default defineComponent({
|
||||
this.workflowsStore.activeWorkflowExecution = selectedExecution;
|
||||
}
|
||||
this.stopLoading();
|
||||
this.collaborationStore.notifyWorkflowOpened(workflow.id);
|
||||
},
|
||||
touchTap(e: MouseEvent | TouchEvent) {
|
||||
if (this.isTouchDevice) {
|
||||
@@ -3047,20 +3055,30 @@ export default defineComponent({
|
||||
|
||||
this.eventsAttached = false;
|
||||
},
|
||||
onBeforeUnload(e) {
|
||||
onBeforeUnload(e: BeforeUnloadEvent) {
|
||||
if (this.isDemo || window.preventNodeViewBeforeUnload) {
|
||||
return;
|
||||
} else if (this.uiStore.stateIsDirty) {
|
||||
const confirmationMessage = this.$locale.baseText(
|
||||
'nodeView.itLooksLikeYouHaveBeenEditingSomething',
|
||||
);
|
||||
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
|
||||
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
|
||||
// A bit hacky solution to detecting users leaving the page after prompt:
|
||||
// 1. Notify that workflow is closed straight away
|
||||
this.collaborationStore.notifyWorkflowClosed(this.workflowsStore.workflowId);
|
||||
// 2. If user decided to stay on the page we notify that the workflow is opened again
|
||||
this.unloadTimeout = setTimeout(() => {
|
||||
this.collaborationStore.notifyWorkflowOpened(this.workflowsStore.workflowId);
|
||||
}, 5 * TIME.SECOND);
|
||||
e.returnValue = true; //Gecko + IE
|
||||
return true; //Gecko + Webkit, Safari, Chrome etc.
|
||||
} else {
|
||||
this.startLoading(this.$locale.baseText('nodeView.redirecting'));
|
||||
this.collaborationStore.notifyWorkflowClosed(this.workflowsStore.workflowId);
|
||||
return;
|
||||
}
|
||||
},
|
||||
onUnload() {
|
||||
// This will fire if users decides to leave the page after prompted
|
||||
// Clear the interval to prevent the notification from being sent
|
||||
clearTimeout(this.unloadTimeout);
|
||||
},
|
||||
async newWorkflow(): Promise<void> {
|
||||
this.startLoading();
|
||||
this.resetWorkspace();
|
||||
@@ -3159,6 +3177,7 @@ export default defineComponent({
|
||||
document.addEventListener('keyup', this.keyUp);
|
||||
|
||||
window.addEventListener('beforeunload', this.onBeforeUnload);
|
||||
window.addEventListener('unload', this.onUnload);
|
||||
},
|
||||
getOutputEndpointUUID(
|
||||
nodeName: string,
|
||||
|
||||
Reference in New Issue
Block a user