feat(web): add bulk resend button and mobile UI improvements to search messages#901
Conversation
…h messages - Add RESEND button with confirmation dialog for bulk resending messages - Resend is only enabled when all selected messages are MT with expired/failed status - Hide RESEND button on mobile - On mobile: remove icons from DELETE, EXPORT, and SEARCH buttons, show uppercase text only - On desktop: keep original icons and text for DELETE, EXPORT, and SEARCH buttons Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Greptile SummaryThis PR adds a bulk RESEND button to the search-messages page for outbound messages with
Confidence Score: 3/5The resend feature works end-to-end, but each individual sendMessage call fires its own toast, so a bulk resend of N messages floods the UI with N+1 notifications — worth fixing before merging. The duplicate-notification issue is a real, reproducible defect in the bulk resend path that fires on every successful resend operation. The rest of the change — mobile breakpoint logic, conditional icons, the canResendSelected guard, and the confirmation dialog — is straightforward and looks correct. web/pages/search-messages/index.vue — specifically the resendMessages method and its interaction with the sendMessage store action Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant UI as SearchMessages (Vue)
participant Store as Vuex Store
participant API as /v1/messages/send
User->>UI: Select expired/failed MT messages
UI->>UI: "canResendSelected = true"
User->>UI: Click RESEND confirm dialog
UI->>Store: Promise.all sendMessage calls
loop For each message
Store->>API: POST /v1/messages/send
API-->>Store: 200 OK
Store-->>UI: addNotification per-message
end
UI->>UI: addNotification queued for resending
UI->>UI: "selectedMessages = []"
UI->>Store: fetchMessages
Reviews (1): Last reviewed commit: "feat(web): include request_id when resen..." | Re-trigger Greptile |
| resendMessages() { | ||
| this.loading = true | ||
| Promise.all( | ||
| this.selectedMessages.map((message) => | ||
| this.$store.dispatch('sendMessage', { | ||
| from: message.owner, | ||
| to: message.contact, | ||
| content: message.content, | ||
| sim: message.sim, | ||
| request_id: message.request_id, | ||
| }), | ||
| ), | ||
| ) | ||
| .then(() => { | ||
| this.$store.dispatch('addNotification', { | ||
| message: 'The selected messages have been queued for resending', | ||
| type: 'success', | ||
| }) | ||
| this.selectedMessages = [] | ||
| }) | ||
| .catch(() => { | ||
| this.$store.dispatch('addNotification', { | ||
| message: 'Error while resending the selected messages', | ||
| type: 'error', | ||
| }) | ||
| }) | ||
| .finally(() => { | ||
| this.loading = false | ||
| this.showResendDialog = false | ||
| this.fetchMessages() | ||
| }) |
There was a problem hiding this comment.
Duplicate success notifications on resend
resendMessages calls Promise.all over multiple sendMessage dispatches. Each sendMessage action already calls addNotification with the API's own response message on success (see store/index.ts ~L720). Then the .then() block here adds a second "queued for resending" notification on top. Resending N messages fires N individual API-level notifications plus 1 bulk one, producing N+1 toasts and confusing the user.
Consider dispatching a raw Axios call instead of reusing the sendMessage store action (which bundles its own notification), or suppressing the per-message notification in the store action when called in bulk.
There was a problem hiding this comment.
Fixed. Replaced the sendMessage store action with a direct axios.post call to avoid per-message notifications. Now only a single bulk notification is shown.
| ) | ||
| .then(() => { | ||
| this.$store.dispatch('addNotification', { | ||
| message: 'The selected messages have been queued for resending', | ||
| type: 'success', | ||
| }) | ||
| this.selectedMessages = [] | ||
| }) | ||
| .catch(() => { | ||
| this.$store.dispatch('addNotification', { | ||
| message: 'Error while resending the selected messages', | ||
| type: 'error', |
There was a problem hiding this comment.
Partial failures leave selection state inconsistent
Promise.all short-circuits on the first rejection, but any sendMessage calls that already resolved before the failure have already been queued on the server. The .catch() block shows a generic "Error while resending" message and does not clear selectedMessages, leaving the user with no indication of which messages were successfully requeued vs. which failed. At minimum, the error message could note that some messages may have been sent, or Promise.allSettled could be used to report per-message outcomes.
There was a problem hiding this comment.
Fixed. Switched from Promise.all to Promise.allSettled so all requests complete regardless of individual failures. The notification now reports how many succeeded vs failed (e.g. '3 messages resent, 2 failed'), giving the user clear feedback on partial failures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g in resend - Use direct axios.post instead of sendMessage store action to avoid per-message notifications - Switch from Promise.all to Promise.allSettled to handle partial failures gracefully - Report how many messages succeeded vs failed when partial failures occur Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changes
How it works
The resend button re-uses the existing \sendMessage\ store action with the original sender, recipient, content, and SIM from each selected message.