mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-03 20:48:07 +00:00 
			
		
		
		
	feat: Ability to unmute muted conversations (#1319)
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							2aad33a5be
						
					
				
				
					commit
					ecebe163e1
				
			@@ -32,6 +32,11 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
 | 
				
			|||||||
    head :ok
 | 
					    head :ok
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def unmute
 | 
				
			||||||
 | 
					    @conversation.unmute!
 | 
				
			||||||
 | 
					    head :ok
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def transcript
 | 
					  def transcript
 | 
				
			||||||
    ConversationReplyMailer.conversation_transcript(@conversation, params[:email])&.deliver_later if params[:email].present?
 | 
					    ConversationReplyMailer.conversation_transcript(@conversation, params[:email])&.deliver_later if params[:email].present?
 | 
				
			||||||
    head :ok
 | 
					    head :ok
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,10 @@ class ConversationApi extends ApiClient {
 | 
				
			|||||||
    return axios.post(`${this.url}/${conversationId}/mute`);
 | 
					    return axios.post(`${this.url}/${conversationId}/mute`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unmute(conversationId) {
 | 
				
			||||||
 | 
					    return axios.post(`${this.url}/${conversationId}/unmute`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  meta({ inboxId, status, assigneeType, labels }) {
 | 
					  meta({ inboxId, status, assigneeType, labels }) {
 | 
				
			||||||
    return axios.get(`${this.url}/meta`, {
 | 
					    return axios.get(`${this.url}/meta`, {
 | 
				
			||||||
      params: {
 | 
					      params: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,32 @@ describe('#ConversationAPI', () => {
 | 
				
			|||||||
    expect(conversationAPI).toHaveProperty('markMessageRead');
 | 
					    expect(conversationAPI).toHaveProperty('markMessageRead');
 | 
				
			||||||
    expect(conversationAPI).toHaveProperty('toggleTyping');
 | 
					    expect(conversationAPI).toHaveProperty('toggleTyping');
 | 
				
			||||||
    expect(conversationAPI).toHaveProperty('mute');
 | 
					    expect(conversationAPI).toHaveProperty('mute');
 | 
				
			||||||
 | 
					    expect(conversationAPI).toHaveProperty('unmute');
 | 
				
			||||||
    expect(conversationAPI).toHaveProperty('meta');
 | 
					    expect(conversationAPI).toHaveProperty('meta');
 | 
				
			||||||
    expect(conversationAPI).toHaveProperty('sendEmailTranscript');
 | 
					    expect(conversationAPI).toHaveProperty('sendEmailTranscript');
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('API calls', () => {
 | 
				
			||||||
 | 
					    let originalAxios = null;
 | 
				
			||||||
 | 
					    let axiosMock = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    beforeEach(() => {
 | 
				
			||||||
 | 
					      originalAxios = window.axios;
 | 
				
			||||||
 | 
					      axiosMock = { post: jest.fn(() => Promise.resolve()) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      window.axios = axiosMock;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    afterEach(() => {
 | 
				
			||||||
 | 
					      window.axios = originalAxios;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('#unmute', () => {
 | 
				
			||||||
 | 
					      conversationAPI.unmute(45);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(axiosMock.post).toHaveBeenCalledWith(
 | 
				
			||||||
 | 
					        '/api/v1/conversations/45/unmute'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,15 @@
 | 
				
			|||||||
      >
 | 
					      >
 | 
				
			||||||
        <span>{{ $t('CONTACT_PANEL.MUTE_CONTACT') }}</span>
 | 
					        <span>{{ $t('CONTACT_PANEL.MUTE_CONTACT') }}</span>
 | 
				
			||||||
      </button>
 | 
					      </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <button
 | 
				
			||||||
 | 
					        v-else
 | 
				
			||||||
 | 
					        class="button small clear row alert small-6 action--button"
 | 
				
			||||||
 | 
					        @click="unmute"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <span>{{ $t('CONTACT_PANEL.UNMUTE_CONTACT') }}</span>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <button
 | 
					      <button
 | 
				
			||||||
        class="button small clear row small-6 action--button"
 | 
					        class="button small clear row small-6 action--button"
 | 
				
			||||||
        @click="toggleEmailActionsModal"
 | 
					        @click="toggleEmailActionsModal"
 | 
				
			||||||
@@ -67,6 +76,11 @@ export default {
 | 
				
			|||||||
      this.showAlert(this.$t('CONTACT_PANEL.MUTED_SUCCESS'));
 | 
					      this.showAlert(this.$t('CONTACT_PANEL.MUTED_SUCCESS'));
 | 
				
			||||||
      this.toggleConversationActions();
 | 
					      this.toggleConversationActions();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    unmute() {
 | 
				
			||||||
 | 
					      this.$store.dispatch('unmuteConversation', this.currentChat.id);
 | 
				
			||||||
 | 
					      this.showAlert(this.$t('CONTACT_PANEL.UNMUTED_SUCCESS'));
 | 
				
			||||||
 | 
					      this.toggleConversationActions();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    toggleEmailActionsModal() {
 | 
					    toggleEmailActionsModal() {
 | 
				
			||||||
      this.showEmailActionsModal = !this.showEmailActionsModal;
 | 
					      this.showEmailActionsModal = !this.showEmailActionsModal;
 | 
				
			||||||
      this.hideConversationActions();
 | 
					      this.hideConversationActions();
 | 
				
			||||||
@@ -129,6 +143,7 @@ export default {
 | 
				
			|||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
  padding: var(--space-small) var(--space-smaller);
 | 
					  padding: var(--space-small) var(--space-smaller);
 | 
				
			||||||
  font-size: var(--font-size-small);
 | 
					  font-size: var(--font-size-small);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					import { createLocalVue, mount } from '@vue/test-utils';
 | 
				
			||||||
 | 
					import Vuex from 'vuex';
 | 
				
			||||||
 | 
					import VueI18n from 'vue-i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Button from 'dashboard/components/buttons/Button';
 | 
				
			||||||
 | 
					import i18n from 'dashboard/i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import MoreActions from '../MoreActions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const localVue = createLocalVue();
 | 
				
			||||||
 | 
					localVue.use(Vuex);
 | 
				
			||||||
 | 
					localVue.use(VueI18n);
 | 
				
			||||||
 | 
					localVue.locale('en', i18n.en);
 | 
				
			||||||
 | 
					localVue.component('woot-button', Button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('MoveActions', () => {
 | 
				
			||||||
 | 
					  let currentChat = { id: 8, muted: false };
 | 
				
			||||||
 | 
					  let state = null;
 | 
				
			||||||
 | 
					  let muteConversation = null;
 | 
				
			||||||
 | 
					  let unmuteConversation = null;
 | 
				
			||||||
 | 
					  let modules = null;
 | 
				
			||||||
 | 
					  let getters = null;
 | 
				
			||||||
 | 
					  let store = null;
 | 
				
			||||||
 | 
					  let moreActions = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    window.bus = {
 | 
				
			||||||
 | 
					      $emit: jest.fn(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state = {
 | 
				
			||||||
 | 
					      authenticated: true,
 | 
				
			||||||
 | 
					      currentChat,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    muteConversation = jest.fn(() => Promise.resolve());
 | 
				
			||||||
 | 
					    unmuteConversation = jest.fn(() => Promise.resolve());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    modules = {
 | 
				
			||||||
 | 
					      conversations: {
 | 
				
			||||||
 | 
					        actions: {
 | 
				
			||||||
 | 
					          muteConversation,
 | 
				
			||||||
 | 
					          unmuteConversation,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getters = {
 | 
				
			||||||
 | 
					      getSelectedChat: () => currentChat,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    store = new Vuex.Store({
 | 
				
			||||||
 | 
					      state,
 | 
				
			||||||
 | 
					      modules,
 | 
				
			||||||
 | 
					      getters,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    moreActions = mount(MoreActions, { store, localVue });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('opens the menu when user clicks "more"', async () => {
 | 
				
			||||||
 | 
					    expect(moreActions.find('.dropdown-pane').exists()).toBe(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await moreActions.find('.more--button').trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(moreActions.find('.dropdown-pane').exists()).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('muting discussion', () => {
 | 
				
			||||||
 | 
					    it('triggers "muteConversation"', async () => {
 | 
				
			||||||
 | 
					      await moreActions.find('.more--button').trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await moreActions
 | 
				
			||||||
 | 
					        .find('.dropdown-pane button:first-child')
 | 
				
			||||||
 | 
					        .trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(muteConversation).toBeCalledWith(
 | 
				
			||||||
 | 
					        expect.any(Object),
 | 
				
			||||||
 | 
					        currentChat.id,
 | 
				
			||||||
 | 
					        undefined
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('shows alert', async () => {
 | 
				
			||||||
 | 
					      await moreActions.find('.more--button').trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await moreActions
 | 
				
			||||||
 | 
					        .find('.dropdown-pane button:first-child')
 | 
				
			||||||
 | 
					        .trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(window.bus.$emit).toBeCalledWith(
 | 
				
			||||||
 | 
					        'newToastMessage',
 | 
				
			||||||
 | 
					        'This conversation is muted for 6 hours'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('unmuting discussion', () => {
 | 
				
			||||||
 | 
					    beforeEach(() => {
 | 
				
			||||||
 | 
					      currentChat.muted = true;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('triggers "unmuteConversation"', async () => {
 | 
				
			||||||
 | 
					      await moreActions.find('.more--button').trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await moreActions
 | 
				
			||||||
 | 
					        .find('.dropdown-pane button:first-child')
 | 
				
			||||||
 | 
					        .trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(unmuteConversation).toBeCalledWith(
 | 
				
			||||||
 | 
					        expect.any(Object),
 | 
				
			||||||
 | 
					        currentChat.id,
 | 
				
			||||||
 | 
					        undefined
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('shows alert', async () => {
 | 
				
			||||||
 | 
					      await moreActions.find('.more--button').trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await moreActions
 | 
				
			||||||
 | 
					        .find('.dropdown-pane button:first-child')
 | 
				
			||||||
 | 
					        .trigger('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(window.bus.$emit).toBeCalledWith(
 | 
				
			||||||
 | 
					        'newToastMessage',
 | 
				
			||||||
 | 
					        'This conversation is unmuted'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -32,7 +32,9 @@
 | 
				
			|||||||
      "NO_AVAILABLE_LABELS": "There are no labels added to this conversation."
 | 
					      "NO_AVAILABLE_LABELS": "There are no labels added to this conversation."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "MUTE_CONTACT": "Mute Conversation",
 | 
					    "MUTE_CONTACT": "Mute Conversation",
 | 
				
			||||||
 | 
					    "UNMUTE_CONTACT": "Unmute Conversation",
 | 
				
			||||||
    "MUTED_SUCCESS": "This conversation is muted for 6 hours",
 | 
					    "MUTED_SUCCESS": "This conversation is muted for 6 hours",
 | 
				
			||||||
 | 
					    "UNMUTED_SUCCESS": "This conversation is unmuted",
 | 
				
			||||||
    "SEND_TRANSCRIPT": "Send Transcript",
 | 
					    "SEND_TRANSCRIPT": "Send Transcript",
 | 
				
			||||||
    "EDIT_LABEL": "Edit"
 | 
					    "EDIT_LABEL": "Edit"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -224,6 +224,15 @@ const actions = {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unmuteConversation: async ({ commit }, conversationId) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await ConversationApi.unmute(conversationId);
 | 
				
			||||||
 | 
					      commit(types.default.UNMUTE_CONVERSATION);
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      //
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sendEmailTranscript: async (_, { conversationId, email }) => {
 | 
					  sendEmailTranscript: async (_, { conversationId, email }) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await ConversationApi.sendEmailTranscript({ conversationId, email });
 | 
					      await ConversationApi.sendEmailTranscript({ conversationId, email });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,11 @@ export const mutations = {
 | 
				
			|||||||
    chat.muted = true;
 | 
					    chat.muted = true;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [types.default.UNMUTE_CONVERSATION](_state) {
 | 
				
			||||||
 | 
					    const [chat] = getSelectedChatConversation(_state);
 | 
				
			||||||
 | 
					    chat.muted = false;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [types.default.SEND_MESSAGE](_state, currentMessage) {
 | 
					  [types.default.SEND_MESSAGE](_state, currentMessage) {
 | 
				
			||||||
    const [chat] = getSelectedChatConversation(_state);
 | 
					    const [chat] = getSelectedChatConversation(_state);
 | 
				
			||||||
    const allMessagesExceptCurrent = (chat.messages || []).filter(
 | 
					    const allMessagesExceptCurrent = (chat.messages || []).filter(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ export default {
 | 
				
			|||||||
  ADD_CONVERSATION: 'ADD_CONVERSATION',
 | 
					  ADD_CONVERSATION: 'ADD_CONVERSATION',
 | 
				
			||||||
  UPDATE_CONVERSATION: 'UPDATE_CONVERSATION',
 | 
					  UPDATE_CONVERSATION: 'UPDATE_CONVERSATION',
 | 
				
			||||||
  MUTE_CONVERSATION: 'MUTE_CONVERSATION',
 | 
					  MUTE_CONVERSATION: 'MUTE_CONVERSATION',
 | 
				
			||||||
 | 
					  UNMUTE_CONVERSATION: 'UNMUTE_CONVERSATION',
 | 
				
			||||||
  SEND_MESSAGE: 'SEND_MESSAGE',
 | 
					  SEND_MESSAGE: 'SEND_MESSAGE',
 | 
				
			||||||
  ASSIGN_AGENT: 'ASSIGN_AGENT',
 | 
					  ASSIGN_AGENT: 'ASSIGN_AGENT',
 | 
				
			||||||
  SET_CHAT_META: 'SET_CHAT_META',
 | 
					  SET_CHAT_META: 'SET_CHAT_META',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,6 +89,10 @@ class Conversation < ApplicationRecord
 | 
				
			|||||||
    Redis::Alfred.setex(mute_key, 1, mute_period)
 | 
					    Redis::Alfred.setex(mute_key, 1, mute_period)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def unmute!
 | 
				
			||||||
 | 
					    Redis::Alfred.delete(mute_key)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def muted?
 | 
					  def muted?
 | 
				
			||||||
    !Redis::Alfred.get(mute_key).nil?
 | 
					    !Redis::Alfred.get(mute_key).nil?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,7 @@ Rails.application.routes.draw do
 | 
				
			|||||||
            end
 | 
					            end
 | 
				
			||||||
            member do
 | 
					            member do
 | 
				
			||||||
              post :mute
 | 
					              post :mute
 | 
				
			||||||
 | 
					              post :unmute
 | 
				
			||||||
              post :transcript
 | 
					              post :transcript
 | 
				
			||||||
              post :toggle_status
 | 
					              post :toggle_status
 | 
				
			||||||
              post :toggle_typing_status
 | 
					              post :toggle_typing_status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -246,6 +246,31 @@ RSpec.describe 'Conversations API', type: :request do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe 'POST /api/v1/accounts/{account.id}/conversations/:id/unmute' do
 | 
				
			||||||
 | 
					    let(:conversation) { create(:conversation, account: account).tap(&:mute!) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when it is an unauthenticated user' do
 | 
				
			||||||
 | 
					      it 'returns unauthorized' do
 | 
				
			||||||
 | 
					        post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/unmute"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(response).to have_http_status(:unauthorized)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when it is an authenticated user' do
 | 
				
			||||||
 | 
					      let(:agent) { create(:user, account: account, role: :agent) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'unmutes conversation' do
 | 
				
			||||||
 | 
					        post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/unmute",
 | 
				
			||||||
 | 
					             headers: agent.create_new_auth_token,
 | 
				
			||||||
 | 
					             as: :json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(response).to have_http_status(:success)
 | 
				
			||||||
 | 
					        expect(conversation.reload.muted?).to eq(false)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe 'POST /api/v1/accounts/{account.id}/conversations/:id/transcript' do
 | 
					  describe 'POST /api/v1/accounts/{account.id}/conversations/:id/transcript' do
 | 
				
			||||||
    let(:conversation) { create(:conversation, account: account) }
 | 
					    let(:conversation) { create(:conversation, account: account) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -289,6 +289,22 @@ RSpec.describe Conversation, type: :model do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#unmute!' do
 | 
				
			||||||
 | 
					    subject(:unmute!) { conversation.unmute! }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let(:conversation) { create(:conversation).tap(&:mute!) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it 'does not change conversation status' do
 | 
				
			||||||
 | 
					      expect { unmute! }.not_to(change { conversation.reload.status })
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it 'marks conversation as muted in redis' do
 | 
				
			||||||
 | 
					      expect { unmute! }
 | 
				
			||||||
 | 
					        .to change { Redis::Alfred.get(conversation.send(:mute_key)) }
 | 
				
			||||||
 | 
					        .to nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '#muted?' do
 | 
					  describe '#muted?' do
 | 
				
			||||||
    subject(:muted?) { conversation.muted? }
 | 
					    subject(:muted?) { conversation.muted? }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user