From 8ad1bf3d2bf2946c316e23d96d67175b95a400e5 Mon Sep 17 00:00:00 2001
From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Date: Wed, 3 Dec 2025 15:48:04 +0530
Subject: [PATCH] feat: Update conversation messages screen UI (#12975)
---
.../components-next/button/Button.vue | 2 +-
.../components-next/message/Message.vue | 4 +-
.../components-next/message/MessageList.vue | 18 +-
.../components-next/message/MessageMeta.vue | 5 +-
.../components-next/message/MessageStatus.vue | 2 +-
.../components-next/message/bubbles/Base.vue | 14 +-
.../components/buttons/ResolveAction.vue | 5 +-
.../components/widgets/AIAssistanceButton.vue | 2 +-
.../components/widgets/VideoCallButton.vue | 2 +-
.../widgets/WootWriter/EditorModeToggle.vue | 32 +-
.../widgets/WootWriter/ReplyBottomPanel.vue | 625 +++++++------
.../widgets/WootWriter/ReplyTopPanel.vue | 7 +-
.../widgets/conversation/ConversationBox.vue | 153 ++--
.../conversation/ConversationHeader.vue | 106 ++-
.../widgets/conversation/MessagesView.vue | 846 +++++++++---------
.../widgets/conversation/MoreActions.vue | 2 +-
.../widgets/conversation/ReplyBox.vue | 5 +-
.../dashboard/composables/useInbox.js | 1 +
.../conversation/ConversationView.vue | 312 +++----
19 files changed, 1089 insertions(+), 1054 deletions(-)
diff --git a/app/javascript/dashboard/components-next/button/Button.vue b/app/javascript/dashboard/components-next/button/Button.vue
index 1dfe488e9..3afe7b644 100644
--- a/app/javascript/dashboard/components-next/button/Button.vue
+++ b/app/javascript/dashboard/components-next/button/Button.vue
@@ -133,7 +133,7 @@ const STYLE_CONFIG = {
},
slate: {
solid:
- 'bg-n-solid-3 dark:hover:enabled:bg-n-solid-2 dark:focus-visible:bg-n-solid-2 hover:enabled:bg-n-alpha-2 focus-visible:bg-n-alpha-2 text-n-slate-12 outline-n-container',
+ 'bg-n-button-color dark:hover:enabled:bg-n-solid-2 dark:focus-visible:bg-n-solid-2 hover:enabled:bg-n-alpha-2 focus-visible:bg-n-alpha-2 text-n-slate-12 outline-n-container',
faded:
'bg-n-slate-9/10 text-n-slate-12 hover:enabled:bg-n-slate-9/20 focus-visible:bg-n-slate-9/20 outline-transparent',
outline:
diff --git a/app/javascript/dashboard/components-next/message/Message.vue b/app/javascript/dashboard/components-next/message/Message.vue
index dd655d0cc..ecc6f4f00 100644
--- a/app/javascript/dashboard/components-next/message/Message.vue
+++ b/app/javascript/dashboard/components-next/message/Message.vue
@@ -498,7 +498,7 @@ provideMessageContext({
'w-full': variant === MESSAGE_VARIANTS.EMAIL,
},
]"
- class="gap-x-2"
+ class="gap-x-3"
:style="{
gridTemplateAreas: gridTemplate,
}"
@@ -508,7 +508,7 @@ provideMessageContext({
v-tooltip.left-end="avatarTooltip"
class="[grid-area:avatar] flex items-end"
>
-
+
{
nextMessageType === MESSAGE_TYPES.TEMPLATE &&
currentMessageType === MESSAGE_TYPES.TEMPLATE;
- if (!hasSameSender || areBothTemplates) return false;
+ const areBothActivity =
+ nextMessageType === MESSAGE_TYPES.ACTIVITY &&
+ currentMessageType === MESSAGE_TYPES.ACTIVITY;
+
+ if (!hasSameSender || areBothTemplates || areBothActivity) return false;
if (currentMessageType !== nextMessageType) return false;
@@ -157,6 +161,17 @@ const getInReplyToMessage = parentMessage => {
return replyMessage ? useCamelCase(replyMessage) : null;
};
+
+const getMessageSpacingClass = (message, messages, index) => {
+ // For non-activity messages, use groupWithNext logic
+ if (message.messageType !== MESSAGE_TYPES.ACTIVITY) {
+ return shouldGroupWithNext(index, messages) ? 'mb-1' : 'mb-6';
+ }
+
+ // For activity messages, check next message exists and is also an activity
+ const nextMessage = messages[index + 1];
+ return nextMessage?.messageType === MESSAGE_TYPES.ACTIVITY ? 'mb-2' : 'mb-6';
+};
@@ -175,6 +190,7 @@ const getInReplyToMessage = parentMessage => {
:inbox-supports-reply-to="inboxSupportsReplyTo"
:current-user-id="currentUserId"
data-clarity-mask="True"
+ :class="getMessageSpacingClass(message, allMessages, index)"
@retry="emit('retry', message)"
/>
diff --git a/app/javascript/dashboard/components-next/message/MessageMeta.vue b/app/javascript/dashboard/components-next/message/MessageMeta.vue
index e0602cb89..3069896e9 100644
--- a/app/javascript/dashboard/components-next/message/MessageMeta.vue
+++ b/app/javascript/dashboard/components-next/message/MessageMeta.vue
@@ -123,10 +123,13 @@ const statusToShow = computed(() => {
diff --git a/app/javascript/dashboard/components-next/message/MessageStatus.vue b/app/javascript/dashboard/components-next/message/MessageStatus.vue
index d8f996cf5..11e12aa0b 100644
--- a/app/javascript/dashboard/components-next/message/MessageStatus.vue
+++ b/app/javascript/dashboard/components-next/message/MessageStatus.vue
@@ -58,7 +58,7 @@ const statusColor = computed(() => {
const statusIconMap = {
[MESSAGE_STATUS.SENT]: 'text-n-slate-10',
[MESSAGE_STATUS.DELIVERED]: 'text-n-slate-10',
- [MESSAGE_STATUS.READ]: 'text-[#7EB6FF]',
+ [MESSAGE_STATUS.READ]: 'text-n-blue-11',
};
return statusIconMap[status];
diff --git a/app/javascript/dashboard/components-next/message/bubbles/Base.vue b/app/javascript/dashboard/components-next/message/bubbles/Base.vue
index f66f272de..e46cb3f82 100644
--- a/app/javascript/dashboard/components-next/message/bubbles/Base.vue
+++ b/app/javascript/dashboard/components-next/message/bubbles/Base.vue
@@ -18,14 +18,14 @@ const { variant, orientation, inReplyTo, shouldGroupWithNext } =
useMessageContext();
const { t } = useI18n();
-const varaintBaseMap = {
- [MESSAGE_VARIANTS.AGENT]: 'bg-n-solid-blue text-n-slate-12',
+const variantBaseMap = {
+ [MESSAGE_VARIANTS.AGENT]: 'bg-n-solid-blue text-n-blue-text',
[MESSAGE_VARIANTS.PRIVATE]:
- 'bg-n-solid-amber text-n-amber-12 [&_.prosemirror-mention-node]:font-semibold',
+ 'bg-n-solid-amber text-n-amber-text [&_.prosemirror-mention-node]:font-semibold',
[MESSAGE_VARIANTS.USER]: 'bg-n-slate-4 text-n-slate-12',
[MESSAGE_VARIANTS.ACTIVITY]: 'bg-n-alpha-1 text-n-slate-11 text-sm',
- [MESSAGE_VARIANTS.BOT]: 'bg-n-solid-iris text-n-slate-12',
- [MESSAGE_VARIANTS.TEMPLATE]: 'bg-n-solid-iris text-n-slate-12',
+ [MESSAGE_VARIANTS.BOT]: 'bg-n-solid-purple text-n-purple-text',
+ [MESSAGE_VARIANTS.TEMPLATE]: 'bg-n-solid-purple text-n-purple-text',
[MESSAGE_VARIANTS.ERROR]: 'bg-n-ruby-4 text-n-ruby-12',
[MESSAGE_VARIANTS.EMAIL]: 'w-full',
[MESSAGE_VARIANTS.UNSUPPORTED]:
@@ -51,7 +51,7 @@ const flexOrientationClass = computed(() => {
});
const messageClass = computed(() => {
- const classToApply = [varaintBaseMap[variant.value]];
+ const classToApply = [variantBaseMap[variant.value]];
if (variant.value !== MESSAGE_VARIANTS.ACTIVITY) {
classToApply.push(orientationMap[orientation.value]);
@@ -118,7 +118,7 @@ const replyToPreview = computed(() => {
flexOrientationClass,
variant === MESSAGE_VARIANTS.EMAIL ? 'px-3 pb-3' : '',
variant === MESSAGE_VARIANTS.PRIVATE
- ? 'text-n-amber-12/50'
+ ? 'text-n-amber-text'
: 'text-n-slate-11',
]"
class="mt-2"
diff --git a/app/javascript/dashboard/components/buttons/ResolveAction.vue b/app/javascript/dashboard/components/buttons/ResolveAction.vue
index 3bd2314f8..dcd2c9929 100644
--- a/app/javascript/dashboard/components/buttons/ResolveAction.vue
+++ b/app/javascript/dashboard/components/buttons/ResolveAction.vue
@@ -139,8 +139,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
diff --git a/app/javascript/dashboard/components/widgets/VideoCallButton.vue b/app/javascript/dashboard/components/widgets/VideoCallButton.vue
index 30984ab4b..a5bd7a857 100644
--- a/app/javascript/dashboard/components/widgets/VideoCallButton.vue
+++ b/app/javascript/dashboard/components/widgets/VideoCallButton.vue
@@ -54,7 +54,7 @@ export default {
"
icon="i-ph-video-camera"
slate
- faded
+ ghost
sm
@click="onClick"
/>
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/EditorModeToggle.vue b/app/javascript/dashboard/components/widgets/WootWriter/EditorModeToggle.vue
index 37108b0f2..30bd14717 100644
--- a/app/javascript/dashboard/components/widgets/WootWriter/EditorModeToggle.vue
+++ b/app/javascript/dashboard/components/widgets/WootWriter/EditorModeToggle.vue
@@ -33,7 +33,7 @@ const isPrivate = computed(() => {
/**
* Computes the width of the sliding background chip in pixels
- * Includes 16px of padding in the calculation
+ * Includes 24px of padding in the calculation (px-3 = 12px on each side)
* @type {ComputedRef}
*/
const width = computed(() => {
@@ -41,17 +41,17 @@ const width = computed(() => {
? privateModeSize.width.value
: replyModeSize.width.value;
- const widthWithPadding = widthToUse + 16;
+ const widthWithPadding = widthToUse + 24;
return `${widthWithPadding}px`;
});
/**
* Computes the X translation value for the sliding background chip
- * Translates by the width of reply mode + padding when in private mode
+ * Translates by the width of reply mode + padding (24px) when in private mode
* @type {ComputedRef}
*/
const translateValue = computed(() => {
- const xTranslate = isPrivate.value ? replyModeSize.width.value + 16 : 0;
+ const xTranslate = isPrivate.value ? replyModeSize.width.value + 24 : 0;
return `${xTranslate}px`;
});
@@ -59,23 +59,39 @@ const translateValue = computed(() => {