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:
Milorad FIlipović
2023-11-23 10:14:34 +01:00
committed by GitHub
parent 99a9ea497a
commit 77bc8ecd4b
18 changed files with 654 additions and 148 deletions

View File

@@ -56,18 +56,20 @@
<span v-else class="tags"></span>
<PushConnectionTracker class="actions">
<span class="activator">
<span :class="`activator ${$style.group}`">
<WorkflowActivator :workflow-active="isWorkflowActive" :workflow-id="currentWorkflowId" />
</span>
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
<n8n-button
type="secondary"
class="mr-2xs"
@click="onShareButtonClick"
data-test-id="workflow-share-button"
>
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
<div :class="$style.group">
<collaboration-pane />
<n8n-button
type="secondary"
@click="onShareButtonClick"
data-test-id="workflow-share-button"
>
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
</div>
<template #fallback>
<n8n-tooltip>
<n8n-button type="secondary" :class="['mr-2xs', $style.disabledShareButton]">
@@ -94,28 +96,30 @@
</n8n-tooltip>
</template>
</enterprise-edition>
<SaveButton
type="primary"
:saved="!this.isDirty && !this.isNewWorkflow"
:disabled="isWorkflowSaving || readOnly"
data-test-id="workflow-save-button"
@click="onSaveButtonClick"
/>
<router-link
v-if="isWorkflowHistoryFeatureEnabled"
:to="workflowHistoryRoute"
:class="$style.workflowHistoryButton"
>
<n8n-icon-button
:disabled="isWorkflowHistoryButtonDisabled"
data-test-id="workflow-history-button"
type="tertiary"
icon="history"
size="medium"
text
<div :class="$style.group">
<SaveButton
type="primary"
:saved="!this.isDirty && !this.isNewWorkflow"
:disabled="isWorkflowSaving || readOnly"
data-test-id="workflow-save-button"
@click="onSaveButtonClick"
/>
</router-link>
<div :class="$style.workflowMenuContainer">
<router-link
v-if="isWorkflowHistoryFeatureEnabled"
:to="workflowHistoryRoute"
:class="$style.workflowHistoryButton"
>
<n8n-icon-button
:disabled="isWorkflowHistoryButtonDisabled"
data-test-id="workflow-history-button"
type="tertiary"
icon="history"
size="medium"
text
/>
</router-link>
</div>
<div :class="[$style.workflowMenuContainer, $style.group]">
<input
:class="$style.hiddenInput"
type="file"
@@ -159,6 +163,7 @@ import SaveButton from '@/components/SaveButton.vue';
import TagsDropdown from '@/components/TagsDropdown.vue';
import InlineTextEdit from '@/components/InlineTextEdit.vue';
import BreakpointsObserver from '@/components/BreakpointsObserver.vue';
import CollaborationPane from '@/components/MainHeader/CollaborationPane.vue';
import type { IUser, IWorkflowDataUpdate, IWorkflowDb, IWorkflowToShare } from '@/Interface';
import { saveAs } from 'file-saver';
@@ -201,6 +206,7 @@ export default defineComponent({
TagsDropdown,
InlineTextEdit,
BreakpointsObserver,
CollaborationPane,
},
props: {
readOnly: {
@@ -681,7 +687,6 @@ $--header-spacing: 20px;
line-height: $--text-line-height;
display: flex;
align-items: center;
margin-right: 30px;
> span {
margin-right: 5px;
@@ -717,14 +722,15 @@ $--header-spacing: 20px;
.actions {
display: flex;
align-items: center;
gap: var(--spacing-m);
}
</style>
<style module lang="scss">
.workflowMenuContainer {
margin-left: var(--spacing-2xs);
.group {
display: flex;
gap: var(--spacing-xs);
}
.hiddenInput {
display: none;
}
@@ -740,8 +746,6 @@ $--header-spacing: 20px;
.workflowHistoryButton {
width: 30px;
height: 30px;
margin-left: var(--spacing-m);
margin-right: var(--spacing-4xs);
color: var(--color-text-dark);
border-radius: var(--border-radius-base);