Skip to content

Master 13.8.1 #1319

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

Merged
merged 39 commits into from
May 21, 2025
Merged

Master 13.8.1 #1319

merged 39 commits into from
May 21, 2025

Conversation

panwaranuj01
Copy link
Contributor

@panwaranuj01 panwaranuj01 commented May 21, 2025

What do you want to achieve?

PR Checklist

  • I have tested it locally and all functionalities are working fine.
  • I have compared it with mocks and all design elements are the same.
  • I have tested it in IE Browser.

How was the code tested?

What new thing you came across while writing this code?

In case you fixed a bug then please describe the root cause of it?

Screenshot

NOTE: Make sure you're comparing your branch with the correct base branch

Summary by CodeRabbit

  • New Features
    • Introduced support for displaying rich email messages, including parsing and rendering .eml files with attachments, inline images, and formatted headers within conversations.
    • Added new custom elements and UI components for enhanced email message presentation.
  • Improvements
    • Enhanced handling of AI-generated tokenized responses and "talk to human" messages, improving typing indicator behavior and UI state management during these scenarios.
    • Updated message rendering logic to better support new email and AI message types.
  • Style
    • Added and updated CSS for improved email message layout, attachment previews, and overall visual consistency.
  • Bug Fixes
    • Prevented duplicate rendering of tokenized AI messages and improved handling of message queue states.
  • Chores
    • Updated internal constants and configuration for new message types.

Sakshi and others added 30 commits February 21, 2025 21:21
generative repsonse implementation
Cus-479-new UI is breaking in widget and dashboard if any msg have table component
TDH-1788 | On restarting the conversation, default language Welcome message is triggering
Copy link

Fails
🚫

Changes were made to package.json, but not to package-lock.json - 'Perhaps you need to run npm install?'

Warnings
⚠️ ❗ Your PR has over 700 lines of code, please consider breaking it down so we can effectively review it. 👍
⚠️ Please add the JIRA issue key to the PR title (e.g. FW-123)
Messages
📖 New Files in this PR: - webplugin/js/app/conversation/mail-parser/attachment-service.js , webplugin/js/app/conversation/mail-parser/dom-service.js , webplugin/js/app/conversation/mail-parser/eml-parser.js , webplugin/js/app/conversation/mail-parser/index.js , webplugin/js/app/conversation/mail-parser/style.js

Generated by 🚫 dangerJS against 3002b54

@panwaranuj01 panwaranuj01 merged commit ccbd801 into master May 21, 2025
3 of 5 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (15)
package.json (1)

4-4: Reconsider using HTML in package.json description

The description field has been updated to include an HTML image tag. While this works, it's unconventional and may not display properly in all contexts where package.json is viewed (like npm registry pages or GitHub).

Consider using a plain text description that explains the package's purpose instead of HTML content. If you want to keep the image reference, consider moving it to documentation files where HTML is expected.

webplugin/js/app/components/custom-element.js (1)

8-8: Consider adding class documentation

A new MckEmailComponent class has been created, but it currently doesn't add any functionality beyond the parent class.

Add a JSDoc comment to explain the purpose of this class and how it differs from ShadowDomComponent, even if it's just for future extensibility.

+/**
+ * Custom component for rendering rich email messages
+ * Currently extends base functionality from ShadowDomComponent
+ * but can be extended for email-specific rendering needs
+ */
class MckEmailComponent extends ShadowDomComponent {}
webplugin/js/app/km-richtext-markup-1.0.js (1)

861-862: Improved email rendering architecture using custom elements.

The replacement of iframe-based rendering with a custom element (<mck-email-rich-message>) is a significant architectural improvement. This approach:

  1. Enhances security by eliminating iframe-related vulnerabilities
  2. Provides better styling control and component isolation
  3. Creates a more maintainable and extensible email rendering solution
  4. Ensures proper message isolation through unique identifiers based on both groupId and key

This change aligns with modern web component practices and creates a foundation for more sophisticated email content rendering.

Consider adding a brief comment explaining the purpose of the custom element and how it relates to the mail parser infrastructure for better code documentation.

webplugin/js/app/km-utils.js (1)

523-538: New utility function for email processing.

This utility function efficiently fetches and parses .eml files using modern async/await patterns and the Fetch API. It integrates with the external kmPostalMime library for parsing the email content.

Consider these improvements:

  1. The commented-out code about responseType header suggests incomplete implementation or testing.
  2. While rethrow-only catch blocks are sometimes flagged by static analysis, keeping it provides a point for future error handling enhancement.
 getAndParseEml: async function (emlUrl) {
     try {
         const response = await fetch(emlUrl, {
             method: 'GET',
-            // headers: {
-            //     responseType: 'arraybuffer',
-            // },
         });
         const emlData = await response.arrayBuffer();

         const data = await kmPostalMime.parse(emlData);
         return data;
     } catch (e) {
-        throw e;
+        console.error('Error parsing email file:', e);
+        throw e; // Rethrow to allow caller to handle
     }
 },
