fix: Retry message not working if the conversation has an external issue (#8529)
This commit is contained in:
@@ -86,6 +86,12 @@ class MessageApi extends ApiClient {
|
||||
return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
|
||||
}
|
||||
|
||||
retry(conversationID, messageId) {
|
||||
return axios.post(
|
||||
`${this.url}/${conversationID}/messages/${messageId}/retry`
|
||||
);
|
||||
}
|
||||
|
||||
getPreviousMessages({ conversationId, after, before }) {
|
||||
const params = { before };
|
||||
if (after && Number(after) !== Number(before)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<li v-if="shouldRenderMessage" :id="`message${data.id}`" :class="alignBubble">
|
||||
<div :class="wrapClass">
|
||||
<div v-if="isFailed" class="message-failed--alert">
|
||||
<div v-if="isFailed && !hasOneDayPassed" class="message-failed--alert">
|
||||
<woot-button
|
||||
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
|
||||
size="tiny"
|
||||
@@ -148,6 +148,7 @@ import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import { getDayDifferenceFromNow } from 'shared/helpers/DateHelper';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -209,6 +210,10 @@ export default {
|
||||
created_at: this.data.created_at || '',
|
||||
}));
|
||||
},
|
||||
hasOneDayPassed() {
|
||||
// Disable retry button if the message is failed and the message is older than 24 hours
|
||||
return getDayDifferenceFromNow(new Date(), this.data?.created_at) >= 1;
|
||||
},
|
||||
shouldRenderMessage() {
|
||||
return (
|
||||
this.hasAttachments ||
|
||||
|
||||
@@ -11,6 +11,19 @@ import {
|
||||
} from './helpers/actionHelpers';
|
||||
import messageReadActions from './actions/messageReadActions';
|
||||
import messageTranslateActions from './actions/messageTranslateActions';
|
||||
|
||||
export const hasMessageFailedWithExternalError = pendingMessage => {
|
||||
// This helper is used to check if the message has failed with an external error.
|
||||
// We have two cases
|
||||
// 1. Messages that fail from the UI itself (due to large attachments or a failed network):
|
||||
// In this case, the message will have a status of failed but no external error. So we need to create that message again
|
||||
// 2. Messages sent from Chatwoot but failed to deliver to the customer for some reason (user blocking or client system down):
|
||||
// In this case, the message will have a status of failed and an external error. So we need to retry that message
|
||||
const { content_attributes: contentAttributes, status } = pendingMessage;
|
||||
const externalError = contentAttributes?.external_error ?? '';
|
||||
return status === MESSAGE_STATUS.FAILED && externalError !== '';
|
||||
};
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getConversation: async ({ commit }, conversationId) => {
|
||||
@@ -242,12 +255,15 @@ const actions = {
|
||||
},
|
||||
|
||||
sendMessageWithData: async ({ commit }, pendingMessage) => {
|
||||
const { conversation_id: conversationId, id } = pendingMessage;
|
||||
try {
|
||||
commit(types.ADD_MESSAGE, {
|
||||
...pendingMessage,
|
||||
status: MESSAGE_STATUS.PROGRESS,
|
||||
});
|
||||
const response = await MessageApi.create(pendingMessage);
|
||||
const response = hasMessageFailedWithExternalError(pendingMessage)
|
||||
? await MessageApi.retry(conversationId, id)
|
||||
: await MessageApi.create(pendingMessage);
|
||||
commit(types.ADD_MESSAGE, {
|
||||
...response.data,
|
||||
status: MESSAGE_STATUS.SENT,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import actions from '../../conversations/actions';
|
||||
import actions, {
|
||||
hasMessageFailedWithExternalError,
|
||||
} from '../../conversations/actions';
|
||||
import types from '../../../mutation-types';
|
||||
const dataToSend = {
|
||||
payload: [
|
||||
@@ -18,6 +20,41 @@ const dispatch = jest.fn();
|
||||
global.axios = axios;
|
||||
jest.mock('axios');
|
||||
|
||||
describe('#hasMessageFailedWithExternalError', () => {
|
||||
it('returns false if message is sent', () => {
|
||||
const pendingMessage = {
|
||||
status: 'sent',
|
||||
content_attributes: {},
|
||||
};
|
||||
expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false);
|
||||
});
|
||||
it('returns false if status is not failed', () => {
|
||||
const pendingMessage = {
|
||||
status: 'progress',
|
||||
content_attributes: {},
|
||||
};
|
||||
expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if status is failed but no external error', () => {
|
||||
const pendingMessage = {
|
||||
status: 'failed',
|
||||
content_attributes: {},
|
||||
};
|
||||
expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if status is failed and has external error', () => {
|
||||
const pendingMessage = {
|
||||
status: 'failed',
|
||||
content_attributes: {
|
||||
external_error: 'error',
|
||||
},
|
||||
};
|
||||
expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#actions', () => {
|
||||
describe('#getConversation', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
import format from 'date-fns/format';
|
||||
import isToday from 'date-fns/isToday';
|
||||
import isYesterday from 'date-fns/isYesterday';
|
||||
import { endOfDay, getUnixTime, startOfDay } from 'date-fns';
|
||||
import { endOfDay, getUnixTime, startOfDay, differenceInDays } from 'date-fns';
|
||||
|
||||
export const formatUnixDate = (date, dateFormat = 'MMM dd, yyyy') => {
|
||||
const unixDate = fromUnixTime(date);
|
||||
@@ -45,3 +45,8 @@ export const generateRelativeTime = (value, unit, languageCode) => {
|
||||
});
|
||||
return rtf.format(value, unit);
|
||||
};
|
||||
|
||||
export const getDayDifferenceFromNow = (now, timestampInSeconds) => {
|
||||
const date = new Date(timestampInSeconds * 1000);
|
||||
return differenceInDays(now, date);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
formatDigitToString,
|
||||
isTimeAfter,
|
||||
generateRelativeTime,
|
||||
getDayDifferenceFromNow,
|
||||
} from '../DateHelper';
|
||||
|
||||
describe('#DateHelper', () => {
|
||||
@@ -120,3 +121,25 @@ describe('generateRelativeTime', () => {
|
||||
expect(actualResult).toBe(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getDayDifferenceFromNow', () => {
|
||||
it('should return the difference if in same day', () => {
|
||||
const now = new Date('2023-12-08T00:00:00.000Z');
|
||||
const timestampInSeconds = 1702020305; // 08/12/2023, 12:55:05 (GMT+05:30)
|
||||
const expectedResult = 0;
|
||||
|
||||
const actualResult = getDayDifferenceFromNow(now, timestampInSeconds);
|
||||
|
||||
expect(actualResult).toBe(expectedResult);
|
||||
});
|
||||
|
||||
it('should return the difference if in different day', () => {
|
||||
const now = new Date('2023-12-11T00:00:00.000Z');
|
||||
const timestampInSeconds = 1702020305; // 08/12/2023, 12:55:05 (GMT+05:30)
|
||||
const expectedResult = 2;
|
||||
|
||||
const actualResult = getDayDifferenceFromNow(now, timestampInSeconds);
|
||||
|
||||
expect(actualResult).toBe(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user