diff --git a/application/Espo/Hooks/Note/WebSocketSubmit.php b/application/Espo/Hooks/Note/WebSocketSubmit.php index 139e2a557f..e65a0ed2bc 100644 --- a/application/Espo/Hooks/Note/WebSocketSubmit.php +++ b/application/Espo/Hooks/Note/WebSocketSubmit.php @@ -48,6 +48,12 @@ class WebSocketSubmit implements AfterSave public function afterSave(Entity $entity, SaveOptions $options): void { + if (!$entity->isNew()) { + $updateTopic = "recordUpdate.Note.{$entity->getId()}"; + + $this->webSocketSubmission->submit($updateTopic); + } + $parentId = $entity->getParentId(); $parentType = $entity->getParentType(); diff --git a/application/Espo/Tools/Stream/MyReactionsService.php b/application/Espo/Tools/Stream/MyReactionsService.php index 7c861656b9..e0ce24eb9d 100644 --- a/application/Espo/Tools/Stream/MyReactionsService.php +++ b/application/Espo/Tools/Stream/MyReactionsService.php @@ -147,5 +147,9 @@ class MyReactionsService $topic = "streamUpdate.{$note->getParentType()}.{$note->getParentId()}"; $this->webSocketSubmission->submit($topic, null, ['noteId' => $note->getId()]); + + $topicUpdate = "recordUpdate.Note.{$note->getId()}"; + + $this->webSocketSubmission->submit($topicUpdate); } } diff --git a/client/src/views/note/detail.js b/client/src/views/note/detail.js index 543832c191..3a23e81648 100644 --- a/client/src/views/note/detail.js +++ b/client/src/views/note/detail.js @@ -27,6 +27,9 @@ ************************************************************************/ import MainView from 'views/main'; +import DebounceHelper from 'helpers/util/debounce'; +import {inject} from 'di'; +import WebSocketManager from 'web-socket-manager'; export default class NoteDetailView extends MainView { @@ -40,11 +43,25 @@ export default class NoteDetailView extends MainView { */ isDeleted = false + /** + * @private + * @type {DebounceHelper} + */ + webSocketDebounceHelper + + /** + * @private + * @type {WebSocketManager} + */ + @inject(WebSocketManager) + webSocketManager + setup() { this.scope = this.model.entityType; this.setupHeader(); this.setupRecord(); + this.setupWebSocket(); this.listenToOnce(this.model, 'remove', () => { this.clearView('record'); @@ -75,11 +92,15 @@ export default class NoteDetailView extends MainView { this.collection = await this.getCollectionFactory().create(this.scope); this.collection.add(this.model); - await this.createView('record', 'views/stream/record/list', { + const view = await this.createView('record', 'views/stream/record/list', { selector: '> .record', collection: this.collection, isUserStream: true, }); + + if (this.webSocketDebounceHelper) { + this.listenTo(view, 'before:save', () => this.webSocketDebounceHelper.block()); + } })() ); } @@ -146,4 +167,33 @@ export default class NoteDetailView extends MainView { Espo.Ui.notify(); } + + onRemove() { + super.onRemove(); + + if (this.webSocketManager.isEnabled()) { + this.webSocketManager.unsubscribe(`recordUpdate.Note.${this.model.id}`); + } + } + + setupWebSocket() { + if (!this.webSocketManager.isEnabled()) { + return; + } + + this.webSocketDebounceHelper = new DebounceHelper({ + handler: () => this.handleRecordUpdate(), + }); + + const topic = `recordUpdate.Note.${this.model.id}`; + + this.webSocketManager.subscribe(topic, () => this.webSocketDebounceHelper.process()); + } + + /** + * @private + */ + async handleRecordUpdate() { + await this.model.fetch({highlight: true}); + } } diff --git a/client/src/views/record/list.js b/client/src/views/record/list.js index 92d46e2216..f5d726b983 100644 --- a/client/src/views/record/list.js +++ b/client/src/views/record/list.js @@ -3433,6 +3433,14 @@ class ListRecordView extends View { model: model, rootUrl: rootUrl, editDisabled: this.quickEditDisabled, + beforeSave: m => { + if (!model) { + // @todo Revise. + return; + } + + this.trigger('before:save', m); + }, afterSave: m => { if (!model) { return; @@ -3498,6 +3506,9 @@ class ListRecordView extends View { model: model, fullFormDisabled: data.noFullForm, rootUrl: rootUrl, + beforeSave: m => { + this.trigger('before:save', m); + }, afterSave: m => { const model = this.collection.get(m.id);