🧰 Tools
🪛 Biome (1.9.4)

[error] 536-536: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

webplugin/js/app/conversation/gen-ai-service.js (1)

22-26: Improve sequence validation with strict equality comparison

This validation correctly ensures tokens are processed in order, but uses non-strict equality comparison.

-        if (this.currentIndex != msg.index - 1) {
+        if (this.currentIndex !== msg.index - 1) {
            // if any token is missed then  stop there
            return;
        }
webplugin/js/app/conversation/mail-parser/dom-service.js (2)

6-8: Add more contextual information to the error message

The error message could be more helpful by including context about why the container wasn't found.

-            throw new Error(`Email container not found: ${containerId}`);
+            throw new Error(`Email container not found: ${containerId}. Make sure the custom element is registered and the container is created before calling this method.`);

57-67: Remove or document commented-out code

There's a significant block of commented-out code that should either be removed if no longer needed or documented with a TODO comment explaining why it's preserved.

Either remove the commented-out code or add a clear explanation:

-            // if (!emlToHtml) {
-            // const users = kmMailProcessor.getToUsersFromGroup(group, message.to);
-            // const fromAddress = group.members.find((mem) => mem.includes(message.to));
-            // const fromMarkup = EmailDOMService.getFromMarkup({
-            //     name: message.to,
-            //     address: fromAddress || '',
-            // });
-            // const toAndCcMarkup = EmailDOMService.getToAndCCMarkup(users);
-            // kmEmailMainContainer.insertAdjacentHTML('afterbegin', toAndCcMarkup);
-            // kmEmailMainContainer.insertAdjacentHTML('afterbegin', fromMarkup);
-            // }
+            // TODO: Code for adding From/To/CC markup is commented out because [reason].
+            // Uncomment and complete implementation when [condition].
webplugin/js/app/conversation/mail-parser/style.js (3)

27-28: Replace commented-out style and improve overflow handling

The CSS contains a commented-out style and uses overflow: scroll which forces scrollbars even when not needed.

-            //   border-top: none;
-              overflow: scroll;
+              overflow: auto;

75-76: Avoid using !important in CSS rules

Using !important overrides the normal cascade and can make styles harder to maintain.

Consider refactoring the CSS selector specificity instead of using !important to ensure the correct styles are applied.

Also applies to: 133-135


109-111: Remove duplicate styling for .km-email-show-more-btn

There appears to be duplicate styling for .km-email-show-more-btn which was already defined in lines 85-108.

-            .km-email-show-more-btn {
-               margin-bottom: 6px;
-            }    
webplugin/js/app/conversation/mail-parser/eml-parser.js (2)

8-8: Add a comment explaining the regular expression

The regular expression in getFormattedMessage is complex and would benefit from a comment explaining what pattern it's targeting.

-        return message.replaceAll('\n', '<br>').replace(/\[\s*([a-zA-Z0-9]+):[^\]]*\s*\]/g, '');
+        // Replace newlines with <br> tags and remove image placeholders of the format [image: image.png]
+        return message.replaceAll('\n', '<br>').replace(/\[\s*([a-zA-Z0-9]+):[^\]]*\s*\]/g, '');

25-30: Remove or document commented-out code

Similar to other files, there's commented-out code that should either be removed if no longer needed or documented with a TODO comment.

Either remove the commented-out code or add a clear explanation about its purpose and future use.

webplugin/js/app/conversation/mail-parser/index.js (1)

109-113: Use standard method syntax – arrow field adds unnecessary overhead

Declaring a class field with = function… creates a new function per instance and slightly complicates type inference. Prefer regular method syntax:

-getToUsersFromGroup = function (group, msgFrom) {
-
-};
+getToUsersFromGroup(group, msgFrom) {
+
+}
webplugin/js/app/conversation/mail-parser/attachment-service.js (1)

16-19: Ensure global availability of DOMPurify

DOMPurify is referenced but not imported. If it isn’t globally attached before this script runs, the call will throw. Verify bundler order or import explicitly.

webplugin/js/app/mck-sidebox-1.0.js (1)

21-25: Prefer const over var for immutable configuration objects

userOverride is never reassigned, so declaring it with var is unnecessary and can invite accidental re-assignment or hoisting surprises elsewhere in this 8 000-line file.

