Merge branch 'develop' into feature/service-worker-caching

This commit is contained in:
Vinay Keerthi
2026-01-12 14:13:27 +05:30
committed by GitHub
7 changed files with 71 additions and 14 deletions
+3 -2
View File
@@ -237,10 +237,11 @@ export const MARKDOWN_PATTERNS = [
patterns: [{ pattern: /`([^`]+)`/g, replacement: '$1' }],
},
{
type: 'link', // PM: link, eg: [text](url) or <url>
type: 'link', // PM: link
patterns: [
{ pattern: /\[([^\]]+)\]\([^)]+\)/g, replacement: '$1' }, // [text](url) -> text
{ pattern: /<(https?:\/\/[^>]+)>/g, replacement: '$1' }, // <url> -> url (autolinks)
{ pattern: /<([a-zA-Z][a-zA-Z0-9+.-]*:[^\s>]+)>/g, replacement: '$1' }, // <https://...>, <mailto:...>, <tel:...>, <ftp://...>, etc
{ pattern: /<([^\s@]+@[^\s@>]+)>/g, replacement: '$1' }, // <user@example.com> -> user@example.com
],
},
];
@@ -901,6 +901,17 @@ describe('stripUnsupportedFormatting', () => {
expect(stripUnsupportedFormatting(content, fullSchema)).toBe(content);
});
it('preserves various URI scheme autolinks', () => {
const content =
'Email <mailto:user@example.com> or call <tel:+1234567890>';
expect(stripUnsupportedFormatting(content, fullSchema)).toBe(content);
});
it('preserves email autolinks', () => {
const content = 'Contact us at <support@chatwoot.com>';
expect(stripUnsupportedFormatting(content, fullSchema)).toBe(content);
});
it('preserves lists when schema supports them', () => {
const content = '- item 1\n- item 2\n1. first\n2. second';
expect(stripUnsupportedFormatting(content, fullSchema)).toBe(content);
@@ -984,6 +995,26 @@ describe('stripUnsupportedFormatting', () => {
expect(stripUnsupportedFormatting(content, emptySchema)).toBe(expected);
});
it('converts URI scheme autolinks to plain text', () => {
const content =
'Email <mailto:support@example.com> or call <tel:+1234567890>';
const expected =
'Email mailto:support@example.com or call tel:+1234567890';
expect(stripUnsupportedFormatting(content, emptySchema)).toBe(expected);
});
it('converts email autolinks to plain text', () => {
const content = 'Reach us at <admin@chatwoot.com> for help';
const expected = 'Reach us at admin@chatwoot.com for help';
expect(stripUnsupportedFormatting(content, emptySchema)).toBe(expected);
});
it('handles mixed autolink types', () => {
const content = 'Visit <https://example.com> or email <info@example.com>';
const expected = 'Visit https://example.com or email info@example.com';
expect(stripUnsupportedFormatting(content, emptySchema)).toBe(expected);
});
it('strips bullet list markers', () => {
expect(
stripUnsupportedFormatting('- item 1\n- item 2', emptySchema)
+12 -4
View File
@@ -14,11 +14,19 @@ class ActionCableListener < BaseListener
end
def notification_deleted(event)
return if event.data[:notification].user.blank?
notification_data = event.data[:notification_data]
notification, account, unread_count, count = extract_notification_and_account(event)
tokens = [event.data[:notification].user.pubsub_token]
broadcast(account, tokens, NOTIFICATION_DELETED, { notification: { id: notification.id }, unread_count: unread_count, count: count })
user = User.find_by(id: notification_data[:user_id])
account = Account.find_by(id: notification_data[:account_id])
return if user.blank? || account.blank?
notification_finder = NotificationFinder.new(user, account)
tokens = [user.pubsub_token]
broadcast(account, tokens, NOTIFICATION_DELETED, {
notification: { id: notification_data[:id] },
unread_count: notification_finder.unread_count,
count: notification_finder.count
})
end
def account_cache_invalidated(event)
+11 -1
View File
@@ -180,7 +180,17 @@ class Notification < ApplicationRecord
end
def dispatch_destroy_event
Rails.configuration.dispatcher.dispatch(NOTIFICATION_DELETED, Time.zone.now, notification: self)
# Pass serialized data instead of ActiveRecord object to avoid DeserializationError
# when the async EventDispatcherJob runs after the notification has been deleted
Rails.configuration.dispatcher.dispatch(
NOTIFICATION_DELETED,
Time.zone.now,
notification_data: {
id: id,
user_id: user_id,
account_id: account_id
}
)
end
def set_last_activity_at
+1 -1
View File
@@ -34,7 +34,7 @@
"dependencies": {
"@breezystack/lamejs": "^1.2.7",
"@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.3.4",
"@chatwoot/prosemirror-schema": "1.3.5",
"@chatwoot/utils": "^0.0.51",
"@formkit/core": "^1.6.7",
"@formkit/vue": "^1.6.7",
+5 -5
View File
@@ -23,8 +23,8 @@ importers:
specifier: 1.2.3
version: 1.2.3
'@chatwoot/prosemirror-schema':
specifier: 1.3.4
version: 1.3.4
specifier: 1.3.5
version: 1.3.5
'@chatwoot/utils':
specifier: ^0.0.51
version: 0.0.51
@@ -454,8 +454,8 @@ packages:
'@chatwoot/ninja-keys@1.2.3':
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
'@chatwoot/prosemirror-schema@1.3.4':
resolution: {integrity: sha512-XY1UyG9topVy7fNCt45YgiU/QzlJJRhrcrSWcvAIIcDYKomnEcGOyeAfM3WeFD6GgOuLzRUyIHrW+WQGpHaZvA==}
'@chatwoot/prosemirror-schema@1.3.5':
resolution: {integrity: sha512-3Koj3jwO1qOxJG84D4FqPOJ6o8k6ehZi1zedO3vKRERATm2Cy1p+ET6FEvVYWUpoBvDwR6hNVScXrcNNVobhsA==}
'@chatwoot/utils@0.0.51':
resolution: {integrity: sha512-WlEmWfOTzR7YZRUWzn5Wpm15/BRudpwqoNckph8TohyDbiim1CP4UZGa+qjajxTbNGLLhtKlm0Xl+X16+5Wceg==}
@@ -4970,7 +4970,7 @@ snapshots:
hotkeys-js: 3.8.7
lit: 2.2.6
'@chatwoot/prosemirror-schema@1.3.4':
'@chatwoot/prosemirror-schema@1.3.5':
dependencies:
markdown-it-sup: 2.0.0
prosemirror-commands: 1.6.0
+8 -1
View File
@@ -132,7 +132,14 @@ describe ActionCableListener do
describe '#notification_deleted' do
let(:event_name) { :'notification.deleted' }
let!(:notification) { create(:notification, account: account, user: agent) }
let!(:event) { Events::Base.new(event_name, Time.zone.now, notification: notification) }
let(:notification_data) do
{
id: notification.id,
user_id: agent.id,
account_id: account.id
}
end
let!(:event) { Events::Base.new(event_name, Time.zone.now, notification_data: notification_data) }
it 'sends message to account admins, inbox agents' do
expect(ActionCableBroadcastJob).to receive(:perform_later).with(