Skip to content

feat(web): add bulk resend button and mobile UI improvements to search messages#901

Merged
AchoArnold merged 4 commits into
mainfrom
feat/search-messages-resend-button
May 22, 2026
Merged

feat(web): add bulk resend button and mobile UI improvements to search messages#901
AchoArnold merged 4 commits into
mainfrom
feat/search-messages-resend-button

Conversation

@AchoArnold
Copy link
Copy Markdown
Member

Changes

  • RESEND button: Added bulk resend with confirmation dialog. Only enabled when all selected messages are outbound (MT) with \�xpired\ or \ ailed\ status. Hidden on mobile.
  • Mobile UI: Remove icons from DELETE, EXPORT, and SEARCH buttons on mobile — show uppercase text only.
  • Desktop UI: Keep original icons and text for all buttons.

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.

…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>
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 22, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

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-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR adds a bulk RESEND button to the search-messages page for outbound messages with expired or failed status, alongside mobile UI improvements that strip icons from DELETE, EXPORT, and SEARCH buttons on small screens in favour of uppercase text labels.

  • Bulk resend: A new canResendSelected computed property gates the button; clicking it opens a confirmation dialog that calls resendMessages, which fans out parallel sendMessage store dispatches via Promise.all and refreshes the list on completion.
  • Mobile UI: All four action buttons now conditionally render their icon only on mdAndUp viewports and switch between uppercase short labels (SEARCH, DELETE, EXPORT, RESEND) and full-text labels depending on breakpoint.
  • One logic issue: The reused sendMessage store action dispatches its own success notification per message, causing N+1 toasts when N messages are resent — the per-action notifications stack on top of the single bulk confirmation shown by resendMessages.then().

Confidence Score: 3/5

The 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

Filename Overview
web/pages/search-messages/index.vue Adds bulk RESEND button with confirmation dialog and mobile UI improvements; contains a notification duplication bug where each reused sendMessage action fires its own toast on top of the bulk success notification

Sequence Diagram

sequenceDiagram
    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
Loading

Reviews (1): Last reviewed commit: "feat(web): include request_id when resen..." | Re-trigger Greptile

Comment on lines +560 to +590
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()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread web/pages/search-messages/index.vue Outdated
Comment on lines +572 to +583
)
.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',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

AchoArnold and others added 2 commits May 22, 2026 09:43
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>
@AchoArnold AchoArnold merged commit 2e483d8 into main May 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant