UI/UX Part 04 — Conversation List / Inbox UX Architecture

Reusable P2P Communication Platform

Status: UI/UX architecture specification
UI Series: Part 04
Desktop UI: Dioxus
Android UI: Kotlin + Jetpack Compose
Core runtime: Rust
Primary purpose: define the complete conversation list / inbox experience across desktop and Android, including unread state, sorting, pinning, archiving, requests, presence, drafts, typing hints, mute state, delivery state previews, multi-device consistency, offline behavior, pagination, accessibility, context actions, and navigation into conversations.


1. Purpose

The conversation list is the primary operational surface of the application.

It must answer, at a glance:

Who contacted me?
What is unread?
What changed recently?
Who is active?
Which messages failed?
Which conversations are pinned?
Which are muted?
Are there message requests?
Is something waiting to send?

The inbox must remain useful when:

offline
partially synced
multi-device
background-updated
search rebuilding
peer unavailable
conversation has draft
conversation has pending transfer

The governing principle is:

The inbox is a projection of durable conversation truth, enriched with bounded ephemeral hints. It is never an independent message database.


2. Architectural Position

Rust Conversation Projection
        │
        ├── unread/read state
        ├── last durable message
        ├── draft summary
        ├── pin/mute/archive state
        ├── peer/group identity
        └── delivery/outbox state
        │
        + Ephemeral overlay
            ├── presence
            ├── typing
            └── call availability
        │
        ▼
ConversationSummary DTO
        │
   ┌────┴─────┐
   │          │
Dioxus     Compose
Desktop    Android

3. Conversation Summary

Recommended shared presentation DTO:

#![allow(unused)]
fn main() {
pub struct ConversationSummary {
    pub id: ConversationId,
    pub kind: ConversationKind,
    pub title: String,
    pub avatar: AvatarRef,
    pub preview: ConversationPreview,
    pub timestamp: Timestamp,
    pub unread_count: u32,
    pub mention_count: u32,
    pub pinned: bool,
    pub muted: bool,
    pub archived: bool,
    pub draft: Option<DraftPreview>,
    pub send_state: ConversationSendState,
    pub presence: Option<PresenceSummary>,
    pub typing: Option<TypingSummary>,
    pub security_badge: Option<SecurityBadge>,
}
}

4. Conversation Kinds

#![allow(unused)]
fn main() {
pub enum ConversationKind {
    Direct,
    Group,
    MessageRequest,
    System,
}
}

Optional future:

Channel
Broadcast
Organization

5. Inbox Sections

Recommended logical sections:

Pinned
Recent
Requests
Archived

Do not create excessive permanent sections.


6. Default Main Inbox

Normal inbox contains:

Pinned
Recent

Requests and archived conversations are separate destinations/filter states.


7. Pinned Conversations

Pinned conversations appear before normal recency-sorted conversations.

Within pinned:

user-defined order
or
recent order

Recommendation:

user-defined order

if drag-reordering is supported.


8. Pin Limit

Keep a reasonable product limit or allow unlimited but virtualized.

Do not make pinning affect durable protocol behavior.

It is user presentation metadata.


9. Recency Ordering

Recent conversations sort by meaningful activity:

latest message
call event
draft change optionally
security event only if conversation-relevant

Do not reorder constantly because presence changes.


10. Presence Must Not Reorder Inbox

Example:

Alice becomes online

should not jump conversation to top.

Presence is an overlay, not durable activity.


11. Typing Must Not Reorder Inbox

Likewise:

Bob is typing…

should not move conversation position.


12. Drafts

A conversation with local draft may show:

Draft: unfinished text

in preview.


13. Draft Priority

Preview priority example:

typing
draft
failed outgoing
last durable message

But avoid unstable flickering.

Recommended display rules are defined explicitly below.


14. Preview State Types

#![allow(unused)]
fn main() {
pub enum ConversationPreview {
    Message(MessagePreview),
    Draft(DraftPreview),
    Typing(TypingPreview),
    Transfer(TransferPreview),
    Call(CallPreview),
    System(SystemPreview),
}
}

