Feature: Typing Indicator on widget and dashboard (#811)
* Adds typing indicator for widget * typing indicator for agents in dashboard Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
committed by
GitHub
parent
fabc3170b7
commit
5bc8219db5
@@ -10,6 +10,7 @@ import contactConversations from './modules/contactConversations';
|
||||
import contacts from './modules/contacts';
|
||||
import conversationLabels from './modules/conversationLabels';
|
||||
import conversationMetadata from './modules/conversationMetadata';
|
||||
import conversationTypingStatus from './modules/conversationTypingStatus';
|
||||
import conversationPage from './modules/conversationPage';
|
||||
import conversations from './modules/conversations';
|
||||
import inboxes from './modules/inboxes';
|
||||
@@ -22,22 +23,23 @@ import accounts from './modules/accounts';
|
||||
Vue.use(Vuex);
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
accounts,
|
||||
agents,
|
||||
auth,
|
||||
billing,
|
||||
cannedResponse,
|
||||
Channel,
|
||||
contacts,
|
||||
contactConversations,
|
||||
contacts,
|
||||
conversationLabels,
|
||||
conversationMetadata,
|
||||
conversationPage,
|
||||
conversations,
|
||||
conversationTypingStatus,
|
||||
inboxes,
|
||||
inboxMembers,
|
||||
reports,
|
||||
userNotificationSettings,
|
||||
webhooks,
|
||||
accounts,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import Vue from 'vue';
|
||||
import * as types from '../mutation-types';
|
||||
|
||||
const state = {
|
||||
records: {},
|
||||
};
|
||||
|
||||
export const getters = {
|
||||
getUserList: $state => id => {
|
||||
return $state.records[Number(id)] || [];
|
||||
},
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
create: ({ commit }, { conversationId, user }) => {
|
||||
commit(types.default.ADD_USER_TYPING_TO_CONVERSATION, {
|
||||
conversationId,
|
||||
user,
|
||||
});
|
||||
},
|
||||
destroy: ({ commit }, { conversationId, user }) => {
|
||||
commit(types.default.REMOVE_USER_TYPING_FROM_CONVERSATION, {
|
||||
conversationId,
|
||||
user,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
[types.default.ADD_USER_TYPING_TO_CONVERSATION]: (
|
||||
$state,
|
||||
{ conversationId, user }
|
||||
) => {
|
||||
const records = $state.records[conversationId] || [];
|
||||
const hasUserRecordAlready = !!records.filter(
|
||||
record => record.id === user.id && record.type === user.type
|
||||
).length;
|
||||
if (!hasUserRecordAlready) {
|
||||
Vue.set($state.records, conversationId, [...records, user]);
|
||||
}
|
||||
},
|
||||
[types.default.REMOVE_USER_TYPING_FROM_CONVERSATION]: (
|
||||
$state,
|
||||
{ conversationId, user }
|
||||
) => {
|
||||
const records = $state.records[conversationId] || [];
|
||||
const updatedRecords = records.filter(
|
||||
record => record.id !== user.id || record.type !== user.type
|
||||
);
|
||||
Vue.set($state.records, conversationId, updatedRecords);
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
};
|
||||
@@ -160,10 +160,10 @@ const actions = {
|
||||
commit(types.default.UPDATE_CONVERSATION, conversation);
|
||||
},
|
||||
|
||||
toggleTyping: async ({ commit }, { status, inboxId, contactId }) => {
|
||||
toggleTyping: async ({ commit }, { status, conversationId }) => {
|
||||
try {
|
||||
await FBChannel.toggleTyping({ status, inboxId, contactId });
|
||||
commit(types.default.FB_TYPING, { status });
|
||||
commit(types.default.SET_AGENT_TYPING, { status });
|
||||
await ConversationApi.toggleTyping({ status, conversationId });
|
||||
} catch (error) {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@ import getters, { getSelectedChatConversation } from './getters';
|
||||
import actions from './actions';
|
||||
import wootConstants from '../../../constants';
|
||||
|
||||
const initialSelectedChat = {
|
||||
id: null,
|
||||
meta: {},
|
||||
status: null,
|
||||
seen: false,
|
||||
agentTyping: 'off',
|
||||
dataFetched: false,
|
||||
};
|
||||
const state = {
|
||||
allConversations: [],
|
||||
convTabStats: {
|
||||
@@ -13,14 +21,7 @@ const state = {
|
||||
unAssignedCount: 0,
|
||||
allCount: 0,
|
||||
},
|
||||
selectedChat: {
|
||||
id: null,
|
||||
meta: {},
|
||||
status: null,
|
||||
seen: false,
|
||||
agentTyping: 'off',
|
||||
dataFetched: false,
|
||||
},
|
||||
selectedChat: { ...initialSelectedChat },
|
||||
listLoadingStatus: true,
|
||||
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
|
||||
currentInbox: null,
|
||||
@@ -42,14 +43,7 @@ const mutations = {
|
||||
},
|
||||
[types.default.EMPTY_ALL_CONVERSATION](_state) {
|
||||
_state.allConversations = [];
|
||||
_state.selectedChat = {
|
||||
id: null,
|
||||
meta: {},
|
||||
status: null,
|
||||
seen: false,
|
||||
agentTyping: 'off',
|
||||
dataFetched: false,
|
||||
};
|
||||
_state.selectedChat = { ...initialSelectedChat };
|
||||
},
|
||||
[types.default.SET_ALL_MESSAGES_LOADED](_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
@@ -175,7 +169,7 @@ const mutations = {
|
||||
_state.selectedChat.seen = true;
|
||||
},
|
||||
|
||||
[types.default.FB_TYPING](_state, { status }) {
|
||||
[types.default.SET_AGENT_TYPING](_state, { status }) {
|
||||
_state.selectedChat.agentTyping = status;
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { actions } from '../../conversationTypingStatus';
|
||||
import * as types from '../../../mutation-types';
|
||||
|
||||
const commit = jest.fn();
|
||||
|
||||
describe('#actions', () => {
|
||||
describe('#create', () => {
|
||||
it('sends correct actions', () => {
|
||||
actions.create(
|
||||
{ commit },
|
||||
{ conversationId: 1, user: { id: 1, name: 'user-1' } }
|
||||
);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[
|
||||
types.default.ADD_USER_TYPING_TO_CONVERSATION,
|
||||
{ conversationId: 1, user: { id: 1, name: 'user-1' } },
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
it('sends correct actions', () => {
|
||||
actions.destroy(
|
||||
{ commit },
|
||||
{ conversationId: 1, user: { id: 1, name: 'user-1' } }
|
||||
);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[
|
||||
types.default.REMOVE_USER_TYPING_FROM_CONVERSATION,
|
||||
{ conversationId: 1, user: { id: 1, name: 'user-1' } },
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { getters } from '../../conversationTypingStatus';
|
||||
|
||||
describe('#getters', () => {
|
||||
it('getUserList', () => {
|
||||
const state = {
|
||||
records: {
|
||||
1: [
|
||||
{ id: 1, name: 'user-1' },
|
||||
{ id: 2, name: 'user-2' },
|
||||
],
|
||||
},
|
||||
};
|
||||
expect(getters.getUserList(state)(1)).toEqual([
|
||||
{ id: 1, name: 'user-1' },
|
||||
{ id: 2, name: 'user-2' },
|
||||
]);
|
||||
expect(getters.getUserList(state)(2)).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import * as types from '../../../mutation-types';
|
||||
import { mutations } from '../../conversationTypingStatus';
|
||||
|
||||
describe('#mutations', () => {
|
||||
describe('#ADD_USER_TYPING_TO_CONVERSATION', () => {
|
||||
it('add user to state', () => {
|
||||
const state = { records: {} };
|
||||
mutations[types.default.ADD_USER_TYPING_TO_CONVERSATION](state, {
|
||||
conversationId: 1,
|
||||
user: { id: 1, type: 'contact', name: 'user-1' },
|
||||
});
|
||||
expect(state.records).toEqual({
|
||||
1: [{ id: 1, type: 'contact', name: 'user-1' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('doesnot add user if user already exist', () => {
|
||||
const state = {
|
||||
records: {
|
||||
1: [{ id: 1, type: 'contact', name: 'user-1' }],
|
||||
},
|
||||
};
|
||||
mutations[types.default.ADD_USER_TYPING_TO_CONVERSATION](state, {
|
||||
conversationId: 1,
|
||||
user: { id: 1, type: 'contact', name: 'user-1' },
|
||||
});
|
||||
expect(state.records).toEqual({
|
||||
1: [{ id: 1, type: 'contact', name: 'user-1' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('add user to state if no matching user profiles are seen', () => {
|
||||
const state = {
|
||||
records: {
|
||||
1: [{ id: 1, type: 'user', name: 'user-1' }],
|
||||
},
|
||||
};
|
||||
mutations[types.default.ADD_USER_TYPING_TO_CONVERSATION](state, {
|
||||
conversationId: 1,
|
||||
user: { id: 1, type: 'contact', name: 'user-1' },
|
||||
});
|
||||
expect(state.records).toEqual({
|
||||
1: [
|
||||
{ id: 1, type: 'user', name: 'user-1' },
|
||||
{ id: 1, type: 'contact', name: 'user-1' },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#REMOVE_USER_TYPING_FROM_CONVERSATION', () => {
|
||||
it('remove add user if user exist', () => {
|
||||
const state = {
|
||||
records: {
|
||||
1: [{ id: 1, type: 'contact', name: 'user-1' }],
|
||||
},
|
||||
};
|
||||
mutations[types.default.REMOVE_USER_TYPING_FROM_CONVERSATION](state, {
|
||||
conversationId: 1,
|
||||
user: { id: 1, type: 'contact', name: 'user-1' },
|
||||
});
|
||||
expect(state.records).toEqual({
|
||||
1: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
ADD_MESSAGE: 'ADD_MESSAGE',
|
||||
MARK_SEEN: 'MARK_SEEN',
|
||||
MARK_MESSAGE_READ: 'MARK_MESSAGE_READ',
|
||||
FB_TYPING: 'FB_TYPING',
|
||||
SET_AGENT_TYPING: 'SET_AGENT_TYPING',
|
||||
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
|
||||
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
||||
|
||||
@@ -104,4 +104,8 @@ export default {
|
||||
// Notification Settings
|
||||
SET_USER_NOTIFICATION_UI_FLAG: 'SET_USER_NOTIFICATION_UI_FLAG',
|
||||
SET_USER_NOTIFICATION: 'SET_USER_NOTIFICATION',
|
||||
|
||||
// User Typing
|
||||
ADD_USER_TYPING_TO_CONVERSATION: 'ADD_USER_TYPING_TO_CONVERSATION',
|
||||
REMOVE_USER_TYPING_FROM_CONVERSATION: 'REMOVE_USER_TYPING_FROM_CONVERSATION',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user