mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-03 12:37:56 +00:00 
			
		
		
		
	chore: Refactor messages to support right click context menu (#6748)
This commit is contained in:
		@@ -21,8 +21,8 @@ class MessageFinder
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def current_messages
 | 
					  def current_messages
 | 
				
			||||||
    if @params[:after].present?
 | 
					    if @params[:after].present? && @params[:before].present?
 | 
				
			||||||
      messages.reorder('created_at asc').where('id >= ?', @params[:before].to_i).limit(20)
 | 
					      messages.reorder('created_at asc').where('id >= ? AND id < ?', @params[:after].to_i, @params[:before].to_i).limit(1000)
 | 
				
			||||||
    elsif @params[:before].present?
 | 
					    elsif @params[:before].present?
 | 
				
			||||||
      messages.reorder('created_at desc').where('id < ?', @params[:before].to_i).limit(20).reverse
 | 
					      messages.reorder('created_at desc').where('id < ?', @params[:before].to_i).limit(20).reverse
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,10 +75,12 @@ class MessageApi extends ApiClient {
 | 
				
			|||||||
    return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
 | 
					    return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getPreviousMessages({ conversationId, before }) {
 | 
					  getPreviousMessages({ conversationId, after, before }) {
 | 
				
			||||||
    return axios.get(`${this.url}/${conversationId}/messages`, {
 | 
					    const params = { before };
 | 
				
			||||||
      params: { before },
 | 
					    if (after && Number(after) !== Number(before)) {
 | 
				
			||||||
    });
 | 
					      params.after = after;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return axios.get(`${this.url}/${conversationId}/messages`, { params });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  translateMessage(conversationId, messageId, targetLanguage) {
 | 
					  translateMessage(conversationId, messageId, targetLanguage) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,39 +73,12 @@
 | 
				
			|||||||
          :created-at="createdAt"
 | 
					          :created-at="createdAt"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <woot-modal
 | 
					      <translate-modal
 | 
				
			||||||
        v-if="showTranslateModal"
 | 
					        v-if="showTranslateModal"
 | 
				
			||||||
        modal-type="right-aligned"
 | 
					        :content="data.content"
 | 
				
			||||||
        show
 | 
					        :content-attributes="contentAttributes"
 | 
				
			||||||
        :on-close="onCloseTranslateModal"
 | 
					        @close="onCloseTranslateModal"
 | 
				
			||||||
      >
 | 
					      />
 | 
				
			||||||
        <div class="column content">
 | 
					 | 
				
			||||||
          <p>
 | 
					 | 
				
			||||||
            <b>{{ $t('TRANSLATE_MODAL.ORIGINAL_CONTENT') }}</b>
 | 
					 | 
				
			||||||
          </p>
 | 
					 | 
				
			||||||
          <p v-dompurify-html="data.content" />
 | 
					 | 
				
			||||||
          <br />
 | 
					 | 
				
			||||||
          <hr />
 | 
					 | 
				
			||||||
          <div v-if="translationsAvailable">
 | 
					 | 
				
			||||||
            <p>
 | 
					 | 
				
			||||||
              <b>{{ $t('TRANSLATE_MODAL.TRANSLATED_CONTENT') }}</b>
 | 
					 | 
				
			||||||
            </p>
 | 
					 | 
				
			||||||
            <div
 | 
					 | 
				
			||||||
              v-for="(translation, language) in translations"
 | 
					 | 
				
			||||||
              :key="language"
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <p>
 | 
					 | 
				
			||||||
                <strong>{{ language }}:</strong>
 | 
					 | 
				
			||||||
              </p>
 | 
					 | 
				
			||||||
              <p v-dompurify-html="translation" />
 | 
					 | 
				
			||||||
              <br />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <p v-else>
 | 
					 | 
				
			||||||
            {{ $t('TRANSLATE_MODAL.NO_TRANSLATIONS_AVAILABLE') }}
 | 
					 | 
				
			||||||
          </p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </woot-modal>
 | 
					 | 
				
			||||||
      <spinner v-if="isPending" size="tiny" />
 | 
					      <spinner v-if="isPending" size="tiny" />
 | 
				
			||||||
      <div
 | 
					      <div
 | 
				
			||||||
        v-if="showAvatar"
 | 
					        v-if="showAvatar"
 | 
				
			||||||
@@ -173,6 +146,7 @@ import contentTypeMixin from 'shared/mixins/contentTypeMixin';
 | 
				
			|||||||
import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
 | 
					import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
 | 
				
			||||||
import { generateBotMessageContent } from './helpers/botMessageContentHelper';
 | 
					import { generateBotMessageContent } from './helpers/botMessageContentHelper';
 | 
				
			||||||
import { mapGetters } from 'vuex';
 | 
					import { mapGetters } from 'vuex';
 | 
				
			||||||
 | 
					import TranslateModal from './bubble/TranslateModal.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
@@ -188,6 +162,7 @@ export default {
 | 
				
			|||||||
    ContextMenu,
 | 
					    ContextMenu,
 | 
				
			||||||
    Spinner,
 | 
					    Spinner,
 | 
				
			||||||
    instagramImageErrorPlaceholder,
 | 
					    instagramImageErrorPlaceholder,
 | 
				
			||||||
 | 
					    TranslateModal,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mixins: [alertMixin, messageFormatterMixin, contentTypeMixin],
 | 
					  mixins: [alertMixin, messageFormatterMixin, contentTypeMixin],
 | 
				
			||||||
  props: {
 | 
					  props: {
 | 
				
			||||||
@@ -239,9 +214,6 @@ export default {
 | 
				
			|||||||
      } = this.contentAttributes.email || {};
 | 
					      } = this.contentAttributes.email || {};
 | 
				
			||||||
      return fullHTMLContent || fullTextContent || '';
 | 
					      return fullHTMLContent || fullTextContent || '';
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    translations() {
 | 
					 | 
				
			||||||
      return this.contentAttributes.translations || {};
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    displayQuotedButton() {
 | 
					    displayQuotedButton() {
 | 
				
			||||||
      if (this.emailMessageContent.includes('<blockquote')) {
 | 
					      if (this.emailMessageContent.includes('<blockquote')) {
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
@@ -253,9 +225,6 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    translationsAvailable() {
 | 
					 | 
				
			||||||
      return !!Object.keys(this.translations).length;
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    message() {
 | 
					    message() {
 | 
				
			||||||
      // If the message is an email, emailMessageContent would be present
 | 
					      // If the message is an email, emailMessageContent would be present
 | 
				
			||||||
      // In that case, we would use letter package to render the email
 | 
					      // In that case, we would use letter package to render the email
 | 
				
			||||||
@@ -612,18 +581,10 @@ export default {
 | 
				
			|||||||
  margin-top: var(--space-smaller) var(--space-smaller) 0 0;
 | 
					  margin-top: var(--space-smaller) var(--space-smaller) 0 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.button--delete-message {
 | 
					 | 
				
			||||||
  visibility: hidden;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
li.left,
 | 
					li.left,
 | 
				
			||||||
li.right {
 | 
					li.right {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: flex-end;
 | 
					  align-items: flex-end;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  &:hover .button--delete-message {
 | 
					 | 
				
			||||||
    visibility: visible;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
li.left.has-tweet-menu .context-menu {
 | 
					li.left.has-tweet-menu .context-menu {
 | 
				
			||||||
@@ -652,9 +613,6 @@ li.right {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.has-context-menu {
 | 
					.has-context-menu {
 | 
				
			||||||
  background: var(--color-background);
 | 
					  background: var(--color-background);
 | 
				
			||||||
  .button--delete-message {
 | 
					 | 
				
			||||||
    visibility: visible;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.context-menu {
 | 
					.context-menu {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <woot-modal
 | 
				
			||||||
 | 
					    modal-type="right-aligned"
 | 
				
			||||||
 | 
					    class="text-left"
 | 
				
			||||||
 | 
					    show
 | 
				
			||||||
 | 
					    :on-close="onClose"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <div class="column content">
 | 
				
			||||||
 | 
					      <p>
 | 
				
			||||||
 | 
					        <b>{{ $t('TRANSLATE_MODAL.ORIGINAL_CONTENT') }}</b>
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p v-dompurify-html="content" />
 | 
				
			||||||
 | 
					      <br />
 | 
				
			||||||
 | 
					      <hr />
 | 
				
			||||||
 | 
					      <div v-if="translationsAvailable">
 | 
				
			||||||
 | 
					        <p>
 | 
				
			||||||
 | 
					          <b>{{ $t('TRANSLATE_MODAL.TRANSLATED_CONTENT') }}</b>
 | 
				
			||||||
 | 
					        </p>
 | 
				
			||||||
 | 
					        <div v-for="(translation, language) in translations" :key="language">
 | 
				
			||||||
 | 
					          <p>
 | 
				
			||||||
 | 
					            <strong>{{ language }}:</strong>
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					          <p v-dompurify-html="translation" />
 | 
				
			||||||
 | 
					          <br />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <p v-else>
 | 
				
			||||||
 | 
					        {{ $t('TRANSLATE_MODAL.NO_TRANSLATIONS_AVAILABLE') }}
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </woot-modal>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    contentAttributes: {
 | 
				
			||||||
 | 
					      type: Object,
 | 
				
			||||||
 | 
					      default: () => ({}),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    content: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    translationsAvailable() {
 | 
				
			||||||
 | 
					      return !!Object.keys(this.translations).length;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    translations() {
 | 
				
			||||||
 | 
					      return this.contentAttributes.translations || {};
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    onClose() {
 | 
				
			||||||
 | 
					      this.$emit('close');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="menu" @click.stop="$emit('click')">
 | 
					  <div class="menu" role="button" @click.stop="$emit('click')">
 | 
				
			||||||
    <fluent-icon
 | 
					    <fluent-icon
 | 
				
			||||||
      v-if="variant === 'icon' && option.icon"
 | 
					      v-if="variant === 'icon' && option.icon"
 | 
				
			||||||
      :icon="option.icon"
 | 
					      :icon="option.icon"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ export default {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.disabled {
 | 
					  &.disabled {
 | 
				
			||||||
    opacity: 50%;
 | 
					    opacity: 0.5;
 | 
				
			||||||
    cursor: not-allowed;
 | 
					    cursor: not-allowed;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ const actions = {
 | 
				
			|||||||
        id: data.conversationId,
 | 
					        id: data.conversationId,
 | 
				
			||||||
        data: payload,
 | 
					        data: payload,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      if (payload.length < 20) {
 | 
					      if (!payload.length) {
 | 
				
			||||||
        commit(types.SET_ALL_MESSAGES_LOADED);
 | 
					        commit(types.SET_ALL_MESSAGES_LOADED);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
@@ -217,9 +217,7 @@ const actions = {
 | 
				
			|||||||
    { conversationId, messageId }
 | 
					    { conversationId, messageId }
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const response = await MessageApi.delete(conversationId, messageId);
 | 
					      const { data } = await MessageApi.delete(conversationId, messageId);
 | 
				
			||||||
      const { data } = response;
 | 
					 | 
				
			||||||
      // The delete message is actually deleting the content.
 | 
					 | 
				
			||||||
      commit(types.ADD_MESSAGE, data);
 | 
					      commit(types.ADD_MESSAGE, data);
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      throw new Error(error);
 | 
					      throw new Error(error);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user