feat: Adds support for draft in conversation reply box (#4205)

* Add draft support

* Fixes issue with draft loading

* Adds draft for private notes

* Use localstorage helper

* .remove instead of .clear

* Remove timestamp

* clearLocalStorageOnLogout

* Fix draft save on refresh

* Remove usage of delete operator

* Adds autosave for draft messages

* Remove setinterval and add debounce

* Removes draft redundancy check

* Adds test cases for debouncer

* Update app/javascript/shared/helpers/specs/TimeHelpers.spec.js

Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>

* Update app/javascript/shared/helpers/specs/TimeHelpers.spec.js

Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>

* Review fixes

* Fixes issue with debouncer

* FIxes debouncer issue

* Fixes issue with draft empty message

* Removes empty keys from local storage drafts

* Fixes error with empty draft

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
Nithin David Thomas
2022-04-07 22:16:45 +05:30
committed by GitHub
parent dfb56f6bb8
commit 5ea0436051
10 changed files with 202 additions and 24 deletions

View File

@@ -137,6 +137,7 @@ import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
import { MAXIMUM_FILE_UPLOAD_SIZE } from 'shared/constants/messages';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { debounce } from 'shared/helpers/TimeHelpers';
import {
isEscape,
@@ -149,6 +150,8 @@ import inboxMixin from 'shared/mixins/inboxMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { DirectUpload } from 'activestorage';
import { frontendURL } from '../../../helper/URLHelper';
import { LocalStorage, LOCAL_STORAGE_KEYS } from '../../../helper/localStorage';
import { trimMessage } from '../../../store/modules/conversations/helpers';
export default {
components: {
@@ -201,6 +204,7 @@ export default {
hasSlashCommand: false,
bccEmails: '',
ccEmails: '',
doAutoSaveDraft: () => {},
};
},
computed: {
@@ -406,10 +410,19 @@ export default {
profilePath() {
return frontendURL(`accounts/${this.accountId}/profile/settings`);
},
conversationId() {
return this.currentChat.id;
},
},
watch: {
currentChat(conversation) {
currentChat(conversation, oldConversation) {
const { can_reply: canReply } = conversation;
if (oldConversation.id !== conversation.id) {
this.setToDraft(oldConversation.id, this.replyType);
this.getFromDraft();
}
if (this.isOnPrivateNote) {
return;
}
@@ -434,22 +447,87 @@ export default {
this.mentionSearchKey = '';
this.showMentions = false;
}
this.doAutoSaveDraft();
},
replyType(updatedReplyType, oldReplyType) {
this.setToDraft(this.conversationId, oldReplyType);
this.getFromDraft();
},
},
mounted() {
this.getFromDraft();
// Donot use the keyboard listener mixin here as the events here are supposed to be
// working even if input/textarea is focussed.
document.addEventListener('keydown', this.handleKeyEvents);
document.addEventListener('paste', this.onPaste);
this.setCCEmailFromLastChat();
this.doAutoSaveDraft = debounce(
() => {
this.saveDraft(this.conversationId, this.replyType);
},
5000,
false
);
},
destroyed() {
document.removeEventListener('keydown', this.handleKeyEvents);
document.removeEventListener('paste', this.onPaste);
},
methods: {
getSavedDraftMessages() {
return LocalStorage.get(LOCAL_STORAGE_KEYS.DRAFT_MESSAGES) || {};
},
saveDraft(conversationId, replyType) {
if (this.message || this.message === '') {
const savedDraftMessages = this.getSavedDraftMessages();
const key = `draft-${conversationId}-${replyType}`;
const draftToSave = trimMessage(this.message || '');
const {
[key]: currentDraft,
...restOfDraftMessages
} = savedDraftMessages;
const updatedDraftMessages = draftToSave
? {
...restOfDraftMessages,
[key]: draftToSave,
}
: restOfDraftMessages;
LocalStorage.set(
LOCAL_STORAGE_KEYS.DRAFT_MESSAGES,
updatedDraftMessages
);
}
},
setToDraft(conversationId, replyType) {
this.saveDraft(conversationId, replyType);
this.message = '';
},
getFromDraft() {
if (this.conversationId) {
try {
const key = `draft-${this.conversationId}-${this.replyType}`;
const savedDraftMessages = this.getSavedDraftMessages();
this.message = `${savedDraftMessages[key] || ''}`;
} catch (error) {
this.message = '';
}
}
},
removeFromDraft() {
if (this.conversationId) {
const key = `draft-${this.conversationId}-${this.replyType}`;
const draftMessages = this.getSavedDraftMessages();
const { [key]: toBeRemoved, ...updatedDraftMessages } = draftMessages;
LocalStorage.set(
LOCAL_STORAGE_KEYS.DRAFT_MESSAGES,
updatedDraftMessages
);
}
},
onPaste(e) {
const data = e.clipboardData.files;
if (!this.showRichContentEditor && data.length !== 0) {
@@ -537,6 +615,7 @@ export default {
messagePayload
);
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
this.removeFromDraft();
} catch (error) {
const errorMessage =
error?.response?.data?.error ||
@@ -608,6 +687,7 @@ export default {
},
onBlur() {
this.isFocused = false;
this.saveDraft(this.conversationId, this.replyType);
},
onFocus() {
this.isFocused = true;