15. Stable Preview Priority

Recommended:

1. typing/recording when fresh
2. draft if user has unsent draft
3. failed/pending outgoing state if important
4. latest durable message/call/file event

16. Typing Preview

Example:

Alice is typing…

Group:

Alice and Bob are typing…

If many:

Several people are typing…

17. Typing TTL

UI never owns expiry.

Rust Part 30 projection sends:

typing active
typing inactive

based on TTL.


18. Voice Recording Preview

Optional:

Recording a voice message…

same ephemeral rules.


19. Last Message Preview

Possible:

text excerpt
Photo
Video
Voice message
File
Missed call

Use human-readable semantic labels.


20. Message Preview Privacy

Inbox is inside app, but app lock/security profile may require:

hide message previews

optionally.


21. Unknown Content

If message encrypted but not yet decryptable:

New encrypted message

or generic:

New message

not raw error.


22. Failed Outgoing Preview

Example:

Failed to send

with warning indicator.


23. Queued Outgoing Preview

Example:

Waiting for connection

or small clock icon.


24. Delivery State in Inbox

Do not overcrowd each row.

Possible only for user's latest outgoing message:

Queued
Sent
Delivered
Read
Failed

represented compactly.


25. Unread Count

Derived from durable read cursor.

Never maintained separately by UI.


26. Mention Count

Group conversations may show:

@2

or special badge when unread mentions exist.


27. Unread Badge

Normal:

count

If count large:

99+

or product-defined cap.


28. Muted Unread

Muted conversation still shows unread count.

Badge may be visually quieter.


29. Mark Read

Action advances durable read cursor through current eligible message sequence.


30. Mark Unread

If supported, this is a user reminder state.

Important distinction:

read receipt already sent

cannot be undone remotely.

Local "mark unread" means:

local reminder/unread marker

not reversing read history.


31. Local Unread Marker

Could store:

#![allow(unused)]
fn main() {
pub struct ManualUnreadMarker {
    pub conversation: ConversationId,
    pub from_seq: MessageSequence,
}
}

32. Archive

Archive removes conversation from normal inbox but does not delete it.


33. New Message in Archived Conversation

Policy options:

unarchive automatically
or
remain archived

Recommended default:

unarchive on new incoming message

unless muted/archived intentionally with special setting.


34. Delete Conversation

This must distinguish:

delete local history
leave group
delete for everyone

Do not combine into one ambiguous action.


35. Direct Conversation Delete

Possible semantics:

remove local conversation history

without deleting peer's copy.


36. Group Delete

Usually:

clear local history

separate from:

Leave group

37. Conversation Requests

Unknown peers should not enter normal inbox equivalently to trusted contacts.

Use:

Requests

38. Request Row

Display:

claimed display name
verification/trust state
message preview limited
request timestamp

39. Unknown Sender Safety

Avoid auto-loading:

large attachment
link preview
external media

from unknown sender.


40. Request Actions

Accept
Block
Delete
Report

where supported.


41. Accept Request

May:

create trusted/accepted conversation relationship

but should not imply cryptographic verification.


42. Verification vs Acceptance

Display separately:

Accepted
Verified

They are not identical.


43. Requests Badge

Main Chats destination can show:

3 requests

without mixing requests into normal unread count if product chooses.


44. Search / Filter Inbox

Inbox filter field can search:

conversation title
contact
group

This is lightweight list filtering.

Global message search remains Part 32.


45. Inbox Search Scope

Do not query all message history on every character in sidebar filter.

Filter loaded conversation summaries.


46. Advanced Filters

Potential:

Unread
Groups
Direct
Muted
Archived

Use chips/menu, not permanent clutter.


47. Sort Options

Default:

Pinned + Recent

Optional future:

Unread first
Name

Avoid too many modes.


48. Conversation Row Anatomy

Recommended semantic structure:

Avatar
Title
Timestamp
Preview
Unread/Mention Badge
Mute/Pin/Send Status
Presence optional

49. Avatar

Direct:

contact avatar

Group:

group avatar

Fallback:

initials / generated identicon

50. Avatar Security

Do not imply verified identity through avatar appearance alone.

Use explicit verification marker if needed.


51. Presence Indicator

Small optional dot/status.

Do not show per-device presence.


52. Presence States

Recommended visible distinction:

Online
Away
none

Avoid strong "Offline" dot if status is actually Unknown.


53. Timestamp

Use compact relative formatting:

10:42
Yesterday
Mon
12 Aug

according to recency.

UI formats locally.


54. Timestamp Source

Use latest meaningful durable conversation activity.

Typing/presence should not alter timestamp.


55. Security Badge

Only when meaningful:

identity changed
new unverified device
verification warning

Do not place green lock icon on every row.


56. Draft Badge

Preview prefix:

Draft:

with distinct semantic styling.


57. Failed State

Latest outgoing failure can show:

warning icon
Failed to send

58. Transfer State

If latest event is active file transfer:

Sending file…

but transfer center remains authoritative.


59. Active Call State

Conversation row may show:

Call in progress

if user is currently in call with that conversation.


60. Desktop Dioxus Inbox Layout

Recommended:

+--------------------------------+
| Chats                    New + |
| Search / Filter                 |
+--------------------------------+
| Pinned                          |
|  Alice                    10:42 |
|  Team                     09:10 |
+--------------------------------+
| Recent                          |
|  Bob                      08:31 |
|  School ERP               Mon   |
|  ...                            |
+--------------------------------+

61. Desktop Sidebar Width

Allow resizing.

Minimum prevents title/preview becoming unusable.


62. Desktop Row Density

Potential modes:

Comfortable
Compact

63. Desktop Hover Actions

On hover:

Mute
Pin
More

but actions must remain available through context menu/keyboard.


64. Desktop Context Menu

Open
Open in New Tab
Open in New Window
Pin/Unpin
Mute/Unmute
Mark Read/Unread
Archive
Delete

65. Desktop Keyboard Navigation

Up/Down → move selection
Enter → open
Shift+F10 → context menu
Ctrl/Cmd+N → new conversation

66. Desktop Selection Model

Single active row.

Optional multi-select only if batch conversation operations are valuable.

Not necessary initially.


67. Desktop Active Row

Clearly distinguish:

selected
unread
hovered
focused

without relying solely on color.


68. Desktop Split Pane

Clicking conversation updates main workspace while sidebar remains visible.


69. New Conversation Button

Visible in chats header.

Opens:

contact picker

or command palette flow.


70. Android Compose Inbox Layout

Recommended:

TopAppBar
    Chats
    Search
    New conversation / overflow

Optional filter chips

LazyColumn conversation list

BottomNavigation

71. Android Conversation Row

Use full-width touch row.

Information:

avatar
title
preview
timestamp
unread badge
small state icons

72. Android Swipe Actions

Potential:

swipe right → mark read/unread
swipe left → archive

only if discoverable and reversible.


73. Swipe Accessibility

Every swipe action must also exist in:

long-press action sheet

74. Android Long Press

Opens conversation action sheet:

Pin
Mute
Mark Read
Archive
Delete

75. Android FAB

Optional:

New conversation

A FAB is reasonable if central to product.

Alternative:

top app bar action

76. Recommendation

Use a small compose/new-message FAB on phone if it does not obscure content.

Tablet can use toolbar action.


77. Pull to Refresh

Optional.

If implemented:

request sync

not clear/reload local state.


78. Offline Pull Refresh

May attempt:

local discovery
outbox retry
sync

but retains list.


79. Tablet Inbox

Expanded layout:

NavigationRail
    │
Conversation List
    │
Conversation Detail

80. Tablet Selection

Opening a row updates detail pane without replacing list.