-var userOverride = {
+const userOverride = {
     voiceOutput: true,
 };

If runtime mutation is required, consider freezing the object (Object.freeze) to make intent explicit.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4e43773 and 3002b54.

📒 Files selected for processing (18)
  • package.json (1 hunks)
  • webplugin/bundleFiles.js (2 hunks)
  • webplugin/css/app/km-rich-message.css (1 hunks)
  • webplugin/css/app/mck-sidebox-1.0.css (1 hunks)
  • webplugin/js/app/components/custom-element.js (1 hunks)
  • webplugin/js/app/components/typing-service.js (3 hunks)
  • webplugin/js/app/conversation/gen-ai-service.js (3 hunks)
  • webplugin/js/app/conversation/mail-parser/attachment-service.js (1 hunks)
  • webplugin/js/app/conversation/mail-parser/dom-service.js (1 hunks)
  • webplugin/js/app/conversation/mail-parser/eml-parser.js (1 hunks)
  • webplugin/js/app/conversation/mail-parser/index.js (1 hunks)
  • webplugin/js/app/conversation/mail-parser/style.js (1 hunks)
  • webplugin/js/app/km-richtext-markup-1.0.js (1 hunks)
  • webplugin/js/app/km-utils.js (2 hunks)
  • webplugin/js/app/kommunicate-client.js (1 hunks)
  • webplugin/js/app/kommunicate-ui.js (1 hunks)
  • webplugin/js/app/kommunicate.js (2 hunks)
  • webplugin/js/app/mck-sidebox-1.0.js (19 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
webplugin/js/app/conversation/mail-parser/index.js (1)
webplugin/js/app/conversation/mail-parser/eml-parser.js (1)
  • kmEmlToHtml (154-154)
🪛 Biome (1.9.4)
webplugin/js/app/km-utils.js

[error] 536-536: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

webplugin/js/app/conversation/mail-parser/dom-service.js

[error] 1-84: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🔇 Additional comments (20)
webplugin/js/app/kommunicate-client.js (1)

68-68: Simple state initialization for loader control.

This new line sets the isWaitingQueue flag to false on the global CURRENT_GROUP_DATA object, which controls the display of a loader on the screen during conversation creation.

webplugin/css/app/km-rich-message.css (1)

782-785: Style addition for email rich message layout.

The new CSS class .mck-email-rich-msg defines width and spacing for email rich message components, supporting the new email parsing and rendering feature.

webplugin/css/app/mck-sidebox-1.0.css (1)

4983-4983: Padding adjustment for email display container.

The padding property has been commented out for the .km-mail-fixed-view class. This change likely improves the layout of email content within the container, allowing the email's own spacing to control the presentation.

webplugin/js/app/components/custom-element.js (1)

15-17: LGTM - Custom element registration looks good

Properly registering the new custom element for email messages, following the pattern used for the HTML rich message component.

webplugin/bundleFiles.js (2)

13-19: LGTM - Well-organized mail parser files

This new constant containing the mail parser file paths is well-structured and follows the existing code patterns.


43-43: LGTM - Clean integration of mail parser files

Properly integrating the mail parser files into the plugin bundle.

webplugin/js/app/kommunicate-ui.js (1)

1582-1590:

✅ Verification successful

LGTM - Properly handling UI state for waiting queue

The changes correctly toggle visibility of the "talk to human" elements and set the waiting queue flag on the current group data.

Let's verify that this state flag is properly reset when a conversation exits the waiting queue:


🏁 Script executed:

#!/bin/bash
# Check where CURRENT_GROUP_DATA.isWaitingQueue gets reset
rg -A 3 "CURRENT_GROUP_DATA.isWaitingQueue = false" --type js

Length of output: 1581


Waiting queue state reset verified across client and sidebox modules

grep shows CURRENT_GROUP_DATA.isWaitingQueue is explicitly reset to false in the following locations, confirming the flag is cleared when exiting the waiting queue:

  • webplugin/js/app/kommunicate-client.js (loader logic)
  • webplugin/js/app/mck-sidebox-1.0.js (multiple exit flows)

No further changes are required—this correctly manages the waiting‐queue UI state.

webplugin/js/app/kommunicate.js (2)

579-582: Proper extension of email message handling to include new ELECTRONIC_MAIL content type.

The condition is appropriately extended to handle both traditional HTML emails (content type TEXT_HTML with source MAIL_INTERCEPTOR) and the newly added ELECTRONIC_MAIL content type. This ensures consistency in how email content is processed and displayed throughout the application.


723-726: Improved text area enablement logic for message processing.

This change enhances the user experience by ensuring the text area is properly enabled in two scenarios: when the last token is received or when processing a non-token message. The reset of the GenAI state in conjunction with enablement ensures a clean state for new interactions.

webplugin/js/app/km-utils.js (1)

115-115: New email content type constant is properly defined.

Adding the ELECTRONIC_MAIL content type with value 104 to the MESSAGE_CONTENT_TYPE enum properly extends the messaging capabilities to support email messages throughout the application.

webplugin/js/app/components/typing-service.js (4)

12-12: Added property for tracking talk-to-human messages.

This property enables the typing service to be aware of special "talk to human" messages, which require different handling for typing indicators and other UI elements.


24-26: Proper setter method implementation for the new property.

The setter method follows good encapsulation practices by providing a controlled way to update the isKmTalkToHumanMsg state.


29-33: Prevent duplicate tokenized responses in the message queue.

This check prevents duplicate UI effects by ensuring tokenized responses aren't added to the queue multiple times. This improves performance and avoids repetitive visual elements.


64-65: Enhanced typing indicator visibility logic.

This condition intelligently suppresses the typing indicator in two special cases:

  1. When a "talk to human" message is being processed
  2. When the user is in a waiting queue state

This prevents confusing UI feedback in situations where showing a typing indicator would be misleading, improving the overall user experience.

webplugin/js/app/conversation/gen-ai-service.js (4)

5-5: LGTM: Added token index tracking state

The addition of currentIndex property initialized to -1 provides a way to track the sequential processing of tokens, which is necessary for maintaining message integrity.


10-10: LGTM: Reset element at method start

Resetting the currentElement at the beginning of each method call ensures a clean slate for token processing and prevents state-related bugs.


27-27: LGTM: Added space after each token

Adding a space after each token ensures proper word separation in the rendered message, which improves readability.


39-39: LGTM: Reset token index tracking

Resetting the currentIndex property in the resetState method ensures complete state cleanup when needed.

webplugin/js/app/conversation/mail-parser/style.js (1)

3-16: LGTM: Well-defined scrollbar styling

The scrollbar styling is comprehensive and provides a consistent appearance across different browsers that support these properties.

webplugin/js/app/mck-sidebox-1.0.js (1)

8372-8377: kmMailProcessor invocation is unconditional – add a safety net

If kmMailProcessor failed to load (e.g. bundle order issue) the whole message-rendering flow will halt. Consider guarding or logging:

- kmMailProcessor.processMail(msg, contact, emlMessage);
+ if (emlMessage && typeof kmMailProcessor?.processMail === 'function') {
+     kmMailProcessor.processMail(msg, contact, true);
+ }

Comment on lines +69 to +73
const anchors = kmEmailMainContainer.querySelectorAll('a');

anchors.forEach((anchor) => {
anchor.setAttribute('target', '_blank');
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add security attributes to anchor tags

When setting target="_blank" on anchor tags, also set rel="noopener noreferrer" to prevent potential security issues known as "reverse tabnabbing".

            anchors.forEach((anchor) => {
                anchor.setAttribute('target', '_blank');
+               anchor.setAttribute('rel', 'noopener noreferrer');
            });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const anchors = kmEmailMainContainer.querySelectorAll('a');
anchors.forEach((anchor) => {
anchor.setAttribute('target', '_blank');
});
const anchors = kmEmailMainContainer.querySelectorAll('a');
anchors.forEach((anchor) => {
anchor.setAttribute('target', '_blank');
anchor.setAttribute('rel', 'noopener noreferrer');
});
🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/dom-service.js around lines 69 to
73, when setting target="_blank" on anchor tags, also add rel="noopener
noreferrer" to each anchor element to prevent reverse tabnabbing security
issues. Update the code to set both attributes on each anchor within the forEach
loop.

Comment on lines +1 to +84
class EmailDOMService {
static createEmailContainer(message, group) {
const containerId = `km-email-${message.groupId}-${message.key}`;
const container = document.getElementById(containerId);

if (!container) {
throw new Error(`Email container not found: ${containerId}`);
}

const shadowRoot = container.shadowRoot;
const style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.innerHTML = kmMailProcessor.getEmailRichMsgStyle();

const mailElement = document.createElement('div');
mailElement.setAttribute(
'class',
`km-email-rich-msg-container ${
group.metadata?.source == 'MAIL_INTERCEPTOR'
? 'km-email-source-mail-interceptor'
: ''
}`
);

shadowRoot.append(style, mailElement);
return mailElement;
}

static getToAndCCMarkup(headers = []) {
const { toUsers, ccUsers, bccUsers } = kmMailProcessor.getCcToBccUsers(headers);

const isCcUserVisible = ccUsers ? 'vis' : 'n-vis';
const isBccUserVisible = bccUsers ? 'vis' : 'n-vis';
return `<div class="km-email-to-cc-container">
<div class="km-email-to-heading km-email-headers" title="${toUsers}"><strong>${'to'}:</strong> <span>${toUsers} </span></div>
<div class="km-email-cc-heading km-email-headers ${isCcUserVisible}" title="${ccUsers}"><strong>${'cc'}:</strong> <span>${ccUsers}</span></div>
<div class="km-email-bcc-heading ${isBccUserVisible}" title="${bccUsers}"><strong>${'bcc'}:</strong> <span>${bccUsers}</span></div>
</div>`;
}

static appendEmailRichMsg(message, group, emlToHtml) {
const richText =
Kommunicate.isRichTextMessage(message.metadata) ||
message.contentType == 3 ||
emlToHtml;

if ((richText && message.source === 7 && message.message) || emlToHtml) {
const kmEmailRichMsg = document.getElementById(
'km-email-' + message.groupId + '-' + message.key
);
const kmEmailMainContainer = kmEmailRichMsg.shadowRoot.querySelector(
'.km-email-rich-msg-container'
);

kmEmailMainContainer.innerHTML = emlToHtml ? message.emlContent : message.message;

// if (!emlToHtml) {
// const users = kmMailProcessor.getToUsersFromGroup(group, message.to);
// const fromAddress = group.members.find((mem) => mem.includes(message.to));
// const fromMarkup = EmailDOMService.getFromMarkup({
// name: message.to,
// address: fromAddress || '',
// });
// const toAndCcMarkup = EmailDOMService.getToAndCCMarkup(users);
// kmEmailMainContainer.insertAdjacentHTML('afterbegin', toAndCcMarkup);
// kmEmailMainContainer.insertAdjacentHTML('afterbegin', fromMarkup);
// }

const anchors = kmEmailMainContainer.querySelectorAll('a');

anchors.forEach((anchor) => {
anchor.setAttribute('target', '_blank');
});
}
}

static getLoadingSpinner() {
return `<div class="km-eml-loading">Loading...</div>`;
}

static getFromMarkup(from = {}) {
return `<div class="km-email-from-heading" title="${from.address}"><strong class="km-email-from">${from.name}</strong> <p>&lt;${from.address}&gt;</p></div>`;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider using utility functions instead of a class with only static methods

This class contains only static methods, which suggests it might be better structured as a collection of utility functions rather than a class.

-class EmailDOMService {
-    static createEmailContainer(message, group) {
+const EmailDOMService = {};
+
+EmailDOMService.createEmailContainer = function(message, group) {

🏁 Script executed:

#!/bin/bash
# Check if other classes in the codebase follow a similar pattern or use regular utility functions
rg -A 1 "class [A-Za-z]+" | grep -A 1 "static " | head -10

Length of output: 170


Refactor static-only class to a utility module

This file (webplugin/js/app/conversation/mail-parser/dom-service.js, lines 1–84) defines a class containing only static methods. You can simplify it by exporting standalone functions instead of wrapping them in a class:

-// webplugin/js/app/conversation/mail-parser/dom-service.js
-class EmailDOMService {
-    static createEmailContainer(message, group) { … }
-    static getToAndCCMarkup(headers = []) { … }
-    static appendEmailRichMsg(message, group, emlToHtml) { … }
-    static getLoadingSpinner() { … }
-    static getFromMarkup(from = {}) { … }
-}
-export { EmailDOMService };
+// webplugin/js/app/conversation/mail-parser/dom-service.js
+export function createEmailContainer(message, group) { … }
+export function getToAndCCMarkup(headers = []) { … }
+export function appendEmailRichMsg(message, group, emlToHtml) { … }
+export function getLoadingSpinner() { … }
+export function getFromMarkup(from = {}) { … }
  • Eliminates an unnecessary class wrapper
  • Aligns with standard ES module patterns
  • Improves tree-shaking and reduces bundle size

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-84: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/dom-service.js lines 1 to 84, the
EmailDOMService class contains only static methods, which is an unnecessary
class wrapper. Refactor by converting each static method into a standalone
exported function, removing the class declaration entirely. Update all method
references accordingly to use these functions directly, improving modularity and
enabling better tree-shaking.

Comment on lines +109 to +127
attachments.map(async (attachment) => {
switch (true) {
case attachment.mimeType?.includes('image/'):
attachMentService.setAttachmentType(imageService);
break;
case attachment.mimeType?.includes('video/'):
attachMentService.setAttachmentType(videoService);
break;
default:
attachMentService.setAttachmentType(otherService);
break;
}

const htmlString = await attachMentService.getAttachment(attachment);
attachmentWrapper.insertAdjacentHTML('beforeend', htmlString);

emailContainer.append(wrapper);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use forEach instead of map when not using the return value

The code uses map but doesn't use its return value, which is not the intended purpose of map.

-        attachments.map(async (attachment) => {
+        attachments.forEach(async (attachment) => {

Also consider using Promise.all to process all attachments in parallel:

Promise.all(
  attachments.map(async (attachment) => {
    // Process each attachment and return the HTML string
    // ...
    return htmlString;
  })
).then(htmlStrings => {
  // Append all HTML strings at once
  htmlStrings.forEach(html => {
    attachmentWrapper.insertAdjacentHTML('beforeend', html);
  });
  emailContainer.append(wrapper);
});
🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/eml-parser.js around lines 109 to
127, the code uses map to iterate over attachments without using the returned
array, which is incorrect. Replace the map call with Promise.all wrapping a map
that returns the awaited HTML strings for each attachment. Then, in the
Promise.all then callback, insert all HTML strings into attachmentWrapper and
append the wrapper to emailContainer once, ensuring parallel processing and
proper handling of asynchronous operations.

Comment on lines +144 to +149
img.src = URL.createObjectURL(
new Blob([attachment.content], {
type: attachment.mimeType,
})
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent memory leaks with URL.createObjectURL

The code creates object URLs but doesn't revoke them, which can lead to memory leaks if many emails with attachments are processed.

Consider revoking the object URL when it's no longer needed:

                if (attachment) {
-                    img.src = URL.createObjectURL(
+                    const objectUrl = URL.createObjectURL(
                        new Blob([attachment.content], {
                            type: attachment.mimeType,
                        })
                    );
+                    img.src = objectUrl;
+                    // Revoke the URL when the image has loaded to free memory
+                    img.onload = () => URL.revokeObjectURL(objectUrl);
                }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
img.src = URL.createObjectURL(
new Blob([attachment.content], {
type: attachment.mimeType,
})
);
}
if (attachment) {
const objectUrl = URL.createObjectURL(
new Blob([attachment.content], {
type: attachment.mimeType,
})
);
img.src = objectUrl;
// Revoke the URL when the image has loaded to free memory
img.onload = () => URL.revokeObjectURL(objectUrl);
}
🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/eml-parser.js around lines 144 to
149, the code creates object URLs using URL.createObjectURL but does not revoke
them, which can cause memory leaks. To fix this, add a call to
URL.revokeObjectURL for the created object URL when the image is no longer
needed, such as after the image has loaded or been removed, to properly release
the allocated memory.

Comment on lines +5 to +14
await this.loadPostalMimeLib();
this.appendEmailWrapper(msg, group);

if (this.checkIfMsgInEmlFormat(msg)) {
await kmEmlToHtml.getEmlToHtml(msg, group, emlMessage);
return;
}
// Process old flow
EmailDOMService.appendEmailRichMsg(msg, group);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Wrap network-dependent logic in a try/catch to avoid breaking the UI

If the external script fails to load, loadPostalMimeLib() rejects, propagating an unhandled promise rejection and leaving the message container empty.
Consider graceful degradation:

-await this.loadPostalMimeLib();
-this.appendEmailWrapper(msg, group);
+try {
+    await this.loadPostalMimeLib();
+} catch (e) {
+    console.error('Falling back – postal-mime lib not available', e);
+}
+this.appendEmailWrapper(msg, group);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await this.loadPostalMimeLib();
this.appendEmailWrapper(msg, group);
if (this.checkIfMsgInEmlFormat(msg)) {
await kmEmlToHtml.getEmlToHtml(msg, group, emlMessage);
return;
}
// Process old flow
EmailDOMService.appendEmailRichMsg(msg, group);
}
try {
await this.loadPostalMimeLib();
} catch (e) {
console.error('Falling back – postal-mime lib not available', e);
}
this.appendEmailWrapper(msg, group);
if (this.checkIfMsgInEmlFormat(msg)) {
await kmEmlToHtml.getEmlToHtml(msg, group, emlMessage);
return;
}
// Process old flow
EmailDOMService.appendEmailRichMsg(msg, group);
}
🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/index.js around lines 5 to 14, the
call to loadPostalMimeLib() is not wrapped in a try/catch block, causing
unhandled promise rejections if the external script fails to load. Wrap the
await this.loadPostalMimeLib() call in a try/catch block to handle errors
gracefully, ensuring the UI does not break and the message container is not left
empty. In the catch block, log the error and allow the code to continue with
fallback behavior.

Comment on lines +104 to +136
arrayBufferToVideo(arrayBuffer, attachment) {
// Create object URL directly instead of using FileReader
const blob = new Blob([arrayBuffer], {
type: attachment.mimeType || 'video/mp4',
});
const objectUrl = URL.createObjectURL(blob);

return new Promise((resolve, reject) => {
const video = document.createElement('video');
video.controls = true;
video.preload = 'metadata'; // Only load metadata initially
video.setAttribute('data-size', alFileService.getFilePreviewSize(blob.size));
// Use loadedmetadata instead of loadeddata for faster loading
video.addEventListener(
'loadedmetadata',
() => {
resolve(video);
},
{ once: true }
); // Use once:true for automatic cleanup

video.addEventListener(
'error',
() => {
URL.revokeObjectURL(objectUrl);
reject(new Error('Failed to load video'));
},
{ once: true }
);

video.src = objectUrl;
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Video blob URL never revoked

The object URL should be freed after the video element is ready to prevent leaks:

video.addEventListener(
    'loadedmetadata',
    () => {
+        URL.revokeObjectURL(objectUrl);
        resolve(video);
    },
    { once: true }
);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
arrayBufferToVideo(arrayBuffer, attachment) {
// Create object URL directly instead of using FileReader
const blob = new Blob([arrayBuffer], {
type: attachment.mimeType || 'video/mp4',
});
const objectUrl = URL.createObjectURL(blob);
return new Promise((resolve, reject) => {
const video = document.createElement('video');
video.controls = true;
video.preload = 'metadata'; // Only load metadata initially
video.setAttribute('data-size', alFileService.getFilePreviewSize(blob.size));
// Use loadedmetadata instead of loadeddata for faster loading
video.addEventListener(
'loadedmetadata',
() => {
resolve(video);
},
{ once: true }
); // Use once:true for automatic cleanup
video.addEventListener(
'error',
() => {
URL.revokeObjectURL(objectUrl);
reject(new Error('Failed to load video'));
},
{ once: true }
);
video.src = objectUrl;
});
}
arrayBufferToVideo(arrayBuffer, attachment) {
// Create object URL directly instead of using FileReader
const blob = new Blob([arrayBuffer], {
type: attachment.mimeType || 'video/mp4',
});
const objectUrl = URL.createObjectURL(blob);
return new Promise((resolve, reject) => {
const video = document.createElement('video');
video.controls = true;
video.preload = 'metadata'; // Only load metadata initially
video.setAttribute('data-size', alFileService.getFilePreviewSize(blob.size));
// Use loadedmetadata instead of loadeddata for faster loading
video.addEventListener(
'loadedmetadata',
() => {
URL.revokeObjectURL(objectUrl);
resolve(video);
},
{ once: true }
); // Use once:true for automatic cleanup
video.addEventListener(
'error',
() => {
URL.revokeObjectURL(objectUrl);
reject(new Error('Failed to load video'));
},
{ once: true }
);
video.src = objectUrl;
});
}
🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/attachment-service.js around lines
104 to 136, the object URL created for the video blob is never revoked, causing
a memory leak. To fix this, add a call to URL.revokeObjectURL(objectUrl) inside
the 'loadedmetadata' event listener after the video is resolved, ensuring the
URL is freed once the video metadata is loaded.

Comment on lines +62 to +82
return new Promise((resolve, reject) => {
const blob = new Blob([arrayBuffer], { type: attachment.mimeType });

const imgElement = document.createElement('img');

imgElement.setAttribute('class', 'file-preview-link fancybox-media kmfancybox');
imgElement.setAttribute('data-type', attachment.mimeType);
imgElement.setAttribute('data-name', attachment.filename);
imgElement.setAttribute('data-url', imgElement.src);
imgElement.setAttribute('data-size', alFileService.getFilePreviewSize(blob.size));

imgElement.onload = () => {
resolve(imgElement);
};
imgElement.onerror = () => {
URL.revokeObjectURL(blob);
reject(new Error('Failed to load image'));
};
imgElement.src = URL.createObjectURL(blob);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Memory leak & incorrect data-url attribute

  1. data-url is set before src, so it stays empty.
  2. The created object-URL is never revoked on successful load – may leak blobs.
-imgElement.setAttribute('data-url', imgElement.src);
+// set src first, then mirror it in data-url
+imgElement.src = URL.createObjectURL(blob);
+imgElement.setAttribute('data-url', imgElement.src);-imgElement.onload = () => {
-    resolve(imgElement);
-};
+imgElement.onload = () => {
+    URL.revokeObjectURL(imgElement.src); // free memory
+    resolve(imgElement);
+};

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In webplugin/js/app/conversation/mail-parser/attachment-service.js between lines
62 and 82, the data-url attribute is set before the imgElement.src, so it
remains empty, and the created object URL is never revoked on successful image
load, causing a memory leak. Fix this by setting data-url after assigning
imgElement.src to the object URL, and revoke the object URL inside the
imgElement.onload handler to release the blob resource properly.

Comment on lines 5615 to +5621
if (validated) {
mckMessageLayout.messageContextMenu(messageKey);
}
if (messagePxy.metadata.hasOwnProperty('TALK_TO_HUMAN')) {
typingService.setTalkToHumanMsg(true);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against missing metadata before property access

messagePxy.metadata may legitimately be null/undefined. Accessing .hasOwnProperty without a check will crash the render pipeline.

-if (messagePxy.metadata.hasOwnProperty('TALK_TO_HUMAN')) {
+if (
+    messagePxy.metadata &&
+    Object.prototype.hasOwnProperty.call(messagePxy.metadata, 'TALK_TO_HUMAN')
+) {
🤖 Prompt for AI Agents
In webplugin/js/app/mck-sidebox-1.0.js around lines 5615 to 5621, the code
accesses messagePxy.metadata.hasOwnProperty without verifying if metadata
exists, which can cause a crash if metadata is null or undefined. Add a guard
condition to check that messagePxy.metadata is truthy before calling
hasOwnProperty on it to prevent runtime errors.

Comment on lines 670 to 675
console.log('conversation created successfully');
kmWidgetEvents.eventTracking(eventMapping.onStartNewConversation);
KommunicateUI.activateTypingField();
kmNavBar.hideAndShowTalkToHumanBtn();

!data?.groupFeeds.length && kmNavBar.hideAndShowTalkToHumanBtn();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

groupFeeds?.length can throw – add another optional-chaining level

data?.groupFeeds.length will still dereference .length even when groupFeeds is undefined, leading to a TypeError.

-!data?.groupFeeds.length && kmNavBar.hideAndShowTalkToHumanBtn();
+!(data?.groupFeeds?.length) && kmNavBar.hideAndShowTalkToHumanBtn();

This tiny change prevents a hard crash when the conversation is created without feeds.

🤖 Prompt for AI Agents
In webplugin/js/app/mck-sidebox-1.0.js around lines 670 to 675, the expression
data?.groupFeeds.length can cause a TypeError if groupFeeds is undefined because
it tries to access .length directly. To fix this, add an additional optional
chaining operator before .length, changing it to data?.groupFeeds?.length,
ensuring safe access and preventing crashes when groupFeeds is missing.

Comment on lines 7993 to 8010

if (
$applozic('#mck-message-cell .' + msg.key).length > 0 &&
!(CURRENT_GROUP_DATA.TOKENIZE_RESPONSE && msg.type !== 5)
!(msg.tokenMessage && msg.type !== 5)
) {
// if message with same key already rendered skiping rendering it again.
return;
}
if (CURRENT_GROUP_DATA.TOKENIZE_RESPONSE && !msg.tokenMessage) {
// deleting tokenized message after receiving complete message from chat server
const element = document.querySelector(`div[data-msgkey="tokenized_response"]`);
if (element) {
console.log('deleted');
element.remove();
genAiService.resetState();
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Duplicate–message filter misses edge-cases & risks invalid class selectors

  1. '.' + msg.key assumes msg.key is a valid CSS class (cannot start with a digit or contain spaces).
  2. The filter ignores tokenised messages when msg.type === 5, but elsewhere the code uses msg.contentType for similar checks (“104”, “3”, etc.). Verify which field is authoritative.
  3. Consider replacing the class-based lookup with the existing data-msgkey attribute you already rely on later – this avoids CSS validity issues entirely.
    Example refactor:
- if ($applozic('#mck-message-cell .' + msg.key).length > 0 ...
+ if (document.querySelector(`div[data-msgkey="${msg.key}"]`) && ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
$applozic('#mck-message-cell .' + msg.key).length > 0 &&
!(CURRENT_GROUP_DATA.TOKENIZE_RESPONSE && msg.type !== 5)
!(msg.tokenMessage && msg.type !== 5)
) {
// if message with same key already rendered skiping rendering it again.
return;
}
if (CURRENT_GROUP_DATA.TOKENIZE_RESPONSE && !msg.tokenMessage) {
// deleting tokenized message after receiving complete message from chat server
const element = document.querySelector(`div[data-msgkey="tokenized_response"]`);
if (element) {
console.log('deleted');
element.remove();
genAiService.resetState();
}
}
if (
- $applozic('#mck-message-cell .' + msg.key).length > 0 &&
+ document.querySelector(`div[data-msgkey="${msg.key}"]`) &&
!(msg.tokenMessage && msg.type !== 5)
) {
// if message with same key already rendered skiping rendering it again.
return;
}
if (CURRENT_GROUP_DATA.TOKENIZE_RESPONSE && !msg.tokenMessage) {
// deleting tokenized message after receiving complete message from chat server
const element = document.querySelector(`div[data-msgkey="tokenized_response"]`);
if (element) {
console.log('deleted');
element.remove();
genAiService.resetState();
}
}
🤖 Prompt for AI Agents
In webplugin/js/app/mck-sidebox-1.0.js around lines 7993 to 8010, the duplicate
message filter uses a class selector with msg.key, which may be invalid as a CSS
class and can cause edge-case failures. Also, the code inconsistently checks
msg.type instead of msg.contentType for message type validation. To fix this,
replace the class-based selector with a querySelector that uses the data-msgkey
attribute for message lookup, ensuring valid selectors. Additionally, verify and
consistently use the correct field (likely msg.contentType) for type checks to
align with the rest of the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants