Refresh badges on new notifications, bulk actions, snooze and unassign. Drop the second count request that fired on every local status change, look up the timezone once per counts request, and move the count base query into queries.sql.
The new cypress spec drives the view dialog through one group, three
conditions in one group, two groups, the ALL/ANY and AND/OR toggles, a
re-save, a group removal, a rename and a delete. The palette now says
"No results found" in macro mode, and the help center pages link to the
docs.
Found an issue with mentions in the reply editor where suggestions always returned empty results by design. However, the mentionSuggestions stills runs and shows the results pop up with empty list.
This fixes so that mention pop-up simply never appears for normal replies as expected.
Manually tested the behavior locally and no-longer seeing the mention pop-up.
This implementation uses already tracked contact_last_seen conversation field and compares the message createdAt value.
It's also confirmed that this field is only updated for live chat conversations so email conversations falls back to the single check mark.
Testing done on the embedded widget and working as expected.
Fixes#434
Restore upstream conversation create/transcript and mobile sidebar behavior that was accidentally overwritten during cherry-pick. Import onMounted in Sidebar.vue and bound view-count queries with a 10s timeout.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds badges with the number of open conversations next to My inbox,
Mentions, Unassigned, All and each personal or shared view. "Open" means
a conversation whose status category is open, so snoozed and resolved
conversations are not counted.
Backend:
- GET /api/v1/conversations/sidebar-counts returns the counts, gated by
the conversations:read permission. Each individual count is additionally
gated server-side by the permission for that list, so an agent never
receives a number for a list they cannot open.
- View counts are unioned into a single statement, so the number of views
does not drive the number of queries.
- The list-type conditions shared by the conversation list and count
queries are extracted into appendListTypeConditions, so both stay in
sync on what each list type means.
- An empty team list now renders as IN (NULL) instead of the invalid
IN (), which an agent holding a team permission without a team hit.
- The feature can be turned off via app.sidebar_counts_enabled, which
defaults to enabled when the setting is missing.
Frontend:
- Counts refresh when switching inboxes, after the agent's own status or
assignee changes, and from new_conversation and status-carrying
conversation_update websocket events.
- Refreshes are throttled and share a short TTL, so bursts of incoming
conversations collapse into a single request. Re-enabling the feature
bypasses the TTL, because being disabled leaves a zeroed result cached.
Refs #466
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The new-conversation button always read the users label, so a visitor saw
the text meant for logged-in contacts. Pick visitors or users the same way
the home screen does. Adds tests for this and for the allow-plus-prevent
combination on the messages tab, both checked against the old code first.
The country-code length error borrowed the phone number label, so a
too-long country code read as a phone number limit. Adds a countryCode
term for it. ConversationsView also offered the new-conversation button
when prevent_multiple_conversations was set, without checking whether
visitors may start one at all, so it now follows the same rule as the
home screen and the server.
An over-long pre-chat value came back as "Must be at most 128 characters"
with no clue which field it was about, so use the existing fieldTooLong
message. The home screen also offered the start button when the inbox says
visitors cannot start conversations, and the visitor only found out when
the send failed, so the button now follows the same rule as the server.
Adds a browser-level livechat suite that embeds the widget on a host page
the way a customer site does, drives the real widget UI, and covers every
livechat config option that has a visible effect. CI needs the widget rate
limit lifted because the suite makes more than 100 widget requests a minute.
KickUser and CloseAll only wrote a close frame and called Conn.Close,
which fasthttp turns into a no-op on a hijacked conn, so a client that
ignores the frame stayed connected and stayed in the hub. Expire the read
deadline and tear the client down so Serve returns.
The agent app had the same replaced-socket problem as the widget, where a
stale socket's close event cleared the live socket's ping timer and the
server dropped it 60s later. Also check the widget client's closed flag
before refreshing the read deadline, and reset inbox and user id on
re-join.
handleMessage had no stale socket guard, so a socket we already threw
away could still push messages into the chat store. Also only call the
HTTP shutdown a drain timeout when the error is context.DeadlineExceeded,
everything else is a plain shutdown error. Adds unit tests for the
socket guards.
Saving, enabling or disabling a livechat inbox rebuilds the channel,
which calls LiveChat.Close. That only closed each client's outbound
channel. The socket stayed open, so the widget kept exchanging
ping/pong and looked connected while every later agent reply went
nowhere. Only a page reload fixed it.
Calling conn.Close was not enough either. fasthttp makes Close a no-op
on a hijacked conn unless KeepHijackedConns is set, so it returned nil
and the read loop stayed blocked. Client now holds a disconnect func
that expires the read deadline, and the read loop also breaks if its
client was closed.
On the widget side, connect() replaced this.socket without closing the
old one, so a reconnect could leave two live sockets. It now closes the
previous socket and ignores events from it.
Also return input errors from the pre-chat form validation. A blank
required field used to come back as a 500 with no hint about which
field was wrong.
Adds a livechat e2e harness in cypress/support/livechat.js and four
specs: the inbox reload regression, messaging, session and auth, and
settings. 26 tests, and the reload one fails against a binary without
this fix.
The resolved conversations chart filtered on created_at but grouped by resolved_at, so anything created before the window never showed up, and the tag list dropped tags whose conversations were all older than the window. Dropped the message volume COUNT(DISTINCT) and three of the four users table scans, and added the created_at indexes these queries need. The Pending tile is now labelled Awaiting First Reply since it counts conversations with no agent reply yet.