81. Empty Detail Pane

If no conversation selected:

Start a conversation

or welcome placeholder.


82. Foldable

If hinge separates:

list on one region
detail on other

where ergonomic.


83. List Pagination

Conversation lists can be large.

Use cursor/page from Rust projection.


84. Initial Page

Load most recent:

50–100

depending row cost and backend.

Tune by measurement.


85. Load More

At scroll threshold:

request next page

86. Pinned Conversations

Pinned items can be loaded separately to guarantee visibility.


87. Stable Keys

Always:

ConversationId

88. List Update

If message arrives:

update row
move conversation according to durable recency

89. Move Animation

Use subtle position animation.

Respect reduced motion.


90. Avoid Row Teleport Confusion

If many messages arrive:

batch/coalesce list reorders

rather than animate dozens of shifts.


91. Multi-Device Updates

If another device:

reads
archives
pins
mutes

policy determines which settings sync.


92. Read State Sync

Durable read cursor syncs.

Inbox badge updates.


93. Pin Sync

Product decision:

account-wide
or
device-local

Recommendation:

account-wide

for consistency.


94. Mute Sync

Recommendation:

account-wide semantic mute

OS notification-channel details remain device-local.


95. Archive Sync

Recommendation:

account-wide

unless product defines device-local inbox organization.


96. Draft Sync

Can start:

device-local

and evolve to account-wide later.


97. Presence Overlay

Presence is not stored into conversation row persistence.

UI receives updated overlay.


98. Typing Overlay

Typing similarly ephemeral.


99. Row Update Frequency

Presence/typing should only recompose the affected row.

Do not recompute whole list.


100. Compose State Granularity

Prefer stable list data with per-row identifiers.

Avoid giant mutable map causing full list recomposition.


101. Dioxus State Granularity

Same principle.

Use localized signals/subscriptions.


102. Inbox Snapshot

#![allow(unused)]
fn main() {
pub struct InboxSnapshot {
    pub pinned: Vec<ConversationSummary>,
    pub recent: Vec<ConversationSummary>,
    pub request_count: u32,
    pub archived_count: u32,
    pub next_cursor: Option<ConversationCursor>,
}
}

103. Inbox Events

#![allow(unused)]
fn main() {
pub enum InboxEvent {
    ConversationInserted(ConversationSummary),
    ConversationUpdated(ConversationSummary),
    ConversationRemoved(ConversationId),
    ConversationReordered {
        id: ConversationId,
        position: usize,
    },
    RequestCountChanged(u32),
}
}

104. Event Strategy

Can simplify:

row upsert
+
sort key

and let presentation layer maintain ordering.


105. Conversation Sort Key

#![allow(unused)]
fn main() {
pub struct ConversationSortKey {
    pub pinned_rank: Option<u32>,
    pub last_activity: Timestamp,
}
}

106. Optimistic Archive

When user archives:

Rust accepts command
→ row projection changes
→ UI animates out

If command fails:

row returns
show snackbar

107. Undo Archive

Good use of snackbar:

Conversation archived — Undo

108. Optimistic Mute

Can update after Rust confirms quickly.


109. Pin Reordering

Desktop drag.

Android long-press drag if worth complexity.


110. Recommendation for v1

Pinned ordering:

automatic order based on pin timestamp

or stable simple order.

Manual pin reorder can be added later.


111. Message Request Security

Unknown requests must not trigger:

presence sharing
read receipts
typing indicators

until accepted, according to privacy policy.


112. Read Receipt for Request

Do not send read receipt automatically to unknown sender by default.


113. Request Preview Privacy

Could show only:

New message request

until user opens.


114. Spam Flood

If many unknown requests:

collapse into request count

not hundreds of normal rows/notifications.


115. Request Rate Limit

Part 28 handles backend quotas.

UI does not render more than bounded page.


116. Blocked Conversations

Blocked peers should normally disappear from main inbox or appear in:

Blocked

management area.


117. System Conversations

If product has:

security/system notices

consider dedicated system area instead of impersonating a human conversation.


118. Conversation Creation

A new conversation may exist locally before first successful message.


119. Empty New Conversation

If user selects contact and opens composer without sending:

do not necessarily persist inbox row

until:

draft stored
or
first message sent

120. Draft-Only Conversation

If durable drafts enabled:

show row

with:

Draft

121. Conversation Deletion Race

If open conversation deleted on another device:

remove from list
main pane shows no longer available

122. Group Rename

Inbox row title/avatar updates.


123. Contact Rename

Local alias update immediately changes row.


124. Identity Change

Security badge appears.

Do not silently replace verified identity without warning.


125. Outbox Failure

If latest outgoing message permanently failed:

warning icon

and conversation remains visible.


126. Pending Queue

If offline:

small clock

not scary red error.


127. Offline Global State

Could show:

Offline

in chats header as subtle banner/chip.

Do not repeat "offline" on every row.


128. Reconnecting

Likewise global.


129. Row Presence While Offline

Remote presence becomes:

Unknown

after TTL.


130. Inbox Search During Offline

Local filtering works fully.


131. Conversation Open During Offline

Full local history opens.


132. Accessibility — Row Semantics

Screen reader row could announce:

Alice, 3 unread messages, last message "See you tomorrow", 10:42 AM, muted

133. Avoid Duplicate Semantics

Do not expose avatar/title/preview as many noisy separate nodes unless interaction benefits.


134. Android TalkBack

Whole row should be one meaningful clickable target with custom actions where useful.


135. Desktop Screen Reader

Focused row reads:

title
unread
preview
time
important state

136. Focus Preservation

When rows reorder because new message arrives:

keyboard focus stays on same ConversationId

not same numeric row index.


137. Scroll Preservation

If user is far down list and new conversation arrives at top:

do not jump scroll position

138. New Activity Indicator

Optional:

new conversations above

if user scrolled far.


139. Empty Inbox

Display:

No conversations yet

Actions:

Start conversation
Add contact
Scan QR

140. Empty Requests

No message requests

No extra clutter.


141. Empty Archive

No archived conversations

142. Loading

Local-first list should usually appear immediately.

Skeleton only on:

first DB load
core startup
large migration

143. Search Rebuilding

Inbox filtering should still work from conversation projection.

No dependency on Part 32 full-text index.


144. Core Unavailable

Show:

Could not load conversations
Retry

with diagnostics if serious.


145. Storage Full

Inbox remains readable.

Global action banner:

Storage is full. New messages may not be saved.
Manage Storage

146. Security Incident

If account identity/security requires attention:

security banner

above list or global shell.

Do not bury in one conversation if account-wide.


147. Inbox Filters

Recommended initial filters:

All
Unread
Groups

Requests and Archived as separate actions.


148. Filter Persistence

Can persist per device.


149. Desktop Filter Bar

Compact:

search field
filter icon

150. Android Filter Chips

Optional:

All
Unread
Groups

only if frequent use.


151. Conversation Count

Do not show total count unless useful.


152. Unread Count at App Level

Chats tab badge derives from aggregate durable unread state.


153. Requests Count at App Level

Could show separately in Chats badge/menu.


154. Active Call Indicator

If active call belongs to conversation:

persistent call bar

already handles it.

Inbox row can optionally show small call indicator.


155. Transfer Indicator

If active transfer:

small progress glyph

optional.

Avoid showing percentage in every inbox row.


156. Draft Preview Length

Truncate to one line or two lines.


157. Message Preview Length

Same.

Avoid expensive rich text in sidebar row.


158. Preview Sanitization

Do not render:

full Markdown
HTML

in conversation row.

Use plain semantic summary.


159. Emoji

Display naturally.


160. RTL

Inbox layout should adapt:

avatar
text
timestamp
badges

to RTL.


161. Localization

All semantic preview labels:

Photo
Missed call
Draft
Failed to send

localized in UI.

