mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 04:57:51 +00:00 
			
		
		
		
	# Pull Request Template
## Description
**Issue**
This PR fixes template variables in messages (e.g., {{customer.name}})
that were being incorrectly converted to clickable links by the
`MessageFormatter's linkify` functionality. This caused formatting
issues and broken links.
**Solution**
Added a `linkify` parameter to `MessageFormatter` to optionally disable
link conversion
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Screenshots**
**Before**
<img width="1012" alt="image"
src="https://github.com/user-attachments/assets/70abb238-b4d9-439d-9e51-c7513cf482fb"
/>
**After**
<img width="1012" alt="image"
src="https://github.com/user-attachments/assets/387acb74-674e-4b26-85cc-2d7190d256b1"
/>
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
		
	
		
			
				
	
	
		
			85 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			85 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import MessageFormatter from '../helpers/MessageFormatter';
 | 
						|
 | 
						|
/**
 | 
						|
 * A composable providing utility functions for message formatting.
 | 
						|
 *
 | 
						|
 * @returns {Object} A set of functions for message formatting.
 | 
						|
 */
 | 
						|
export const useMessageFormatter = () => {
 | 
						|
  /**
 | 
						|
   * Formats a message based on specified conditions.
 | 
						|
   *
 | 
						|
   * @param {string} message - The message to be formatted.
 | 
						|
   * @param {boolean} isATweet - Whether the message is a tweet.
 | 
						|
   * @param {boolean} isAPrivateNote - Whether the message is a private note.
 | 
						|
   * @returns {string} - The formatted message.
 | 
						|
   */
 | 
						|
  // TODO: ref:https://github.com/chatwoot/chatwoot/pull/10725#discussion_r1925300874
 | 
						|
  const formatMessage = (message, isATweet, isAPrivateNote, linkify) => {
 | 
						|
    const messageFormatter = new MessageFormatter(
 | 
						|
      message,
 | 
						|
      isATweet,
 | 
						|
      isAPrivateNote,
 | 
						|
      linkify
 | 
						|
    );
 | 
						|
    return messageFormatter.formattedMessage;
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * Converts a message to plain text.
 | 
						|
   *
 | 
						|
   * @param {string} message - The message to be converted.
 | 
						|
   * @param {boolean} isATweet - Whether the message is a tweet.
 | 
						|
   * @returns {string} - The plain text message.
 | 
						|
   */
 | 
						|
  const getPlainText = (message, isATweet) => {
 | 
						|
    const messageFormatter = new MessageFormatter(message, isATweet);
 | 
						|
    return messageFormatter.plainText;
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * Truncates a description to a maximum length of 100 characters.
 | 
						|
   *
 | 
						|
   * @param {string} [description=''] - The description to be truncated.
 | 
						|
   * @returns {string} - The truncated description.
 | 
						|
   */
 | 
						|
  const truncateMessage = (description = '') => {
 | 
						|
    if (description.length < 100) {
 | 
						|
      return description;
 | 
						|
    }
 | 
						|
 | 
						|
    return `${description.slice(0, 97)}...`;
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * Highlights occurrences of a search term within given content.
 | 
						|
   *
 | 
						|
   * @param {string} [content=''] - The content in which to search.
 | 
						|
   * @param {string} [searchTerm=''] - The term to search for.
 | 
						|
   * @param {string} [highlightClass=''] - The CSS class to apply to the highlighted term.
 | 
						|
   * @returns {string} - The content with highlighted terms.
 | 
						|
   */
 | 
						|
  const highlightContent = (
 | 
						|
    content = '',
 | 
						|
    searchTerm = '',
 | 
						|
    highlightClass = ''
 | 
						|
  ) => {
 | 
						|
    const plainTextContent = getPlainText(content);
 | 
						|
 | 
						|
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
 | 
						|
    const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
 | 
						|
 | 
						|
    return plainTextContent.replace(
 | 
						|
      new RegExp(`(${escapedSearchTerm})`, 'ig'),
 | 
						|
      `<span class="${highlightClass}">$1</span>`
 | 
						|
    );
 | 
						|
  };
 | 
						|
 | 
						|
  return {
 | 
						|
    formatMessage,
 | 
						|
    getPlainText,
 | 
						|
    truncateMessage,
 | 
						|
    highlightContent,
 | 
						|
  };
 | 
						|
};
 |