Skip to content

feat: show offline notice in conversation - WPB-25326 - #5473

Open
caldrian wants to merge 15 commits into
feat/pending-message-foundation-WPB-26307from
feat/offline-system-message-WPB-25326
Open

caldrian wants to merge 15 commits into
feat/pending-message-foundation-WPB-26307from
feat/offline-system-message-WPB-25326

Conversation

@caldrian

@caldrian caldrian commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor
TaskWPB-25326 [iOS] Show "No Internet" banner at the bottom of the conversation

Issue

While the device is offline, the open conversation now shows a notice: "You are offline. Your messages will be sent once you are online again." It's behind the developer flag offlinePendingMessages and builds on #5447 (offline pending messages), so it depends on it and will be rebased once that's merged.

Behavior

  • It's shown in any conversation that's open while offline, the same state that shows the NO INTERNET bar. It disappears when the device is back online.
  • If the newest messages are the self user's pending ones, the notice is shown above them. Otherwise it's shown below the newest message. It scrolls with the messages.

Implementation

  • It's only in the UI: an extra cell attached to one message's cells, the same way the date divider is added. Nothing is stored, so unread counts and the conversation list are unaffected and there's nothing to clean up after a restart.
  • ConversationMessageContext has a new offlineNotice value (above or below) that says where the cell goes. ConversationTableViewDataSource computes it from the network state and the pending messages.
  • ConversationContentViewController observes network availability like NetworkStatusViewController.

Testing

Turn on Developer settings → offlinePendingMessages and restart the app.

  1. Open a conversation and go offline. The notice appears below the newest message.
  2. Send a few messages while offline. The notice stays above them, and the first one shows the sender name.
  3. Go online. The notice disappears and the messages are sent.
  4. With the flag off, no notice is shown.

Checklist

  • Title contains a reference JIRA issue number like [WPB-XXX].
  • Description is filled and free of optional paragraphs.
  • Adds/updates automated tests.

UI accessibility checklist

  • The notice is a single accessibility element whose label is the full text.
  • Dynamic Type: the text uses fixed font sizes, matching the existing system message cell. To be checked with design.

caldrian and others added 10 commits October 28, 2026 14:15
With offline pending messages, a text written after an image that is
still waiting for its upload could be sent first once the image is
older than the dependency cap. Also look up the self user's assets
that are uploading or waiting to be sent when resolving dependencies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126T3TR7JLgpH6bwpF2wAWp
An image or file waiting for its upload can't be cancelled, and Delete
is hidden for pending messages. With offline pending messages it can
wait for a long time, so allow deleting it locally while it's still
uploading: the asset message is only sent after the upload, so nobody
else has it. The upload is cancelled first, and the deletion runs on
the sync context so it can't race with the send.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126T3TR7JLgpH6bwpF2wAWp
…fline-pending-assets-WPB-26307

# Conflicts:
#	wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController+MessageDeletion.swift
While offline (behind the offlinePendingMessages flag), the open
conversation shows a notice that messages are sent once online again. It's
placed above the messages the self user sent while offline, or below the
newest message, and disappears when the device is back online.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126T3TR7JLgpH6bwpF2wAWp
@github-actions

Copy link
Copy Markdown
Contributor

Please make sure new localizations are translated once merged to develop.
We need 100% German translations before releasing.

New strings (English):

  • content.system.offline_notice.title = "You are offline."
  • content.system.offline_notice.message = "Your messages will be sent once you are online again."

@caldrian
caldrian marked this pull request as ready for review October 29, 2026 13:09
@caldrian
caldrian requested review from a team, jullianm and samwyndham and removed request for a team October 29, 2026 13:09
@github-actions github-actions Bot added the Messenger Jira component label Oct 29, 2026
@caldrian
caldrian requested review from a team and David-Henner and removed request for a team and samwyndham October 29, 2026 13:09
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit ba43a43.

♻️ This comment has been updated with latest results.

Summary: workflow run #41443125353
Allure report (download zip): html-report-34900-feat_offline-system-message-WPB-25326

@datadog-wireapp

Copy link
Copy Markdown

Tests

⚠️ Warnings

Your PR has warnings. Tests failed — unable to determine if these are from required jobs.

🧪 1 Test failed in 1 job

Test Pull Request Changes | upload-test-results-datadadog — ❄️ 1 known flaky test · 🔄 may pass on retry

View more details · View in GitHub Actions

