Files
simplex-chat/apps/ios/spec/client/chat-list.md
2026-02-19 10:58:16 +00:00

13 KiB

SimpleX Chat iOS -- Chat List Module

Technical specification for the conversation list, filtering, search, swipe actions, and user picker.

Related specs: Chat View | Navigation | State Management | README Related product: Chat List View

Source: ChatListView.swift


Table of Contents

  1. Overview
  2. ChatListView
  3. ChatPreviewView
  4. ChatListNavLink
  5. Filtering & Tags
  6. Search
  7. Swipe Actions
  8. UserPicker
  9. Floating Action Button

1. Overview

The chat list is the main screen of the app, displaying all conversations for the current user. It provides:

  • Conversation previews with unread badges
  • Filter tabs (All, Unread, Favorites, Groups, Contacts, Business, user-defined tags)
  • Search across chat names and message content
  • Swipe actions for quick operations
  • User profile switcher
  • Floating action button for new conversations
ChatListView
├── Navigation Bar
│   ├── User avatar (tap → UserPicker)
│   └── Filter tabs (TagListView)
├── Search bar (on pull-down or tap)
├── Chat List (List/LazyVStack)
│   └── ChatListNavLink (per conversation)
│       └── ChatPreviewView
│           ├── Avatar
│           ├── Chat name + last message preview
│           ├── Timestamp
│           └── Unread badge
├── FAB (New Chat button)
└── Pending connection cards

2. ChatListView

File: Shared/Views/ChatList/ChatListView.swift

The root list view. Key responsibilities:

Data Source

  • Reads ChatModel.shared.chats (all conversations)
  • Applies active filter from ChatTagsModel.shared.activeFilter
  • Applies search query filtering via filteredChats()
  • Sorts by last activity (most recent first), with pinned chats at top

Layout

  • Uses SwiftUI List with ForEach over filtered chats
  • Each row is a ChatListNavLink wrapping a ChatPreviewView
  • Pull-to-refresh triggers updateChats() API call
  • Empty state: ChatHelp view with getting-started guidance

Connection Cards

  • Pending contact connections (ChatInfo.contactConnection) shown as cards
  • Contact requests (ChatInfo.contactRequest) shown with accept/reject UI via ContactRequestView

Key Functions

Function Line Description
body 163 Main view body
filteredChats() 472 Applies active filter and search to chat list
searchString() 514 Normalizes search text for comparison
unreadBadge() 448 Renders unread count circle badge
stopAudioPlayer() 467 Stops any playing voice message

3. ChatPreviewView

File: Shared/Views/ChatList/ChatPreviewView.swift

Renders a single row in the chat list. Shows:

Element Source Description
Avatar chatInfo.image Profile image or default icon
Chat name chatInfo.displayName Contact name, group name, or connection label
Last message chat.chatItems.last Preview text of most recent message
Timestamp chat.chatItems.last?.timestampText Relative time of last message
Unread badge chat.chatStats.unreadCount Circular badge with unread count
Mute icon chatInfo.chatSettings?.enableNtfs Bell-slash icon if notifications muted
Pin icon -- Pin indicator for pinned chats
Incognito icon Contact.contactConnIncognito Incognito mode indicator
Delivery status Last sent item's meta.itemStatus Check marks for delivery confirmation

Preview Text Rendering

  • Text messages: first line of message content
  • Images: camera icon + caption (if any)
  • Files: paperclip icon + filename
  • Voice: microphone icon + duration
  • Calls: phone icon + call status
  • Group events: system event description
  • Encrypted/deleted: placeholder text

File: Shared/Views/ChatList/ChatListNavLink.swift

Wraps ChatPreviewView in a navigation link with tap and swipe behavior:

Tap Behavior

Navigation

  • Uses NavigationLink (iOS 15) or programmatic navigation (iOS 16+)
  • Sets ChatModel.chatId to trigger navigation
  • ItemsModel.loadOpenChat() loads messages with a 250ms navigation delay for smooth animation

5. Filtering & Tags

Filter Tabs (TagListView)

File: Shared/Views/ChatList/TagListView.swift

Horizontal scrolling tab bar below the navigation bar. Tabs:

Tab Filter Shows
All nil All conversations
Unread .unread Conversations with unread messages
Favorites .presetTag(.favorites) Favorited conversations
Groups .presetTag(.groups) Group conversations
Contacts .presetTag(.contacts) Direct conversations
Business .presetTag(.business) Business conversations
Group Reports .presetTag(.groupReports) Groups with pending reports
User tags .userTag(ChatTag) User-defined custom tags

Filter matching is handled by presetTagMatchesChat() (L910) and the in-view TagsView struct (L705).

ChatTagsModel State

Filtering state is managed by ChatTagsModel (ChatModel.swift L183):

class ChatTagsModel: ObservableObject {
    @Published var userTags: [ChatTag] = []
    @Published var activeFilter: ActiveFilter? = nil
    @Published var presetTags: [PresetTag: Int] = [:]   // count per preset tag
    @Published var unreadTags: [Int64: Int] = [:]        // unread count per user tag
}
  • presetTags counts are updated whenever chats changes via updateChatTags() (L197)
  • Tags with zero matching chats are auto-hidden
  • Active filter is auto-cleared when its tag has no matching chats

Supporting Types

Type File Line Description
PresetTag ChatListView.swift 34 Enum of built-in filter categories
ActiveFilter ChatListView.swift 49 Enum wrapping preset, user-tag, or unread filter
setActiveFilter() ChatListView.swift 878 Applies a filter and persists selection

Tag Management Commands

  • apiCreateChatTag(tag: ChatTagData) -- create tag
  • apiSetChatTags(type:, id:, tagIds:) -- assign tags to a chat
  • apiDeleteChatTag(tagId:) -- delete tag
  • apiUpdateChatTag(tagId:, tagData:) -- rename tag
  • apiReorderChatTags(tagIds:) -- reorder tags

Search is available via pull-down gesture or search button in the navigation bar.

Search bar UI: ChatListSearchBar (ChatListView.swift L578)

Filtering Logic

  • Filters ChatModel.chats by matching search text against:
    • chatInfo.displayName (contact/group name)
    • chatInfo.localAlias (local alias)
    • chatInfo.fullName (full name)
  • For deeper message content search, uses apiGetChat(chatId:, search:) parameter
  • Core logic in filteredChats() (L480) and searchString() (L523)

Search Results

  • Matching chats are displayed in the same list format
  • Results update as the user types (debounced)
  • Clearing search restores the full filtered list

7. Swipe Actions

ChatListNavLink provides swipe actions on each row:

Leading Swipe (left-to-right)

Action Icon Handler Line API Condition
Pin / Unpin pin toggleFavoriteButton() 347 apiSetChatSettings (favorite) Always
Read / Unread envelope markReadButton() 328 apiChatRead / apiChatUnread Always

Trailing Swipe (right-to-left)

Action Icon Handler Line API Condition
Mute / Unmute bell.slash toggleNtfsButton() 365 apiSetChatSettings (enableNtfs) Always
Clear trash clearChatButton() 385 apiClearChat Has messages
Delete trash.fill -- -- apiDeleteChat Not active chat
Tag tag -- -- apiSetChatTags Always

8. UserPicker

File: Shared/Views/ChatList/UserPicker.swift

Triggered by tapping the user avatar in the navigation bar. Presented as a sheet with:

Section Contents
User list All non-hidden users with unread counts
Active user Highlighted with checkmark
Actions Settings, Your SimpleX address, User profiles

User Switching

  • Tapping a different user calls apiSetActiveUser(userId:)
  • Triggers apiGetChats for the new user
  • ChatModel.currentUser updates, causing full UI refresh
  • Hidden users are not shown (require password entry via settings)

9. Floating Action Button

The FAB (floating action button) in the bottom-right corner opens the new chat flow:

  • Tap: opens NewChatView sheet for creating a new contact connection or group
  • Shows options: Create link, Scan QR code, Paste link, Create group

Source Files

File Path Key struct Line
Chat list view ChatListView.swift ChatListView 138
Chat preview row ChatPreviewView.swift ChatPreviewView 12
Navigation link wrapper ChatListNavLink.swift ChatListNavLink 43
Tag filter tabs TagListView.swift TagListView 19
User picker sheet UserPicker.swift UserPicker 9
Getting started help ChatHelp.swift
Contact request view ContactRequestView.swift
Contact connection info ContactConnectionInfo.swift
Contact connection view ContactConnectionView.swift
Server summary ServersSummaryView.swift
One-hand UI card OneHandUICard.swift