From a4e36556448b10a985b37a2507037bd0e8584300 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 12:46:32 +0100 Subject: [PATCH 01/13] implement collation of messages --- src/Message.module.css | 6 +++++- src/Message.tsx | 12 +++++++----- src/utils.ts | 11 +++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Message.module.css b/src/Message.module.css index 107a769..f5dcb68 100644 --- a/src/Message.module.css +++ b/src/Message.module.css @@ -12,7 +12,11 @@ color: var(--webchat-message-color, #1c1c1c); font-weight: 400; font-size: var(--webchat-message-font-size, 14px); - line-height: 1.3; + line-height: 1.4; margin-block: var(--webchat-message-margin-block, 24px); margin-inline: var(--webchat-message-margin-inline, 20px); } + +.collated { + margin-block-start: calc(var(--webchat-message-margin-block, 24px) * -0.66); +} diff --git a/src/Message.tsx b/src/Message.tsx index 884c738..921e1ff 100644 --- a/src/Message.tsx +++ b/src/Message.tsx @@ -8,23 +8,27 @@ import { IWebchatConfig, MessageSender, WebchatMessage } from "./messages/types" import "./theme.css"; import classes from "./Message.module.css"; +import { isMessageCollatable } from "./utils"; export interface MessageProps { action?: MessageSender; className?: string; config?: IWebchatConfig; - disableHeader?: boolean; message: WebchatMessage; - plugins?: MatchConfig[]; onEmitAnalytics?: (event: string, payload?: unknown) => void; + plugins?: MatchConfig[]; + prevMessage?: WebchatMessage; } const Message: FC = props => { + const shouldCollate = isMessageCollatable(props.message, props.prevMessage); + const rootClassName = classnames( "webchat-message-row", props.message.source, props.className, classes.message, + shouldCollate && classes.collated, ); const MessageComponent = match(props.message, props.plugins, props.config); @@ -44,9 +48,7 @@ const Message: FC = props => { config={props.config} >
- {!props.disableHeader && ( - - )} + {!shouldCollate && }
diff --git a/src/utils.ts b/src/utils.ts index 039bfc2..8dd6704 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,3 +28,14 @@ export const getWebchatButtonLabel: getWebchatButtonLabel = button => { } return title; }; + +export const isMessageCollatable = (message: WebchatMessage, prevMessage?: WebchatMessage) => { + const COLLATION_LIMIT = 1000 * 60; // 60 sec + + const difference = Number(message?.timestamp) - Number(prevMessage?.timestamp); + return ( + isNaN(difference) === false && + difference < COLLATION_LIMIT && + prevMessage.source === message.source + ); +}; From edae5f3c79e899c93e2784e2d25fe544af18b8c8 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 12:46:39 +0100 Subject: [PATCH 02/13] add examples --- src/demo.css | 17 +++-------------- src/demo.tsx | 30 ++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/demo.css b/src/demo.css index f654ecc..82fd4b0 100644 --- a/src/demo.css +++ b/src/demo.css @@ -2,20 +2,9 @@ body { background-color: gray; } #root { - background-color: #fff; - font-family: - Figtree, - system-ui, - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Oxygen, - Ubuntu, - Cantarell, - "Open Sans", - "Helvetica Neue", - sans-serif; + background-color: #d9d9d9; + font-family: Figtree, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, + Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; margin-inline: auto; width: 375px; height: 620px; diff --git a/src/demo.tsx b/src/demo.tsx index e4bc978..187b9cb 100644 --- a/src/demo.tsx +++ b/src/demo.tsx @@ -6,10 +6,30 @@ import Message, { MessageProps } from "./Message.tsx"; import { MessageSender } from "./messages/types.ts"; const messages: MessageProps[] = [ + { + message: { + text: "First message", + source: "bot", + timestamp: "1701163314138", + }, + }, + { + message: { + text: "Second message", + source: "bot", + timestamp: "1701163319138", + }, + prevMessage: { + text: "Firts message", + source: "bot", + timestamp: "1701163314138", + }, + }, { message: { avatarNane: "Dognigy", text: "", + source: "bot", data: { _cognigy: { _webchat: { @@ -38,6 +58,8 @@ const messages: MessageProps[] = [ { message: { avatarName: "Cognigy", + source: "bot", + data: { _cognigy: { _webchat: { @@ -100,14 +122,6 @@ const messages: MessageProps[] = [ avatarName: "Cognigy", }, }, - { - message: { - source: "bot", - text: "This messaged is with previous with disableHeader prop", - avatarName: "Cognigy", - }, - disableHeader: true, - }, { message: { text: null, From 0b86639512fe53a97114d9684d5288b9ce1935d5 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 13:01:52 +0100 Subject: [PATCH 03/13] fix import --- src/messages/Message.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messages/Message.tsx b/src/messages/Message.tsx index ae47648..737bdb9 100644 --- a/src/messages/Message.tsx +++ b/src/messages/Message.tsx @@ -8,7 +8,7 @@ import { IWebchatConfig, MessageSender, WebchatMessage } from "./types"; import "src/theme.css"; import classes from "./Message.module.css"; -import { isMessageCollatable } from "./utils"; +import { isMessageCollatable } from "../utils"; export interface MessageProps { action?: MessageSender; From 3f23ad677b0939993edaed9517a25db54b702d77 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 13:29:26 +0100 Subject: [PATCH 04/13] tweak styling --- src/Message.module.css | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Message.module.css diff --git a/src/Message.module.css b/src/Message.module.css new file mode 100644 index 0000000..b0691a0 --- /dev/null +++ b/src/Message.module.css @@ -0,0 +1,22 @@ +.message { + line-height: 1.5; + font-weight: 400; + color: black; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + color: var(--webchat-message-color, #1c1c1c); + font-weight: 400; + font-size: var(--webchat-message-font-size, 14px); + line-height: 1.4; + margin-block: var(--webchat-message-margin-block, 24px); + margin-inline: var(--webchat-message-margin-inline, 20px); +} + +.collated { + margin-block-start: calc(var(--webchat-message-margin-block, 24px) * -0.5); +} From fa780146556e8cfdeaa4c8fe9fd8dd39b61219ba Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 13:29:35 +0100 Subject: [PATCH 05/13] test collation --- src/common/MessageHeader.tsx | 2 +- test/Collation.spec.tsx | 93 ++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/Collation.spec.tsx diff --git a/src/common/MessageHeader.tsx b/src/common/MessageHeader.tsx index b150abd..069bf81 100644 --- a/src/common/MessageHeader.tsx +++ b/src/common/MessageHeader.tsx @@ -30,7 +30,7 @@ const MessageHeader: FC = props => { const recievedAt = message?.timestamp ? Number(message.timestamp) : Date.now(); return ( -
+
{props.enableAvatar && }
{!isUserMessage && ( diff --git a/test/Collation.spec.tsx b/test/Collation.spec.tsx new file mode 100644 index 0000000..3d316db --- /dev/null +++ b/test/Collation.spec.tsx @@ -0,0 +1,93 @@ +import { render } from "@testing-library/react"; +import { it, describe, expect } from "vitest"; +import Message from "../src/messages/Message"; + +describe("Collation", () => { + it("collates if timestamp is in limit", () => { + const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 * 50 }; + const message2 = { + text: "World", + source: "bot", + timestamp: Date.now(), + }; + + const { getAllByTestId } = render( + <> + + + , + ); + + const messageHeaders = getAllByTestId("message-header"); + expect(messageHeaders).toHaveLength(1); + }); + + it("does NOT collate if timestamp is NOT in limit", () => { + const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 * 120 }; + const message2 = { + text: "World", + source: "bot", + timestamp: Date.now(), + }; + + const { getAllByTestId } = render( + <> + + + , + ); + + const messageHeaders = getAllByTestId("message-header"); + expect(messageHeaders).toHaveLength(2); + }); + + it("does NOT collate if source is different", () => { + const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 }; + const message2 = { + text: "World", + source: "user", + timestamp: Date.now(), + }; + + const { getAllByTestId } = render( + <> + + + , + ); + + const messageHeaders = getAllByTestId("message-header"); + expect(messageHeaders).toHaveLength(2); + }); + + it("collates multiple if all are in limit", () => { + const message1 = { text: "Hello", source: "user", timestamp: Date.now() }; + const message2 = { + text: "World", + source: "user", + timestamp: Date.now(), + }; + const message3 = { + text: "World2", + source: "user", + timestamp: Date.now(), + }; + const message4 = { + text: "World3", + source: "user", + timestamp: Date.now(), + }; + + const { getAllByTestId } = render( + <> + + + + + , + ); + + const messageHeaders = getAllByTestId("message-header"); + expect(messageHeaders).toHaveLength(1); + }); +}); From 4cfae207f5ceda9858eaf88422402fc9c37af6bd Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 13:32:11 +0100 Subject: [PATCH 06/13] move github actions config to root --- {src/.github => .github}/workflows/prettier.yml | 0 {src/.github => .github}/workflows/tests.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {src/.github => .github}/workflows/prettier.yml (100%) rename {src/.github => .github}/workflows/tests.yml (100%) diff --git a/src/.github/workflows/prettier.yml b/.github/workflows/prettier.yml similarity index 100% rename from src/.github/workflows/prettier.yml rename to .github/workflows/prettier.yml diff --git a/src/.github/workflows/tests.yml b/.github/workflows/tests.yml similarity index 100% rename from src/.github/workflows/tests.yml rename to .github/workflows/tests.yml From 1854c5d11991ec5c2b10dc74f9d4461bfa3cb2d9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 13:54:55 +0100 Subject: [PATCH 07/13] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4b94790..a33154a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cognigy/chat-components", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cognigy/chat-components", - "version": "0.3.0", + "version": "0.4.0", "dependencies": { "@braintree/sanitize-url": "^6.0.4", "@cognigy/socket-client": "^5.0.0-beta.2", diff --git a/package.json b/package.json index 9728984..179b425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cognigy/chat-components", - "version": "0.3.0", + "version": "0.4.0", "type": "module", "exports": "./dist/chat-components.js", "files": [ From 0fd30893009b1e68ab89f7e2ea7a8038687d639f Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 14:11:20 +0100 Subject: [PATCH 08/13] remove duplicate, tweak style --- src/Message.module.css | 22 ---------------------- src/messages/Message.module.css | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 src/Message.module.css diff --git a/src/Message.module.css b/src/Message.module.css deleted file mode 100644 index b0691a0..0000000 --- a/src/Message.module.css +++ /dev/null @@ -1,22 +0,0 @@ -.message { - line-height: 1.5; - font-weight: 400; - color: black; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; - - color: var(--webchat-message-color, #1c1c1c); - font-weight: 400; - font-size: var(--webchat-message-font-size, 14px); - line-height: 1.4; - margin-block: var(--webchat-message-margin-block, 24px); - margin-inline: var(--webchat-message-margin-inline, 20px); -} - -.collated { - margin-block-start: calc(var(--webchat-message-margin-block, 24px) * -0.5); -} diff --git a/src/messages/Message.module.css b/src/messages/Message.module.css index f5dcb68..b0691a0 100644 --- a/src/messages/Message.module.css +++ b/src/messages/Message.module.css @@ -18,5 +18,5 @@ } .collated { - margin-block-start: calc(var(--webchat-message-margin-block, 24px) * -0.66); + margin-block-start: calc(var(--webchat-message-margin-block, 24px) * -0.5); } From 60877018ca233daedd4065c380975fb0b2f2b9c8 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Tue, 28 Nov 2023 14:14:17 +0100 Subject: [PATCH 09/13] fix execution order for test pipeline --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c2de923..5bcfc08 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,6 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: "npm" - - run: npm run build - run: npm ci + - run: npm run build - run: npm test From 4db84922d3c0ffb8e6c15f9c36c45f178bd9fa41 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Wed, 29 Nov 2023 09:56:19 +0100 Subject: [PATCH 10/13] use fixture --- src/demo.tsx | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/demo.tsx b/src/demo.tsx index a65f8ad..067e143 100644 --- a/src/demo.tsx +++ b/src/demo.tsx @@ -9,6 +9,7 @@ import { MessageSender } from "./messages/types.ts"; import listMessage from "test/fixtures/list.json"; import gallery from "test/fixtures/gallery.json"; import imageDownloadable from "test/fixtures/image-downloadable.json"; +import image from "test/fixtures/image.json"; import imageBroken from "test/fixtures/imageBroken.json"; import video from "test/fixtures/video.json"; import videoYoutube from "test/fixtures/videoYoutube.json"; @@ -132,32 +133,7 @@ const messages: MessageProps[] = [ }, }, { - message: { - text: null, - data: { - _cognigy: { - _default: { - _image: { - type: "image", - imageUrl: "https://placekitten.com/300/300", - imageAltText: "Kitten", - }, - }, - _webchat: { - message: { - attachment: { - type: "image", - payload: { - url: "https://picsum.photos/500/500", - altText: "Random image", - }, - - }, - }, - }, - }, - }, - }, + message: image as IMessage, }, { message: { From ca643bcee74f4fd4dfe3967140650a0554ab3576 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Wed, 29 Nov 2023 09:56:45 +0100 Subject: [PATCH 11/13] add safety checks --- src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 4451b8f..c1f102a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -34,9 +34,10 @@ export const isMessageCollatable = (message: WebchatMessage, prevMessage?: Webch const difference = Number(message?.timestamp) - Number(prevMessage?.timestamp); return ( + prevMessage && isNaN(difference) === false && difference < COLLATION_LIMIT && - prevMessage.source === message.source + prevMessage?.source === message?.source ); }; From be7112b59a02a5f216d769f087d10f315703a1af Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Wed, 29 Nov 2023 09:57:06 +0100 Subject: [PATCH 12/13] use proper interface --- src/messages/Message.tsx | 2 +- src/utils.ts | 2 +- test/Collation.spec.tsx | 45 ++++++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/messages/Message.tsx b/src/messages/Message.tsx index b90caad..1507481 100644 --- a/src/messages/Message.tsx +++ b/src/messages/Message.tsx @@ -19,7 +19,7 @@ export interface MessageProps { message: IMessage; onEmitAnalytics?: (event: string, payload?: unknown) => void; plugins?: MatchConfig[]; - prevMessage?: WebchatMessage; + prevMessage?: IMessage; } const Message: FC = props => { diff --git a/src/utils.ts b/src/utils.ts index c1f102a..d3168c2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,7 +29,7 @@ export const getWebchatButtonLabel: getWebchatButtonLabel = button => { return title; }; -export const isMessageCollatable = (message: WebchatMessage, prevMessage?: WebchatMessage) => { +export const isMessageCollatable = (message: IMessage, prevMessage?: IMessage) => { const COLLATION_LIMIT = 1000 * 60; // 60 sec const difference = Number(message?.timestamp) - Number(prevMessage?.timestamp); diff --git a/test/Collation.spec.tsx b/test/Collation.spec.tsx index 3d316db..784d3d6 100644 --- a/test/Collation.spec.tsx +++ b/test/Collation.spec.tsx @@ -1,14 +1,19 @@ import { render } from "@testing-library/react"; import { it, describe, expect } from "vitest"; import Message from "../src/messages/Message"; +import { IMessage } from "@cognigy/socket-client"; describe("Collation", () => { it("collates if timestamp is in limit", () => { - const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 * 50 }; - const message2 = { + const message1: IMessage = { + text: "Hello", + source: "bot", + timestamp: String(Date.now() - 1000 * 50), + }; + const message2: IMessage = { text: "World", source: "bot", - timestamp: Date.now(), + timestamp: String(Date.now()), }; const { getAllByTestId } = render( @@ -23,11 +28,15 @@ describe("Collation", () => { }); it("does NOT collate if timestamp is NOT in limit", () => { - const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 * 120 }; - const message2 = { + const message1: IMessage = { + text: "Hello", + source: "bot", + timestamp: String(Date.now() - 1000 * 120), + }; + const message2: IMessage = { text: "World", source: "bot", - timestamp: Date.now(), + timestamp: String(Date.now()), }; const { getAllByTestId } = render( @@ -42,11 +51,15 @@ describe("Collation", () => { }); it("does NOT collate if source is different", () => { - const message1 = { text: "Hello", source: "bot", timestamp: Date.now() - 1000 }; - const message2 = { + const message1: IMessage = { + text: "Hello", + source: "bot", + timestamp: String(Date.now() - 1000), + }; + const message2: IMessage = { text: "World", source: "user", - timestamp: Date.now(), + timestamp: String(Date.now()), }; const { getAllByTestId } = render( @@ -61,21 +74,21 @@ describe("Collation", () => { }); it("collates multiple if all are in limit", () => { - const message1 = { text: "Hello", source: "user", timestamp: Date.now() }; - const message2 = { + const message1: IMessage = { text: "Hello", source: "user", timestamp: String(Date.now()) }; + const message2: IMessage = { text: "World", source: "user", - timestamp: Date.now(), + timestamp: String(Date.now()), }; - const message3 = { + const message3: IMessage = { text: "World2", source: "user", - timestamp: Date.now(), + timestamp: String(Date.now()), }; - const message4 = { + const message4: IMessage = { text: "World3", source: "user", - timestamp: Date.now(), + timestamp: String(Date.now()), }; const { getAllByTestId } = render( From 41be480ca164b9290301bc9ef65b1b439b3556a7 Mon Sep 17 00:00:00 2001 From: Dmitrii Ostasevich Date: Wed, 29 Nov 2023 10:06:56 +0100 Subject: [PATCH 13/13] add container background --- src/messages/Image/Image.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/messages/Image/Image.module.css b/src/messages/Image/Image.module.css index 19fb064..3fca09e 100644 --- a/src/messages/Image/Image.module.css +++ b/src/messages/Image/Image.module.css @@ -18,6 +18,7 @@ article .wrapper img { } .wrapperDownloadable { + background-color: var(--white); cursor: pointer; border: 1px solid var(--black-80); }