feat: add media view for contacts (#14393)
This commit is contained in:
@@ -2,12 +2,12 @@ import {
|
||||
DuplicateContactException,
|
||||
ExceptionWithMessage,
|
||||
} from 'shared/helpers/CustomErrors';
|
||||
import types from '../../mutation-types';
|
||||
import ContactAPI from '../../../api/contacts';
|
||||
import snakecaseKeys from 'snakecase-keys';
|
||||
import AccountActionsAPI from '../../../api/accountActions';
|
||||
import ContactAPI from '../../../api/contacts';
|
||||
import AnalyticsHelper from '../../../helper/AnalyticsHelper';
|
||||
import { CONTACTS_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
import types from '../../mutation-types';
|
||||
|
||||
const buildContactFormData = contactParams => {
|
||||
const formData = new FormData();
|
||||
@@ -114,6 +114,19 @@ export const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
fetchAttachments: async ({ commit }, id) => {
|
||||
commit(types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true });
|
||||
try {
|
||||
const response = await ContactAPI.getAttachments(id);
|
||||
commit(types.SET_CONTACT_ATTACHMENTS, {
|
||||
id,
|
||||
data: response.data.payload,
|
||||
});
|
||||
} finally {
|
||||
commit(types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false });
|
||||
}
|
||||
},
|
||||
|
||||
update: async ({ commit }, { id, isFormData = false, ...contactParams }) => {
|
||||
const { avatar, customAttributes, ...paramsToDecamelize } = contactParams;
|
||||
const decamelizedContactParams = {
|
||||
|
||||
@@ -24,6 +24,7 @@ export const getters = {
|
||||
stopPaths: ['custom_attributes'],
|
||||
});
|
||||
},
|
||||
getContactAttachments: $state => id => $state.records[id]?.attachments || [],
|
||||
getMeta: $state => {
|
||||
return $state.meta;
|
||||
},
|
||||
|
||||
@@ -58,7 +58,15 @@ export const mutations = {
|
||||
},
|
||||
|
||||
[types.EDIT_CONTACT]: ($state, data) => {
|
||||
$state.records[data.id] = data;
|
||||
const existingAttachments = $state.records[data.id]?.attachments;
|
||||
$state.records[data.id] = existingAttachments
|
||||
? { ...data, attachments: existingAttachments }
|
||||
: data;
|
||||
},
|
||||
|
||||
[types.SET_CONTACT_ATTACHMENTS]: ($state, { id, data }) => {
|
||||
if (!$state.records[id]) $state.records[id] = {};
|
||||
$state.records[id].attachments = data;
|
||||
},
|
||||
|
||||
[types.DELETE_CONTACT]: ($state, id) => {
|
||||
|
||||
@@ -438,4 +438,48 @@ describe('#actions', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#fetchAttachments', () => {
|
||||
const attachments = [
|
||||
{ id: 11, message_id: 21, file_type: 'image' },
|
||||
{ id: 12, message_id: 22, file_type: 'file' },
|
||||
];
|
||||
|
||||
it('fetches and stores attachments on the contact record', async () => {
|
||||
axios.get.mockResolvedValue({ data: { payload: attachments } });
|
||||
const state = { records: { 1: { id: 1 } } };
|
||||
await actions.fetchAttachments({ commit, state }, 1);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
|
||||
[types.SET_CONTACT_ATTACHMENTS, { id: 1, data: attachments }],
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('refetches even when attachments are already cached', async () => {
|
||||
axios.get.mockResolvedValue({ data: { payload: attachments } });
|
||||
const state = {
|
||||
records: { 1: { id: 1, attachments: [{ id: 99 }] } },
|
||||
};
|
||||
await actions.fetchAttachments({ commit, state }, 1);
|
||||
expect(axios.get).toHaveBeenCalled();
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
|
||||
[types.SET_CONTACT_ATTACHMENTS, { id: 1, data: attachments }],
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('clears the loading flag and rethrows when the API errors', async () => {
|
||||
axios.get.mockRejectedValue(new Error('Network error'));
|
||||
const state = { records: { 1: { id: 1 } } };
|
||||
await expect(
|
||||
actions.fetchAttachments({ commit, state }, 1)
|
||||
).rejects.toThrow('Network error');
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,4 +50,22 @@ describe('#getters', () => {
|
||||
};
|
||||
expect(getters.getAppliedContactFilters(state)).toEqual(filters);
|
||||
});
|
||||
|
||||
describe('getContactAttachments', () => {
|
||||
it('returns the attachments stored on the contact record', () => {
|
||||
const data = [{ id: 11, file_type: 'image' }];
|
||||
const state = { records: { 1: { id: 1, attachments: data } } };
|
||||
expect(getters.getContactAttachments(state)(1)).toEqual(data);
|
||||
});
|
||||
|
||||
it('returns an empty array when the contact has no cached attachments', () => {
|
||||
const state = { records: { 1: { id: 1 } } };
|
||||
expect(getters.getContactAttachments(state)(1)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns an empty array when the contact is not in the store', () => {
|
||||
const state = { records: {} };
|
||||
expect(getters.getContactAttachments(state)(99)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,6 +63,21 @@ describe('#mutations', () => {
|
||||
1: { id: 1, name: 'contact2', email: 'contact2@chatwoot.com' },
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves a cached attachments list across edits', () => {
|
||||
const attachments = [{ id: 11, file_type: 'image' }];
|
||||
const state = {
|
||||
records: {
|
||||
1: { id: 1, name: 'contact1', attachments },
|
||||
},
|
||||
};
|
||||
mutations[types.EDIT_CONTACT](state, { id: 1, name: 'contact2' });
|
||||
expect(state.records[1]).toEqual({
|
||||
id: 1,
|
||||
name: 'contact2',
|
||||
attachments,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#SET_CONTACT_FILTERS', () => {
|
||||
@@ -102,4 +117,33 @@ describe('#mutations', () => {
|
||||
expect(state.appliedFilters).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#SET_CONTACT_ATTACHMENTS', () => {
|
||||
it('attaches the list to the existing contact record', () => {
|
||||
const state = { records: { 1: { id: 1, name: 'Sivin' } } };
|
||||
const data = [{ id: 11, file_type: 'image' }];
|
||||
mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 1, data });
|
||||
expect(state.records[1]).toEqual({
|
||||
id: 1,
|
||||
name: 'Sivin',
|
||||
attachments: data,
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a record shell when the contact is not yet loaded', () => {
|
||||
const state = { records: {} };
|
||||
const data = [{ id: 12, file_type: 'file' }];
|
||||
mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 5, data });
|
||||
expect(state.records[5]).toEqual({ attachments: data });
|
||||
});
|
||||
|
||||
it('replaces an existing attachment list', () => {
|
||||
const state = {
|
||||
records: { 1: { id: 1, attachments: [{ id: 99 }] } },
|
||||
};
|
||||
const data = [{ id: 11 }];
|
||||
mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 1, data });
|
||||
expect(state.records[1].attachments).toEqual(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user