Rust should provide type + safe fields, not English.


162. Relative Time Updates

Do not tick every second.

Update:

minute/hour/day boundaries

as needed.


163. Timer Efficiency

One shared UI timer can refresh visible timestamp labels.

Do not create timer per row.


164. Avatar Loading

Use thumbnail/cache.

Show stable placeholder immediately.


165. Presence Avatar Badge

Ensure accessibility label.


166. Broken Avatar

Fallback without error icon clutter.


167. Conversation List Performance

Target smooth scrolling with:

thousands of conversations

through virtualization/paging.


168. Row Render Cost

Keep low:

one avatar
few text nodes
few icons

169. Avoid Per-Row Heavy Subscriptions

Do not subscribe separately to network/presence actors for every row.

Rust/presentation layer should provide aggregated updates.


170. Visible-Presence Subscription

Part 30 can subscribe presence only for:

visible rows
active conversation

to reduce background work.


171. Visible Row Reporting

UI may report current visible ConversationIds to presentation layer.


172. Privacy

Do not upload inbox ordering/search/filter behavior as telemetry by default.


173. Analytics

If enabled, only aggregate metrics:

inbox load time
row count
filter usage

without names/previews.


174. Screenshot Testing

Capture:

empty
normal
many unread
typing
draft
failed message
request
offline
large font
RTL
dark mode

175. Desktop Interaction Tests

Verify:

arrow navigation
Enter open
context menu
archive undo
pin
search filter

176. Android Interaction Tests

Verify:

tap
long press
swipe
filter
FAB/new message
TalkBack

177. Multi-Device Tests

Scenario:

phone reads
desktop unread clears

Scenario:

desktop archives
phone row disappears

if archive sync enabled.


178. Typing Test

Typing appears, expires automatically, and does not reorder row.


179. Presence Test

Presence changes without reordering.


180. Draft Test

Draft preview supersedes old message preview according to rules.


181. Failed Send Test

Failure indicator survives restart because outbox state is durable.


182. Offline Test

Queued outgoing preview remains.


183. Request Flood Test

Hundreds of unknown requests remain bounded and grouped.


184. Group Rename Test

Row updates without losing selection/focus.


185. Delete Open Conversation Test

Row disappears; detail navigates safely.


186. Pagination Test

Loading older list pages does not duplicate rows.


187. Stable Ordering Test

Presence/typing/read receipt changes do not reorder unless durable activity changed.


188. Rust Presentation API

#![allow(unused)]
fn main() {
pub trait InboxPresentation {
    async fn snapshot(
        &self,
        filter: InboxFilter,
    ) -> Result<InboxSnapshot, UiError>;

    async fn next_page(
        &self,
        cursor: ConversationCursor,
    ) -> Result<ConversationPage, UiError>;

    async fn archive(
        &self,
        conversation: ConversationId,
    ) -> Result<(), UiError>;

    async fn set_muted(
        &self,
        conversation: ConversationId,
        muted: bool,
    ) -> Result<(), UiError>;

    async fn set_pinned(
        &self,
        conversation: ConversationId,
        pinned: bool,
    ) -> Result<(), UiError>;
}
}

189. Inbox Filter

#![allow(unused)]
fn main() {
pub enum InboxFilter {
    All,
    Unread,
    Groups,
    Archived,
    Requests,
}
}

190. Row UI Model

Platform-specific UI models may mirror the shared DTO.

Android:

data class ConversationRowUiModel(...)

Desktop:

#![allow(unused)]
fn main() {
pub struct ConversationRowView(...)
}

191. Mapping Rule

Platform mapper can change:

formatted time
localized labels
icon resource

but not semantic state.


192. Android ViewModel

Owns:

current filter
search text
scroll restoration metadata
UiEffect

Rust owns list truth.


193. Desktop Presenter

Owns:

selected conversation
filter text
sidebar presentation
keyboard focus identity

Rust owns inbox truth.


194. Selection vs Conversation State

Selected row is UI state.

Unread status is domain state.


195. Navigation

Click/tap:

ConversationId
→ typed navigation

Desktop updates detail pane.

Android pushes conversation destination on compact width.


196. Adaptive Navigation

On tablet:

row selection
→ detail pane

without full back-stack replacement.


197. Back from Conversation

Phone:

back → inbox

Desktop:

sidebar always remains

198. Notification Entry

If notification opens conversation:

inbox may update selection

and read state follows actual viewport semantics.


199. Mark Read from Notification

Inbox unread updates from Rust event.


200. Inbox Initialization

Startup:

load local snapshot
render
subscribe
attach network/presence later

201. Network Independence

The list must not wait for:

relay
presence
push
Internet

to render.


202. Background Update

Part 31 may receive messages while no UI exists.

When app opens:

snapshot already reflects them

203. Crash Recovery

Inbox itself requires no special event replay in UI.

Request fresh snapshot.


204. Search Filter after Restart

May restore per-platform preference.


205. Security Boundary

Conversation summary must not expose:

raw encryption keys
transport addresses
device secrets

206. Unknown Peer Metadata

Avoid rich metadata fetch before acceptance.


207. Row Action Authorization

Rust validates:

can delete
can leave
can block

UI only hides/disables obvious unavailable actions.


208. Context Capability DTO

Could include:

#![allow(unused)]
fn main() {
pub struct ConversationCapabilities {
    pub can_edit_title: bool,
    pub can_leave: bool,
    pub can_delete_local: bool,
    pub can_block: bool,
}
}

209. Conversation Row Does Not Need Every Capability

Fetch detailed capabilities when action menu opens if expensive.


210. Undo Architecture

Archive/mute actions can return:

UndoToken

if truly supported.


211. UI/UX Part 04 Definition of Done

Part 04 is complete when:

  • conversation list is a projection of Rust durable state
  • pinned/recent/requests/archived semantics are defined
  • typing/presence enrich rows without reordering them
  • unread counts derive from durable read cursors
  • mark unread is distinguished from remote read receipt reversal
  • draft, failed-send, queued-send, file, call, and normal message previews are defined
  • unknown message requests remain separated from trusted inbox
  • request acceptance does not imply identity verification
  • desktop Dioxus uses a persistent sidebar with keyboard/context-menu actions
  • Android Compose uses a native touch list with long-press/swipe alternatives
  • tablet/foldable list-detail layouts are defined
  • row actions share Rust commands
  • archive/mute/pin state can sync according to explicit policy
  • message list ordering depends on durable activity, not presence/typing
  • pagination/virtualization and stable ConversationId keys are mandatory
  • list remains useful offline
  • background-delivered messages appear from fresh snapshot without UI-specific reconciliation
  • accessibility, RTL, large font, focus preservation, and screen-reader row semantics are defined
  • failure, request flood, deletion, group rename, multi-device, presence, typing, and pagination tests are included

212. Final Cross-Platform Architecture

                 RUST INBOX PROJECTION
                        │
      ┌─────────────────┼─────────────────┐
      │                 │                 │
 Durable State      Ephemeral         Preferences
 messages/read      typing            pin/mute/archive
 outbox/activity    presence
      │                 │                 │
      └─────────────────┼─────────────────┘
                        │
                ConversationSummary
                 ┌──────┴──────┐
                 │             │
              Dioxus        Compose
                 │             │
          Sidebar/List     LazyColumn
                 │             │
                 └──────┬──────┘
                        │
                ConversationId
                        │
                 Typed Navigation

213. Final Principle

The inbox should feel:

immediate
stable
predictable
private
offline-capable

It should not feel like a noisy live dashboard where every typing or presence update reshuffles the UI.

The right model is:

durable conversation order
+
clear unread/read state
+
temporary presence/typing overlays
+
platform-native interactions
+
shared Rust truth

This makes the conversation list a reliable entry point for the entire communication product across both desktop and Android.