* ui: markdown messages show the empty lines that were typed * ui: snapshot references of messages with empty lines * ui: terminal layout spacing by one paragraph unit, names at text size * ui: terminal gap below reactions, same empty lines in both apps * ui: flat layout references with the paragraph unit * core, ui: text with empty lines as paragraphs, ios bubbles fit text * core, ui: move text with empty lines as paragraphs to a follow-up * ui: ios references with bubbles that fit their text * ui: other senders' names in the flat layout in the secondary colour * core, ui: text with empty lines as paragraphs * core, ui: single-star text italic, reports drawn without core prefix
11 KiB
Chat View (Conversation)
Related spec: spec/client/chat-view.md | spec/client/compose.md
Purpose
Full conversation view for displaying and interacting with messages in a direct contact chat, group chat, or note-to-self. Supports text messaging with markdown, media attachments, voice messages, E2E encrypted calls, message reactions, replies, forwarding, and content search/filtering.
Route / Navigation
- Entry point: Tap a chat row in
ChatListView - Presented by:
NavStackCompatdestination fromChatListView, bound tochatModel.chatId - Back navigation: Dismiss sets
chatModel.chatId = nil, returning to chat list - Sub-navigation: Info button navigates to
ChatInfoView(contact) orGroupChatInfoView(group); member avatars navigate toGroupMemberInfoView
Page Sections
Navigation Bar
Custom toolbar overlaying the chat with themed material background:
| Element | Description |
|---|---|
| Back button | Returns to chat list |
| Contact/Group avatar | Small profile image |
| Chat name | Display name; tappable to open info sheet |
| Encryption badge | Shows PQ (post-quantum) or standard E2E status |
| Call buttons | Audio and video call icons (direct chats only) |
| Search button | Toggles in-chat message search |
| Info button | Opens ChatInfoView or GroupChatInfoView |
Message List
Rendered by EndlessScrollView<MergedItem> with lazy loading and pagination:
| Feature | Description |
|---|---|
| Scroll direction | Bottom-to-top (newest messages at bottom) |
| Pagination | Loads more items on scroll to top (loadingTopItems) and bottom (loadingBottomItems) |
| Merged items | Adjacent messages from the same sender are visually merged via MergedItems |
| Floating buttons | Scroll-to-bottom button with unread count; scroll-to-first-unread button |
| Date separators | Sticky date headers between messages from different days |
| Wallpaper | Themed background image with tint and opacity from theme.wallpaper |
| Content filter | Filter messages by type: .images, .files, .links |
Message Types
Each type has a dedicated view in Shared/Views/Chat/ChatItem/:
| Type | View | Description |
|---|---|---|
| Text | MsgContentView |
Rendered with markdown (bold, italic, code, links, mentions) |
| Image | CIImageView |
Thumbnail with tap-to-fullscreen via FullScreenMediaView |
| Video | CIVideoView |
Video thumbnail with play button; inline playback |
| Voice | CIVoiceView / FramedCIVoiceView |
Waveform visualization with playback controls and duration |
| File | CIFileView |
File icon, name, size; download/open actions |
| Link preview | CILinkView |
URL preview card with title, description, image |
| Emoji-only | EmojiItemView |
Large emoji rendering without message bubble |
| Call event | CICallItemView |
Call status (missed, ended, duration) |
| Group event | CIEventView |
Member joined/left, role changes, group updates |
| E2EE info | CIChatFeatureView |
Encryption status and feature change notifications |
| Group invitation | CIGroupInvitationView |
Inline group join invitation card |
| Deleted | DeletedItemView / MarkedDeletedItemView |
Placeholder for deleted messages |
| Decryption error | CIRcvDecryptionError |
Error with ratchet sync suggestion |
| Invalid JSON | CIInvalidJSONView |
Developer fallback for malformed items |
| Integrity error | IntegrityErrorItemView |
Message integrity/gap warnings |
Message Interactions
Long-press context menu on any message:
| Action | Description |
|---|---|
| Reply | Sets compose bar to reply mode with quoted message |
| Forward | Opens forwardedChatItems sheet to pick destination chat |
| Copy | Copies message text to clipboard |
| Edit | Enters edit mode in compose bar (own messages, within edit window) |
| Delete | Delete for self or delete for everyone (with confirmation) |
| React | Opens emoji reaction picker |
| Select multiple | Enters multi-select mode (selectedChatItems) with bulk delete/forward |
| Info | Shows delivery status and timestamps |
Emoji reactions bar displayed below messages with reaction counts.
Compose Bar (ComposeView)
| Element | Description |
|---|---|
| Text input | NativeTextEditor with markdown support and auto-growing height |
| Attachment button | Opens picker for images, videos, files, camera |
| Send button | Sends composed message; changes to voice record button when empty |
| Voice record | Hold-to-record with waveform preview; swipe-to-cancel |
| Reply quote | Shows quoted message above input when replying |
| Edit indicator | Shows "editing" label when editing a previous message |
| Link preview | Auto-generated preview card for detected URLs (ComposeLinkView) |
| Image/Video preview | Thumbnail strip for selected media (ComposeImageView) |
| File preview | File name and size for attached file (ComposeFileView) |
| Voice preview | Waveform of recorded voice message (ComposeVoiceView) |
| Live message | Real-time typing broadcast (optional, with alert on first use) |
| Context actions | ContextContactRequestActionsView for accepting/rejecting contact requests; ContextPendingMemberActionsView for pending group member actions |
| Commands menu | CommandsMenuView for bot/menu commands in chats with menuCommands |
| Group mentions | GroupMentionsView autocomplete popup when typing @ in groups |
| Profile picker | ContextProfilePickerView for choosing incognito/main profile |
Channel Messages
In channel conversations (groupInfo.useRelays == true), received messages (.channelRcv direction) display with:
- The channel icon (
antenna.radiowaves.left.and.right) instead of the standard group icon - The channel name as sender, with "channel" as the role label
- The group profile image as the avatar (tapping opens group info, not member info)
- Consecutive channel messages are grouped without repeating the avatar
- Channel messages cannot be moderated per-member (no member identity)
Member Support Chat (Groups)
For groups with member support enabled:
MemberSupportViewandMemberSupportChatToolbarshown as secondary chat within groupSecondaryChatViewfor scoped group chat views (reports, member support)- User knocking state:
userMemberKnockingTitleBar()shown when user is pending admission
Terminal Layout
A chat with the terminal layout shows messages as full-width rows without bubbles, starting at one edge for sent and received messages, with the sender's name above each group of messages, again after a pause of 7.5 minutes, and the time at the end of the last line. Groups and channels show the sender's avatar beside its name; other chats have no avatar column. The wallpaper is drawn as its colour without the image.
A chat uses the user's choice for that chat, otherwise the layout set by the group owner or by the address the contact came from, otherwise the user's choice for the profile and then the app setting, otherwise classic. The choice for a chat is made in its theme screen; the choices for the app and the profile are made in Settings › Appearance.
Loading / Error States
| State | Behavior |
|---|---|
| Initial load | Messages load from ItemsModel with merged items; allowLoadMoreItems throttles pagination |
| Loading more (top) | loadingTopItems spinner at top of scroll view |
| Loading more (bottom) | loadingBottomItems spinner at bottom |
| Connection in progress | ConnectProgressManager shows connecting text below compose bar |
| Connecting text | "connecting..." label shown below message list when chat not yet ready |
| Send disabled | Compose bar shows disabledText reason when userCantSendReason is set |
| Empty chat | No messages placeholder (implicit -- empty scroll view) |
Related Specs
spec/client/chat-view.md-- Chat view feature specificationspec/client/compose.md-- Compose bar specification- Chat List -- Parent navigation
- Contact Info -- Info sheet for direct chats
- Group Info -- Info sheet for group chats
- Call -- Audio/video calls initiated from toolbar
Source Files
Shared/Views/Chat/ChatView.swift-- Main chat view, message list, navigation, state managementShared/Views/Chat/ChatItemView.swift-- Individual message item rendering dispatcherShared/Views/Chat/ComposeMessage/ComposeView.swift-- Compose bar containerShared/Views/Chat/ComposeMessage/SendMessageView.swift-- Send button and voice recordShared/Views/Chat/ComposeMessage/NativeTextEditor.swift-- Text input with markdownShared/Views/Chat/ComposeMessage/ComposeImageView.swift-- Image attachment previewShared/Views/Chat/ComposeMessage/ComposeFileView.swift-- File attachment previewShared/Views/Chat/ComposeMessage/ComposeVoiceView.swift-- Voice recording previewShared/Views/Chat/ComposeMessage/ComposeLinkView.swift-- Link preview generationShared/Views/Chat/ComposeMessage/ContextItemView.swift-- Reply/edit context displayShared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift-- Contact request accept/rejectShared/Views/Chat/ComposeMessage/ContextPendingMemberActionsView.swift-- Pending member actionsShared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift-- Profile picker for incognitoShared/Views/Chat/ChatItem/FramedItemView.swift-- Framed message bubble renderingShared/Views/Chat/ChatItem/MsgContentView.swift-- Text message content with markdownShared/Views/Chat/ChatItem/TerminalItemView.swift-- Terminal layout rows, sender rows and the time at the row endShared/Views/Chat/ChatItem/CIImageView.swift-- Image message viewShared/Views/Chat/ChatItem/CIVideoView.swift-- Video message viewShared/Views/Chat/ChatItem/CIVoiceView.swift-- Voice message viewShared/Views/Chat/ChatItem/CIFileView.swift-- File message viewShared/Views/Chat/ChatItem/CILinkView.swift-- Link preview viewShared/Views/Chat/ChatItem/EmojiItemView.swift-- Large emoji viewShared/Views/Chat/ChatItem/CICallItemView.swift-- Call event viewShared/Views/Chat/ChatItem/CIEventView.swift-- Group/system event viewShared/Views/Chat/ChatItem/CIChatFeatureView.swift-- Feature change notificationShared/Views/Chat/ChatItem/CIMetaView.swift-- Timestamp and delivery statusShared/Views/Chat/ChatItem/FullScreenMediaView.swift-- Fullscreen image/video viewerShared/Views/Chat/ChatItem/AnimatedImageView.swift-- Animated GIF renderingShared/Views/Chat/Group/GroupMentions.swift-- @mention autocompleteShared/Views/Chat/Group/MemberSupportView.swift-- Member support scoped chatShared/Views/Chat/Group/MemberSupportChatToolbar.swift-- Support chat toolbarShared/Views/Chat/Group/SecondaryChatView.swift-- Secondary scoped chat view