Compare commits

...
Author SHA1 Message Date
Sivin VargheseandGitHub fcdc9d4388 Merge branch 'develop' into chore/upgrade-packages-2-25 2025-02-11 15:00:49 +05:30
PranavandGitHub 4b12a8a51e chore: Add more conversation events for reload (#10877)
Followup PR for https://github.com/chatwoot/chatwoot/pull/10876. This PR
just adds all the events related to conversation update to be reloaded
before sending it to the UI.
2025-02-11 00:33:45 -08:00
Muhsin KelothandGitHub f639d8ca51 Merge branch 'develop' into chore/upgrade-packages-2-25 2025-02-11 13:20:46 +05:30
PranavandGitHub 3c78d25306 chore: Reload conversation data in ActionCableBroadcastJob before sending (#10876)
During high-traffic periods, events may appear out of order, causing the
conversation job to queue outdated data, which can lead to issues in the
UI. This update ensures that only the latest available data is sent to the UI.

The conversation object is refreshed before sending it to the UI.
2025-02-10 23:16:15 -08:00
PranavandGitHub 8faccba052 chore: Update the precision of the updated_at timestamp in conversation model (#10875)
Use to_f instead of to_i to preserve the millisecond precision in the UI.
2025-02-10 20:22:11 -08:00
PranavandGitHub 02000de905 chore: Add updated_at attribute to the conversation event (#10873)
This PR adds updated_at attribute to the conversation event.
2025-02-10 19:33:26 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d7c0507e33 chore(deps): Bump net-imap from 0.4.17 to 0.4.19 (#10871)
Bumps [net-imap](https://github.com/ruby/net-imap) from 0.4.17 to
0.4.19.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ruby/net-imap/releases">net-imap's
releases</a>.</em></p>
<blockquote>
<h2>v0.4.19</h2>
<h2>What's Changed</h2>
<h3>🔒 Security Fix</h3>
<p>Fixes CVE-2025-25186 (GHSA-7fc5-f82f-cx69): A malicious server can
exhaust client memory by sending <code>APPENDUID</code> or
<code>COPYUID</code> responses with very large <code>uid-set</code>
ranges. <code>Net::IMAP::UIDPlusData</code> expands these ranges into
arrays of integers.</p>
<h4>Fix with minor API changes</h4>
<p>Set <code>config.parser_use_deprecated_uidplus_data</code> to
<code>false</code> to replace <code>UIDPlusData</code> with
<code>AppendUIDData</code> and <code>CopyUIDData</code>. These classes
store their UIDs as <code>Net::IMAP::SequenceSet</code> objects
(<em>not</em> expanded into arrays of integers). Code that does not
handle <code>APPENDUID</code> or <code>COPYUID</code> responses should
not see any difference. Code that does handle these responses
<em>may</em> need to be updated.</p>
<p>For v0.3.8, this option is not available
For v0.4.19, the default value is <code>true</code>.
For v0.5.6, the default value is <code>:up_to_max_size</code>.
For v0.6.0, the only allowed value will be <code>false</code>
<em>(<code>UIDPlusData</code> will be removed from v0.6)</em>.</p>
<h4>Mitigate with backward compatible API</h4>
<p>Adjust <code>config.parser_max_deprecated_uidplus_data_size</code> to
limit the maximum <code>UIDPlusData</code> UID set size.
When <code>config.parser_use_deprecated_uidplus_data == true</code>,
larger sets will crash.
When <code>config.parser_use_deprecated_uidplus_data ==
:up_to_max_size</code>, larger sets will use <code>AppendUIDData</code>
or <code>CopyUIDData</code>.</p>
<p>For v0.3,8, this limit is <em>hard-coded</em> to 10,000.
For v0.4.19, this limit defaults to 1000.
For v0.5.6, this limit defaults to 100.
For v0.6.0, the only allowed value will be <code>0</code>
<em>(<code>UIDPlusData</code> will be removed from v0.6)</em>.</p>
<h4>Please Note: unhandled responses</h4>
<p>If the client does not add response handlers to prune unhandled
responses, a malicious server can still eventually exhaust all client
memory, by repeatedly sending malicious responses. However,
<code>net-imap</code> has always retained unhandled responses, and it
has always been necessary for long-lived connections to prune these
responses. This is not significantly different from connecting to a
trusted server with a long-lived connection. To limit the maximum number
of retained responses, a simple handler might look something like the
following:</p>
<pre lang="ruby"><code>limit = 1000
imap.add_response_handler do |resp|
  next unless resp.respond_to?(:name) &amp;&amp; resp.respond_to?(:data)
  name = resp.name
code = resp.data.code&amp;.name if
resp.data.in?(Net::IMAP::ResponseText)
  imap.responses(name) { _1.slice!(0...-limit) }
  imap.responses(code) { _1.slice!(0...-limit) }
end
</code></pre>
<h3>Added</h3>
<ul>
<li>🔧 ResponseParser config is mutable and non-global (backports <a
href="https://redirect.github.com/ruby/net-imap/issues/381">#381</a>) by
<a href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/382">ruby/net-imap#382</a></li>
<li>✨ SequenceSet ordered entries methods (backports to v0.4-stable) by
<a href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/402">ruby/net-imap#402</a>
Backports the following:
<ul>
<li>✨ Add SequenceSet methods for querying about duplicates by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/384">ruby/net-imap#384</a></li>
<li>✨ Add <code>SequenceSet#each_ordered_number</code> by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/386">ruby/net-imap#386</a></li>
<li>✨ Add <code>SequenceSet#find_ordered_index</code> by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/396">ruby/net-imap#396</a></li>
<li>✨ Add <code>SequenceSet#ordered_at</code> by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/397">ruby/net-imap#397</a></li>
</ul>
</li>
<li>✨ Backport UIDPlusData, AppendUIDData, CopyUIDData to v0.4 by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/404">ruby/net-imap#404</a>
Backports the following:
<ul>
<li>✨ Add AppendUIDData and CopyUIDData classes by <a
href="https://github.com/nevans"><code>@​nevans</code></a> in <a
href="https://redirect.github.com/ruby/net-imap/pull/400">ruby/net-imap#400</a></li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/ruby/net-imap/commit/4c4ed09997ccd108a50fd7b30edb1b32806a162f"><code>4c4ed09</code></a>
🔖 Bump version to 0.4.19</li>
<li><a
href="https://github.com/ruby/net-imap/commit/c8c5a643739d2669f0c9a6bb9770d0c045fd74a3"><code>c8c5a64</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/ruby/net-imap/commit/abff00fd700bd71ece3b3f841d4428028f16b8bb"><code>abff00f</code></a>
🔧 Add <code>:up_to_max_size</code> config for UIDPlusData</li>
<li><a
href="https://github.com/ruby/net-imap/commit/34a1f27a457be5236848543f3221b33c2494a34c"><code>34a1f27</code></a>
🔧 Add config option for max UIDPlusData size</li>
<li><a
href="https://github.com/ruby/net-imap/commit/6613d57e8eb885d65903819a2f83919a164a3680"><code>6613d57</code></a>
🔒 Limit exponential memory usage to parse uid-set</li>
<li><a
href="https://github.com/ruby/net-imap/commit/e4d57b1e000794882d5bca0f4cf7aa0658a83da7"><code>e4d57b1</code></a>
🔀 Merge pull request <a
href="https://redirect.github.com/ruby/net-imap/issues/404">#404</a>
from ruby/backport-0.4-uidplus-deprecation</li>
<li><a
href="https://github.com/ruby/net-imap/commit/d32320a74980a8754c5da3389693af8f597ab39c"><code>d32320a</code></a>
🐛 Fix missing <code>Data.define</code> for new classes</li>
<li><a
href="https://github.com/ruby/net-imap/commit/3c592fc98c26f11043a2b8e112bc91f31d7efad0"><code>3c592fc</code></a>
🔧🗑️ Deprecate UIDPlusData, with config to upgrade</li>
<li><a
href="https://github.com/ruby/net-imap/commit/7e58ef35fae7b52eb24b39c87f5b7fc69fa3e757"><code>7e58ef3</code></a>
✨ Add CopyUIDData (to replace UIDPlusData)</li>
<li><a
href="https://github.com/ruby/net-imap/commit/4c601c3a8468104eba5f9ee474f753064bf62115"><code>4c601c3</code></a>
✨ Add AppendUIDData (to replace UIDPlusData)</li>
<li>Additional commits viewable in <a
href="https://github.com/ruby/net-imap/compare/v0.4.17...v0.4.19">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=net-imap&package-manager=bundler&previous-version=0.4.17&new-version=0.4.19)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/chatwoot/chatwoot/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 17:35:12 -08:00
Shivam Mishra a7f0476e19 feat: update depedencies 2025-02-10 14:42:44 +05:30
Shivam Mishra 51e0c1b9d9 feat: upgade icons 2025-02-10 14:30:32 +05:30
Shivam Mishra a8c57f692e feat: move material-symbols to dev dependency 2025-02-10 14:29:45 +05:30
Sivin VargheseandGitHub e97e68b1ba fix: Message signature is not appending (#10855)
# Pull Request Template

## Description

**Issue:** The message signature wasn't being appended to new email
conversations when a target inbox was selected.

**Solution:** To address this, a reusable `handleSignatureSetup`
function was created to manage the signature logic. The same logic was
applied in both cases, when the inbox selection changed (using `watch`)
and during the initial load (using `mounted`).

Fixes
https://linear.app/chatwoot/issue/CW-4005/allow-to-activate-the-message-signature-for-new-email-conversations-by
https://github.com/chatwoot/chatwoot/issues/10836

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

**Steps to reproduce**:
https://github.com/chatwoot/chatwoot/issues/10836#issuecomment-2637354304

### Loom video

**Before**

https://www.loom.com/share/ccf597cfa8d94d0eaff1222102901d2c?sid=abfea42b-425e-446e-8e92-99359b786607

**After**

https://www.loom.com/share/d9deddfcf8de48ab87e31911dfb774d8?sid=c1aac19b-b243-428e-9a9f-2ad9f4efe49c


## 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
- [ ] 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
2025-02-07 16:28:27 +05:30
be1999e7f8 fix: re-rendering of components when shifting from the unread list to the read list (#10835)
Fixes https://github.com/chatwoot/chatwoot/issues/10812

Demo


https://github.com/user-attachments/assets/a0f7eb64-8f6e-4992-a163-c972e85fb205

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-02-07 08:54:25 +05:30
12 changed files with 495 additions and 556 deletions
+3 -3
View File
@@ -186,7 +186,7 @@ GEM
database_cleaner-core (2.0.1)
datadog-ci (0.8.3)
msgpack
date (3.3.4)
date (3.4.1)
ddtrace (1.23.2)
datadog-ci (~> 0.8.1)
debase-ruby_core_source (= 3.3.1)
@@ -487,7 +487,7 @@ GEM
uri
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.4.17)
net-imap (0.4.19)
date
net-protocol
net-pop (0.1.2)
@@ -782,7 +782,7 @@ GEM
time_diff (0.3.0)
activesupport
i18n
timeout (0.4.1)
timeout (0.4.3)
trailblazer-option (0.1.2)
twilio-ruby (5.77.0)
faraday (>= 0.9, < 3.0)
@@ -77,7 +77,7 @@ const toggleMessageSignature = () => {
setSignature();
};
// Added this watch to dynamically set signature.
// Added this watch to dynamically set signature on target inbox change.
// Only targetInbox has value and is Advance Editor(used by isEmailOrWebWidgetInbox)
// Set the signature only if the inbox based flag is true
watch(
@@ -86,7 +86,8 @@ watch(
nextTick(() => {
if (newValue && props.isEmailOrWebWidgetInbox) setSignature();
});
}
},
{ immediate: true }
);
const onClickInsertEmoji = emoji => {
@@ -15,18 +15,14 @@ import { useCamelCase } from 'dashboard/composables/useTransformKeys';
* @property {Array} messages - Array of all messages [These are not in camelcase]
*/
const props = defineProps({
readMessages: {
type: Array,
default: () => [],
},
unReadMessages: {
type: Array,
default: () => [],
},
currentUserId: {
type: Number,
required: true,
},
firstUnreadId: {
type: Number,
default: null,
},
isAnEmailChannel: {
type: Boolean,
default: false,
@@ -41,12 +37,8 @@ const props = defineProps({
},
});
const unread = computed(() => {
return useCamelCase(props.unReadMessages, { deep: true });
});
const read = computed(() => {
return useCamelCase(props.readMessages, { deep: true });
const allMessages = computed(() => {
return useCamelCase(props.messages, { deep: true });
});
/**
@@ -108,26 +100,18 @@ const getInReplyToMessage = parentMessage => {
<template>
<ul class="px-4 bg-n-background">
<slot name="beforeAll" />
<template v-for="(message, index) in read" :key="message.id">
<Message
v-bind="message"
:is-email-inbox="isAnEmailChannel"
:in-reply-to="getInReplyToMessage(message)"
:group-with-next="shouldGroupWithNext(index, read)"
:inbox-supports-reply-to="inboxSupportsReplyTo"
:current-user-id="currentUserId"
data-clarity-mask="True"
<template v-for="(message, index) in allMessages" :key="message.id">
<slot
v-if="firstUnreadId && message.id === firstUnreadId"
name="unreadBadge"
/>
</template>
<slot name="beforeUnread" />
<template v-for="(message, index) in unread" :key="message.id">
<Message
v-bind="message"
:is-email-inbox="isAnEmailChannel"
:in-reply-to="getInReplyToMessage(message)"
:group-with-next="shouldGroupWithNext(index, unread)"
:group-with-next="shouldGroupWithNext(index, allMessages)"
:inbox-supports-reply-to="inboxSupportsReplyTo"
:current-user-id="currentUserId"
:is-email-inbox="isAnEmailChannel"
data-clarity-mask="True"
/>
</template>
@@ -38,9 +38,10 @@ onMounted(() => {
});
const formatTime = time => {
if (!time || Number.isNaN(time)) return '00:00';
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
const toggleMute = () => {
@@ -49,7 +50,7 @@ const toggleMute = () => {
};
const onTimeUpdate = () => {
currentTime.value = audioPlayer.value.currentTime;
currentTime.value = audioPlayer.value?.currentTime;
};
const seek = event => {
@@ -243,6 +243,15 @@ export default {
unreadMessageCount() {
return this.currentChat.unread_count || 0;
},
unreadMessageLabel() {
const count =
this.unreadMessageCount > 9 ? '9+' : this.unreadMessageCount;
const label =
this.unreadMessageCount > 1
? 'CONVERSATION.UNREAD_MESSAGES'
: 'CONVERSATION.UNREAD_MESSAGE';
return `${count} ${this.$t(label)}`;
},
isInstagramDM() {
return this.conversationType === 'instagram_direct_message';
},
@@ -492,12 +501,11 @@ export default {
<NextMessageList
v-if="showNextBubbles"
class="conversation-panel"
:read-messages="readMessages"
:un-read-messages="unReadMessages"
:current-user-id="currentUserId"
:first-unread-id="unReadMessages[0]?.id"
:is-an-email-channel="isAnEmailChannel"
:inbox-supports-reply-to="inboxSupportsReplyTo"
:messages="currentChat ? currentChat.messages : []"
:messages="getMessages"
>
<template #beforeAll>
<transition name="slide-up">
@@ -507,15 +515,10 @@ export default {
</li>
</transition>
</template>
<template #beforeUnread>
<template #unreadBadge>
<li v-show="unreadMessageCount != 0" class="unread--toast">
<span>
{{ unreadMessageCount > 9 ? '9+' : unreadMessageCount }}
{{
unreadMessageCount > 1
? $t('CONVERSATION.UNREAD_MESSAGES')
: $t('CONVERSATION.UNREAD_MESSAGE')
}}
{{ unreadMessageLabel }}
</span>
</li>
</template>
+36 -1
View File
@@ -1,9 +1,44 @@
class ActionCableBroadcastJob < ApplicationJob
queue_as :critical
include Events::Types
CONVERSATION_UPDATE_EVENTS = [
CONVERSATION_READ,
CONVERSATION_UPDATED,
TEAM_CHANGED,
ASSIGNEE_CHANGED,
CONVERSATION_STATUS_CHANGED
].freeze
def perform(members, event_name, data)
return if members.blank?
broadcast_data = prepare_broadcast_data(event_name, data)
broadcast_to_members(members, event_name, broadcast_data)
end
private
# Ensures that only the latest available data is sent to prevent UI issues
# caused by out-of-order events during high-traffic periods. This prevents
# the conversation job from processing outdated data.
def prepare_broadcast_data(event_name, data)
return data unless CONVERSATION_UPDATE_EVENTS.include?(event_name)
account = Account.find(data[:account_id])
conversation = account.conversations.find_by!(display_id: data[:id])
conversation.push_event_data.merge(account_id: data[:account_id])
end
def broadcast_to_members(members, event_name, broadcast_data)
members.each do |member|
ActionCable.server.broadcast(member, { event: event_name, data: data })
ActionCable.server.broadcast(
member,
{
event: event_name,
data: broadcast_data
}
)
end
end
end
@@ -42,7 +42,8 @@ class Conversations::EventDataPresenter < SimpleDelegator
contact_last_seen_at: contact_last_seen_at.to_i,
last_activity_at: last_activity_at.to_i,
timestamp: last_activity_at.to_i,
created_at: created_at.to_i
created_at: created_at.to_i,
updated_at: updated_at.to_f
}
end
end
@@ -44,6 +44,7 @@ json.muted conversation.muted?
json.snoozed_until conversation.snoozed_until
json.status conversation.status
json.created_at conversation.created_at.to_i
json.updated_at conversation.updated_at.to_f
json.timestamp conversation.last_activity_at.to_i
json.first_reply_created_at conversation.first_reply_created_at.to_i
json.unread_count conversation.unread_incoming_messages.count
+22 -22
View File
@@ -34,18 +34,17 @@
"@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.1.1-next",
"@chatwoot/utils": "^0.0.35",
"@formkit/core": "^1.6.7",
"@formkit/vue": "^1.6.7",
"@formkit/core": "^1.6.9",
"@formkit/vue": "^1.6.9",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
"@highlightjs/vue-plugin": "^2.1.0",
"@iconify-json/material-symbols": "^1.2.10",
"@june-so/analytics-next": "^2.0.0",
"@lk77/vue3-color": "^3.0.6",
"@radix-ui/colors": "^3.0.0",
"@rails/actioncable": "6.1.3",
"@rails/ujs": "^7.1.400",
"@scmmishra/pico-search": "0.5.4",
"@sentry/vue": "^8.31.0",
"@sentry/vue": "^8.54.0",
"@sindresorhus/slugify": "2.2.1",
"@tailwindcss/typography": "^0.5.15",
"@tanstack/vue-table": "^8.20.5",
@@ -53,25 +52,25 @@
"@vue/compiler-sfc": "^3.5.8",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
"@vueuse/components": "^12.0.0",
"@vueuse/core": "^12.0.0",
"@vueuse/components": "^12.5.0",
"@vueuse/core": "^12.5.0",
"activestorage": "^5.2.6",
"axios": "^1.7.7",
"axios": "^1.7.9",
"camelcase-keys": "^9.1.3",
"chart.js": "~4.4.4",
"color2k": "^2.0.2",
"company-email-validator": "^1.1.0",
"core-js": "3.38.1",
"countries-and-timezones": "^3.6.0",
"countries-and-timezones": "^3.7.2",
"date-fns": "2.21.1",
"date-fns-tz": "^1.3.3",
"dompurify": "3.1.6",
"flag-icons": "^7.2.3",
"flag-icons": "^7.3.2",
"floating-vue": "^5.2.2",
"highlight.js": "^11.10.0",
"idb": "^8.0.0",
"highlight.js": "^11.11.1",
"idb": "^8.0.2",
"js-cookie": "^3.0.5",
"libphonenumber-js": "^1.11.9",
"libphonenumber-js": "^1.11.19",
"markdown-it": "^13.0.2",
"markdown-it-link-attributes": "^4.0.1",
"md5": "^2.3.0",
@@ -86,10 +85,10 @@
"video.js": "7.18.1",
"videojs-record": "4.5.0",
"videojs-wavesurfer": "3.8.0",
"vue": "^3.5.12",
"vue": "^3.5.13",
"vue-chartjs": "5.3.1",
"vue-datepicker-next": "^1.0.3",
"vue-dompurify-html": "^5.1.0",
"vue-dompurify-html": "^5.2.0",
"vue-i18n": "9.14.2",
"vue-letter": "^0.2.0",
"vue-multiselect": "3.1.0",
@@ -103,18 +102,19 @@
"wavesurfer.js": "7.8.6"
},
"devDependencies": {
"@egoist/tailwindcss-icons": "^1.8.1",
"@egoist/tailwindcss-icons": "^1.9.0",
"@histoire/plugin-vue": "0.17.15",
"@iconify-json/logos": "^1.2.3",
"@iconify-json/lucide": "^1.2.11",
"@iconify-json/ph": "^1.2.1",
"@iconify-json/ri": "^1.2.3",
"@iconify-json/teenyicons": "^1.2.1",
"@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.26",
"@iconify-json/material-symbols": "^1.2.14",
"@iconify-json/ph": "^1.2.2",
"@iconify-json/ri": "^1.2.5",
"@iconify-json/teenyicons": "^1.2.2",
"@size-limit/file": "^8.2.4",
"@vitest/coverage-v8": "3.0.5",
"@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-interactive": "^11.1.0",
@@ -133,7 +133,7 @@
"prettier": "^3.3.3",
"prosemirror-model": "^1.22.3",
"size-limit": "^8.2.4",
"tailwindcss": "^3.4.13",
"tailwindcss": "^3.4.17",
"vite": "^5.4.12",
"vite-plugin-ruby": "^5.0.0",
"vitest": "3.0.5"
+398 -487
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -538,6 +538,7 @@ RSpec.describe Conversation do
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
created_at: conversation.created_at.to_i,
updated_at: conversation.updated_at.to_f,
waiting_since: conversation.waiting_since.to_i,
priority: nil,
unread_count: 0
@@ -31,6 +31,7 @@ RSpec.describe Conversations::EventDataPresenter do
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
created_at: conversation.created_at.to_i,
updated_at: conversation.updated_at.to_f,
waiting_since: conversation.waiting_since.to_i,
priority: nil,
unread_count: 0