Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(livechat): Persistent after-guest-registration Trigger Messages (livechat client) #32233

Merged
merged 10 commits into from
May 10, 2024
6 changes: 6 additions & 0 deletions .changeset/gorgeous-lizards-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/livechat": minor
---

Makes the triggers fired by the condition `after-guest-registration` persist on the livechat client, it will persist through reloads and pagination, only reseting when a conversation is closed (no changes were done on the agent side of the conversation)
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ test.describe('OC - Livechat New Chat Triggers - After Registration', () => {
});
});

test.describe('OC - Livechat Triggers - After Registration - Reload', async () => {
test('expect trigger message after registration', async () => {
await test.step('expect trigger message after registration to be visible', async () => {
await poLiveChat.page.goto('/livechat');
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(newUser, false);
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
})

await test.step('expect trigger message after registration to be visible after reload', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
})

await test.step('expect to close room and reload', async () => {
await poLiveChat.onlineAgentMessage.type('message_after_trigger');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('message_after_trigger')).toBeVisible();
await poLiveChat.closeChat();

await expect(poLiveChat.btnNewChat).toBeVisible();
await poLiveChat.startNewChat();
await poLiveChat.page.reload()
})

await test.step('expect trigger message after registration to be visible after reload on new chat', async () => {
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
})
});
});

test.describe('OC - Livechat New Chat Triggers - After Registration, clear Local storage', async () => {
test.beforeAll(async ({ api }) => {
await api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: true });
Expand Down
10 changes: 10 additions & 0 deletions packages/livechat/src/definitions/triggerMessage.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { IOmnichannelAgent } from '@rocket.chat/core-typings';

export type TriggerMessage = {
msg?: string;
token: string;
u: Serialized<IOmnichannelAgent>;
ts: string;
_id: string;
trigger: boolean;
};
4 changes: 2 additions & 2 deletions packages/livechat/src/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const updateBusinessUnit = async (newBusinessUnit) => {
};

export const loadConfig = async () => {
const { token, businessUnit = null, iframe: { guest: { department } = {} } = {} } = store.state;
const { renderedTriggers, token, businessUnit = null, iframe: { guest: { department } = {} } = {} } = store.state;

Livechat.credentials.token = token;

Expand All @@ -60,7 +60,7 @@ export const loadConfig = async () => {
user,
queueInfo,
sound: { src, enabled: true, play: false },
messages: [],
messages: [...renderedTriggers],
typing: [],
noMoreMessages: false,
visible: true,
Expand Down
24 changes: 17 additions & 7 deletions packages/livechat/src/lib/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const closeChat = async ({ transcriptRequested } = {}) => {
return;
}

await store.setState({ room: null });
await store.setState({ room: null, renderedTriggers: [] });

if (clearLocalStorageWhenChatEnded) {
// exclude UI-affecting flags
Expand Down Expand Up @@ -255,17 +255,23 @@ export const getGreetingMessages = (messages) => messages && messages.filter((ms
export const getLatestCallMessage = (messages) => messages && messages.filter((msg) => isVideoCallMessage(msg)).pop();

export const loadMessages = async () => {
const { ongoingCall, messages: storedMessages, room } = store.state;
const { ongoingCall, messages: storedMessages, room, renderedTriggers } = store.state;

if (!room?._id) {
return;
}

const { _id: rid, callStatus } = room;
const previousMessages = getGreetingMessages(storedMessages);

await store.setState({ loading: true });
const rawMessages = (await Livechat.loadMessages(rid)).concat(previousMessages);

const rawMessages = (await Livechat.loadMessages(rid)) ?? [];

if (rawMessages?.length < 20) {
const triggers = previousMessages.length === 0 ? renderedTriggers : previousMessages;
rawMessages.push(...triggers.reverse());
}

const messages = (await normalizeMessages(rawMessages)).map(transformAgentInformationOnMessage);

await initRoom();
Expand Down Expand Up @@ -315,7 +321,7 @@ export const loadMessages = async () => {
};

export const loadMoreMessages = async () => {
const { room, messages = [], noMoreMessages = false } = store.state;
const { room, messages = [], noMoreMessages = false, renderedTriggers } = store.state;
const { _id: rid } = room || {};

if (!rid || noMoreMessages) {
Expand All @@ -327,9 +333,13 @@ export const loadMoreMessages = async () => {
const rawMessages = await Livechat.loadMessages(rid, { limit: messages.length + 10 });
const moreMessages = (await normalizeMessages(rawMessages)).map(transformAgentInformationOnMessage);

const newNoMoreMessages = messages.length + 10 > moreMessages.length;
const triggers = newNoMoreMessages ? [...renderedTriggers] : [];
const newMessages = ([...moreMessages, ...triggers] || []).reverse();

await store.setState({
messages: (moreMessages || []).reverse(),
noMoreMessages: messages.length + 10 > moreMessages.length,
messages: newMessages,
noMoreMessages: newNoMoreMessages,
loading: false,
});
};
Expand Down
19 changes: 18 additions & 1 deletion packages/livechat/src/lib/triggerActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export const sendMessageAction = async (_: string, action: ILivechatSendMessageA

await upsertMessage(message);

// Save the triggers for subsequent renders
if (condition.name === 'after-guest-registration') {
store.setState({
renderedTriggers: [...store.state.renderedTriggers, message],
});
}

if (agent && '_id' in agent) {
await store.setState({ agent });
parentCall('callback', 'assign-agent', normalizeAgent(agent));
Expand Down Expand Up @@ -78,7 +85,17 @@ export const sendMessageExternalServiceAction = async (
trigger: true,
}));

await Promise.all(messages.map((message) => upsertMessage(message)));
await Promise.all(
messages.map((message) => {
if (condition.name === 'after-guest-registration') {
store.setState({
renderedTriggers: [...store.state.renderedTriggers, message],
});
}

return upsertMessage(message);
}),
);

if (agent && '_id' in agent) {
await store.setState({ agent });
Expand Down
5 changes: 5 additions & 0 deletions packages/livechat/src/lib/triggerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ export const removeMessage = async (messageId: string) => {
await store.setState({ messages: messages.filter(({ _id }) => _id !== messageId) });
};

export const removeTriggerMessage = async (messageId: string) => {
const { renderedTriggers } = store.state;
await store.setState({ renderedTriggers: renderedTriggers.filter(({ _id }) => _id !== messageId) });
};

export const hasTriggerCondition = (conditionName: ILivechatTriggerType) => (trigger: ILivechatTrigger) => {
return trigger.conditions.some((condition) => condition.name === conditionName);
};
Expand Down
3 changes: 3 additions & 0 deletions packages/livechat/src/store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useContext } from 'preact/hooks';
import type { CustomField } from '../components/Form/CustomFields';
import type { Agent } from '../definitions/agents';
import type { Department } from '../definitions/departments';
import type { TriggerMessage } from '../definitions/triggerMessage';
import { parentCall } from '../lib/parentCall';
import { createToken } from '../lib/random';
import Store from './Store';
Expand Down Expand Up @@ -120,6 +121,7 @@ export type StoreState = {
parentUrl?: string;
connecting?: boolean;
messageListPosition?: 'top' | 'bottom' | 'free';
renderedTriggers: TriggerMessage[];
};

export const initialState = (): StoreState => ({
Expand Down Expand Up @@ -160,6 +162,7 @@ export const initialState = (): StoreState => ({
incomingCallAlert: null,
ongoingCall: null, // TODO: store call info like url, startTime, timeout, etc here
businessUnit: null,
renderedTriggers: [],
});

const dontPersist = [
Expand Down
Loading