↳ ❄️ Known flaky: testThatItReturnsLastMessageIfLastMessagesAreTextsAndSentBySelfUser from WireDataModelTests.ZMConversationTests   View in Datadog

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 3b2ed87 | Docs | View more details | Give us feedback!

Copilot AI left a comment

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.

🟡 Changes recommended

The offline notice has unresolved VoiceOver and Dynamic Type gaps, is absent in empty conversations, and causes quadratic pending-message scans during rebuilds.

4 open issues
What changed in this PR

Adds a conversation-level offline notice behind offlinePendingMessages, building on #5447’s pending-message behavior. The PR also extends pending asset deletion and send ordering.

Changes:

  • Observe network availability and position the notice above trailing pending messages or below the newest message.
  • Add localized notice text and tests for notice placement.
  • Allow local deletion of uploading assets and include older pending assets in message dependencies.
File Description
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​ConversationTableViewDataSource.swift Computes notice visibility and placement.
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​ConversationContentViewController+OfflineNotice.swift Observes network state behind the developer flag.
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​ConversationContentViewController+MessageDeletion.swift Generalizes pending deletion to supported message types.
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​ConversationContentViewController.swift Starts and retains network observation.
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​Cells/​ConfigurationMessageCell/​ConversationMessageSectionController.swift Inserts notice cells and preserves sender headers.
wire-ios/​Wire-iOS/​Sources/​UserInterface/​Conversation/​Content/​Cells/​ConfigurationMessageCell/​Components/​ConversationOfflineNoticeCell.swift Defines the notice’s appearance and accessibility label.
wire-ios/​Wire-iOS/​Resources/​Localization/​Base.lproj/​Localizable.strings Adds offline notice text.
wire-ios/​Wire-iOS/​Generated/​Strings+Generated.swift Adds generated localization accessors.
wire-ios/​Wire-iOS Tests/​ConversationMessageCell/​ConversationMessageSectionControllerTests.swift Tests notice placement and cell grouping.
wire-ios-request-strategy/​Sources/​Protocols/​OTREntityTests+Dependency.swift Tests dependencies on older uploading assets.
wire-ios-request-strategy/​Sources/​Helpers/​ZMMessage+Dependency.swift Includes pending assets in send dependencies.
wire-ios-data-model/​Tests/​Model/​Messages/​ZMAssetClientMessageTests.swift Tests pending asset deletion eligibility and behavior.
wire-ios-data-model/​Source/​Model/​Message/​ZMAssetClientMessage+Deletion.swift Adds shared pending-deletion support and asset cancellation.
wire-ios-data-model/​Source/​Model/​Message/​ConversationMessage.swift Uses shared pending-deletion eligibility.

Review effort: Balanced


override init(frame: CGRect) {
super.init(frame: frame)
lineView.isHidden = true
let textColor = SemanticColors.Label.textDefault
let attributedText = NSMutableAttributedString(
string: Strings.title,
attributes: [.font: UIFont.systemFont(ofSize: 14, weight: .semibold), .foregroundColor: textColor]
searchQueries: searchQueries,
previousMessageIsKnock: previousMessage?.isKnock == true
previousMessageIsKnock: previousMessage?.isKnock == true,
offlineNotice: offlineNoticePosition(at: index, messages: messages)
Comment on lines +860 to +862
let pendingMessageCount = messages.prefix { message in
Message.isNormal(message) && message.deliveryState == .pending && message.sender?.isSelfUser == true
}.count

@acv-w acv-w left a comment

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.

Please fix the empty-state notice and accessibility issues before merge.

searchQueries: searchQueries,
previousMessageIsKnock: previousMessage?.isKnock == true
previousMessageIsKnock: previousMessage?.isKnock == true,
offlineNotice: offlineNoticePosition(at: index, messages: messages)

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.

issue (P2): An empty conversation creates no message sections, so it never shows this notice while offline. Please add a UI-only section for the empty state.


override init(frame: CGRect) {
super.init(frame: frame)
lineView.isHidden = true

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.

issue (P2): Please set isAccessibilityElement = true on this view. The base cell hides its text and image from accessibility, so the assigned label is not exposed to VoiceOver.

let textColor = SemanticColors.Label.textDefault
let attributedText = NSMutableAttributedString(
string: Strings.title,
attributes: [.font: UIFont.systemFont(ofSize: 14, weight: .semibold), .foregroundColor: textColor]

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.

issue (P2): These fixed 14-point fonts do not scale with Dynamic Type. Please use scaled fonts and update the text when the content-size category changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Messenger Jira component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants