UI/UX Part 05 — Conversation / Message Timeline UX Architecture
Reusable P2P Communication Platform
Status: UI/UX architecture specification
UI Series: Part 05
Desktop UI: Dioxus
Android UI: Kotlin + Jetpack Compose
Core runtime: Rust
Primary purpose: define the complete conversation screen and message timeline experience across desktop and Android, including paging, scroll anchoring, unread boundaries, message grouping, replies, edits, deletes, reactions, attachments, voice notes, delivery/read state, failed/pending messages, typing overlays, search navigation, accessibility, and large-history performance.
1. Purpose
The conversation timeline is the heart of the communication product.
It must remain understandable and stable while handling:
live incoming messages
offline queues
edited messages
deleted messages
replies
attachments
voice notes
read receipts
typing
message search
history pagination
multi-device sync
reconnects
The governing principle is:
The timeline is a stable chronological projection of durable message truth, with ephemeral overlays layered on top without destabilizing reading position.
2. Architectural Position
Rust Conversation Projection
│
├── paged messages
├── edits/deletes
├── reactions
├── delivery/read state
├── attachment metadata
├── reply references
└── outbox state
│
+ Ephemeral overlay
├── typing
├── recording
└── presence
│
▼
ConversationScreenSnapshot
│
┌────┴─────┐
│ │
Dioxus Compose
Desktop Android
3. Conversation Screen Regions
Shared semantic regions:
Header
Message Timeline
Unread / Date Separators
Typing / Recording Indicator
Reply/Edit Context
Composer
Attachment / Transfer Context
Desktop may add a persistent inspector.
Android uses a focused single-screen composition.
4. Conversation Header
Shows:
title
avatar
presence/availability
verification/security state if relevant
audio call action
video call action
search
details
Do not overload with backend diagnostics.
5. Header Presence
Examples:
Online
Away
Last seen recently
according to privacy settings.
If unknown:
show nothing
rather than false offline precision.
6. Header Security State
Show only meaningful actionable state:
Verified
Identity changed
Unverified device warning
No decorative lock clutter.
7. Timeline Data Model
#![allow(unused)] fn main() { pub struct ConversationScreenSnapshot { pub conversation: ConversationHeaderView, pub messages: Vec<MessageView>, pub oldest_cursor: Option<MessageCursor>, pub newest_cursor: Option<MessageCursor>, pub unread_boundary: Option<MessageSequence>, pub typing: TypingSummary, pub recording: RecordingSummary, } }
8. Message Identity
Every row keyed by:
MessageId
Never list index.
9. Message Sequence
Conversation ordering uses stable logical sequence:
#![allow(unused)] fn main() { pub struct MessageSequence(pub u64); }
Do not use wall-clock timestamps alone for ordering.
10. Message View
#![allow(unused)] fn main() { pub struct MessageView { pub id: MessageId, pub sequence: MessageSequence, pub sender: SenderView, pub direction: MessageDirection, pub content: MessageContentView, pub sent_at: Timestamp, pub edited: bool, pub deleted: bool, pub delivery: MessageDeliveryView, pub reply_to: Option<MessageReferenceView>, pub reactions: Vec<ReactionSummary>, } }
11. Message Direction
#![allow(unused)] fn main() { pub enum MessageDirection { Incoming, Outgoing, System, } }
12. Initial Load
Open conversation:
load recent window around newest message
Example:
50–100 messages
Tune by device/performance.
13. Local-First Open
Existing conversation should open from local state immediately.
Do not wait for:
presence
relay
sync
Internet
14. History Pagination
Scrolling upward:
request older page
prepend
preserve anchor
15. Scroll Anchor
Use:
MessageId + visual offset
not numeric index.
16. Anchor Preservation
When older rows prepend:
same visible message stays at same screen location
17. New Messages While at Bottom
If user is at bottom:
append
auto-follow
18. New Messages While Reading History
If user is not near bottom:
append invisibly
show "N new messages" chip
Do not force scroll.
19. New Message Chip
Action:
jump to newest
May display:
3 new messages
20. Unread Boundary
When opening unread conversation:
show separator
"New messages"
before first unread message.
21. Unread Boundary Persistence
Boundary derives from durable read cursor.
UI does not invent it.
22. Marking Read
Part 30 semantics.
UI reports highest truly read sequence based on:
visibility
focus/lifecycle
23. Desktop Read Criteria
Recommended:
conversation active
window focused
message sufficiently visible
24. Android Read Criteria
Recommended:
Activity RESUMED
conversation destination visible
message sufficiently visible
device not obscuring app
25. Do Not Mark Entire Conversation Read on Open
If user opens and immediately leaves:
only truly viewed range
should advance read cursor.
26. Date Separators
Show when calendar grouping changes:
Today
Yesterday
Monday
12 Aug 2026
localized.
27. Date Separator Computation
Can be presentation-layer derived from timestamps.
28. Message Grouping
Consecutive messages from same sender within a short interval can visually group.
29. Grouping Criteria
Example:
same sender
same message type class
time gap < 5 minutes
no major system boundary
30. Grouped Rendering
First message may show:
avatar
sender name
timestamp
following messages reduce repeated chrome.
31. Do Not Group Across
date separator
unread boundary
system event
security event
large time gap
32. Group Message Sender Name
Direct conversation:
usually unnecessary
Group conversation:
show sender name
33. Message Bubble vs Flat Layout
Android may use bubble-oriented layout.
Desktop can use:
bubbles
or
clean aligned timeline
but semantics remain identical.
34. Outgoing Alignment
Typically:
right
in LTR.
RTL should adapt appropriately without confusing sender identity.
35. RTL
Use layout direction correctly.
Do not hardcode outgoing=invariant physical right if product decides semantic alignment differently in RTL.
36. Message Width
Limit maximum readable width.
Do not stretch one-line message across ultrawide desktop.
37. Text Rendering
Support:
Unicode
emoji
RTL
mixed scripts
links
basic formatting
38. Rich Text
If Markdown-like formatting supported:
bold
italic
code
quote
links
must be sanitized.
39. No Arbitrary HTML
Do not render untrusted HTML from message content.
40. Links
Detect and render clickable links.
41. External Link Safety
Open via platform browser after scheme validation.
Warn on suspicious/unsupported schemes.
42. Link Preview
Optional enriched message content.
Preview metadata is derived.
If unavailable:
message remains valid
43. Link Preview Privacy
Avoid fetching preview server-side in a way that leaks user IP/contact graph unintentionally.
Use privacy-aware backend policy.
44. Replies
Reply creates reference:
MessageId
not copied-only text.
45. Reply Preview
Message bubble shows:
sender
small quoted excerpt/type
46. Reply Navigation
Tap/click quoted reply:
jump to referenced MessageId
47. Missing Reply Target
If original unavailable/deleted:
Original message unavailable
not broken layout.
48. Reply Highlight
After jump:
brief highlight
respect reduced motion.
49. Editing
Editable only if Rust capability says allowed.
50. Edit UI
Entering edit mode:
composer populated
edit context banner
51. Save Edit
Rust performs authoritative validation.
52. Edited Label
Show compact:
edited
53. Edit History
Optional future:
view edit history
not required initially.
54. Delete
Possible states:
Delete for me
Delete for everyone
depending protocol/capability.
55. Deleted Message Placeholder
If tombstone retained:
Message deleted
56. Local-Only Delete
May remove row entirely or show local tombstone according to history model.
57. Reactions
Reaction bar below/near message.
Examples:
👍 3
❤️ 2
58. Reaction Interaction
Desktop:
hover action
context menu
click existing reaction
Android:
long press
reaction picker
tap existing reaction
59. Reaction Summary
Rust provides aggregated:
#![allow(unused)] fn main() { pub struct ReactionSummary { pub emoji: String, pub count: u32, pub reacted_by_me: bool, } }
60. Reaction Details
Optional:
who reacted
sheet/dialog.
61. Reaction Accessibility
Screen reader:
3 thumbs up reactions
62. Attachments
Message content can include:
image
video
document
audio
generic file
63. Attachment State
#![allow(unused)] fn main() { pub enum AttachmentState { RemoteAvailable, Queued, Downloading, Verifying, Ready, Failed, Removed, } }
64. Attachment Card
Shows:
name/type
size
thumbnail if available
transfer state
download/open action
65. Image Message
Use:
thumbnail
aspect-ratio-preserving container
66. Progressive Image
placeholder
thumbnail
full image on open
67. Video Message
Show:
thumbnail
duration
play
download state
68. Document Message
Show:
filename
type
size
download/open
69. File Transfer Progress
Do not update message row at packet frequency.
Throttle/coalesce.
70. Attachment Failure
Show:
Retry
if recoverable.
71. Attachment Missing
Example:
File no longer available
72. Voice Notes
Message card shows:
play/pause
progress
duration
speed
waveform optional
download state
73. Voice Note Waveform
Derived visualization.
Do not make waveform a requirement if unavailable.
74. Playback Speed
Examples:
1×
1.5×
2×
75. Voice Note Scrubbing
Support seek if media format/index allows.
76. Voice Note Read State
Playing audio is separate from message read state.
77. Media Viewer
Tap image/video/file:
open dedicated viewer
Desktop may use detached window.
Android may use full-screen destination.
78. Media Viewer Navigation
Allow:
previous/next media in conversation
using logical media query.
79. Pending Outgoing Message
After send command:
Rust creates durable local pending message
Timeline renders immediately.
80. Pending Visual
Small:
clock
Queued
Sending
depending state.
81. Offline Pending
User sees message in place.
No modal network error.
82. Sent
Local outbox has completed sender semantics.
83. Delivered
Recipient application durably accepted.
84. Read
Recipient read cursor advanced through message.
85. Failure
Permanent/retry-needed failure:
warning icon
Tap/click to retry
86. Retry
Uses same logical MessageId/idempotency where backend requires.
Do not duplicate visible message.
87. Retry UX
Action:
Retry
Optional:
Delete
Details
88. Message Info
Shows:
sent time
delivered time
read time
delivery devices only if product exposes
Privacy rules apply.
89. Group Delivery Info
Potential:
Delivered to 5
Read by 3
90. Group Detail View
Optional per-member receipt list for small groups.
91. Typing Indicator
Appears:
above composer
or
bottom of timeline
92. Recommendation
Place typing indicator just above composer / at bottom of timeline so it feels associated with incoming message flow.
93. Typing Should Not Occupy Message Row Identity
It is not a durable message.
94. Recording Indicator
Similar:
Alice is recording audio…
95. Typing While Scrolled Up
Do not force scroll.
Indicator can stay near composer.
96. Presence
Header-level status is usually sufficient.
Avoid presence labels on every message.
97. System Messages
Examples:
Alice joined
Device verified
Group renamed
Call ended
Use centered/system style.
98. Security Events in Timeline
Only if conversation-relevant.
High-risk event may include:
Identity changed
with action:
Review
99. Do Not Fake Security Events
Security events originate from Rust/security subsystem.
100. Calls in Timeline
Call history event:
Audio call — 12 min
Missed call
Video call
101. Call Event Actions
Call back
102. File Events
Sending file is usually represented as message attachment, not separate duplicate system event.
103. Selection Mode
Desktop:
mouse/keyboard multi-select
Android:
long press → contextual selection mode
104. Multi-Select Actions
Potential:
Copy
Forward
Delete
Save attachments
depending capabilities.
105. Selection Stability
Key by MessageId.
Reordering/edit must not lose selection accidentally.
106. Desktop Selection
Shift-click range.
Ctrl/Cmd-click toggle.
107. Android Selection
Long-press starts selection.
Tap toggles.
Back exits selection before leaving conversation.
108. Context Actions
Message actions must be capability-driven.
109. Capability Model
#![allow(unused)] fn main() { pub struct MessageCapabilities { pub can_reply: bool, pub can_edit: bool, pub can_delete_local: bool, pub can_delete_everywhere: bool, pub can_forward: bool, pub can_react: bool, pub can_copy: bool, } }
110. Context Menu / Bottom Sheet
Desktop:
right click
hover menu
keyboard
Android:
long press
bottom sheet/contextual toolbar
111. Copy
Copies message text only if content type allows.
112. Forward
Select destination conversation(s).
Rust creates proper forwarded message representation.
113. Forward Privacy
Do not automatically expose original sender identity if product semantics do not require it.
114. Search Within Conversation
Desktop:
Ctrl/Cmd+F
Android:
search action in app bar
115. Search Results
Part 32 returns MessageIds.
Timeline supports:
jump to result
highlight
next/previous result
116. Search Navigation State
Do not duplicate search engine.
UI maintains current result index only.
117. Jump Loading
If MessageId is far in history:
request page centered around target
118. Centered Page Request
#![allow(unused)] fn main() { pub struct MessageAroundRequest { pub conversation: ConversationId, pub anchor: MessageId, pub before: usize, pub after: usize, } }
119. Jump Highlight
Temporary visual emphasis.
120. Return to Latest
After search/jump:
button to return to newest
if far from bottom.
121. Scroll-To-Bottom Button
Appears when:
user far from newest
May include unread/new count.
122. Desktop Timeline Layout
Recommended:
Header
────────────────────────
Virtualized Message List
────────────────────────
Typing / Reply Context
Composer
Optional right inspector.
123. Android Timeline Layout
Recommended:
TopAppBar
LazyColumn timeline
Typing indicator
Reply/Edit context
Composer with IME insets
124. Reverse List vs Normal List
Compose implementations often use reverse layout.
Architecture should choose based on:
paging
anchor stability
accessibility
not habit.
125. Recommendation
Use a model that keeps chronological semantics clear and supports robust prepend paging.
Implementation can use reverse layout internally if thoroughly tested.
126. Desktop Virtualization
Dioxus timeline must virtualize long histories.
127. Variable Height Rows
Messages have variable height.
Virtualizer must handle:
text
images
replies
files
reactions
128. Estimated Heights
Can use estimated heights but must correct without large jumps.
129. Image Dimension Metadata
Know image aspect ratio before full image decode if possible to reserve layout space.
130. Avoid Layout Shift
Reserve attachment bounds.
131. Android LazyColumn
Use:
key = MessageId
contentType = message type
where helpful.
132. Compose Recomposition Granularity
Delivery tick/reaction change should recompose affected row, not whole conversation.
133. Desktop Signal Granularity
Same.
134. Message Row Presentation Model
Can be specialized:
#![allow(unused)] fn main() { pub enum MessageContentView { Text(TextMessageView), Image(ImageMessageView), Video(VideoMessageView), File(FileMessageView), Voice(VoiceMessageView), System(SystemMessageView), } }
135. Platform Mapping
Android maps to sealed UI model.
Desktop uses Rust enum directly/presenter mapping.
136. Time Display
Do not show timestamp on every grouped message if visually noisy.
Possible:
show on group end
show on hover desktop
show small always mobile
137. Accessibility Timestamp
Even if hidden visually, timestamp should remain available to screen reader/details.
138. Desktop Hover Details
Hover can reveal:
timestamp
actions
but nothing essential should exist only on hover.
139. Android Touch Interaction
Tap on message should not always open action sheet.
Recommended:
tap content-specific
long press actions
140. Double Tap
Optional quick reaction.
Do not make critical action rely on it.
141. Message Spacing
Use smaller spacing inside grouped runs.
Larger spacing between sender/time groups.
142. System Event Spacing
Distinct but not oversized.
143. Conversation Background
Neutral.
Do not reduce text contrast with decorative wallpapers by default.
144. Custom Wallpaper
Optional later.
Must preserve readability/accessibility.
145. Theme Contrast
Incoming/outgoing bubbles need sufficient contrast in light/dark modes.
146. Color Is Not Sole Sender Indicator
Use alignment/shape/name as well.
147. Large Font
Timeline remains usable at large font scale.
Message action buttons must not overlap.
148. Android IME Insets
Composer stays above keyboard.
Timeline adjusts without losing anchor.
149. Keyboard Open/Close
Do not unexpectedly jump to bottom if user was reading history.
150. Composer Expansion
Multiline text expands up to limit.
Then scroll inside composer.
151. Reply Context
Above composer:
Replying to Alice
excerpt
X cancel
152. Edit Context
Editing message
distinct from reply.
153. Attachment Draft Strip
Selected attachments preview above composer.
Detailed composer architecture belongs Part 06.
154. Timeline During Attachment Upload
Pending attachment message remains in timeline.
155. Message Send Ordering
If user sends rapidly:
local sequence/order is stable
even if network completions differ.
156. Server/Peer Reordering
Rust resolves canonical logical order.
UI should not sort by arrival time.
157. Timestamp Corrections
If message timestamp adjusted after sync:
do not cause arbitrary reorder if logical sequence stable
158. Duplicate Delivery
Same MessageId arriving via direct/relay/DTN:
one row
159. Edit Race
If user opens action menu while message edited remotely:
capability/state refresh
before commit.
160. Delete Race
If message disappears while selected:
selection removes safely
161. Reaction Race
Use optimistic display only if Rust returns accepted pending state.
162. Multi-Device Sent Message
Message sent from user's other device should appear as outgoing.
163. Sender Device Detail
Do not clutter timeline with device identity.
Message info may show:
Sent from Desktop
only for own-device diagnostics if useful.
164. Read Cursor Sync
Other device reading conversation updates local unread boundary/badge.
Do not forcibly move scroll.
165. Message Expiration
If disappearing-message feature later exists:
expired row removed/tombstoned
without destabilizing scroll excessively.
166. Expiry Countdown
Avoid second-by-second countdown on every message.
Show coarse info/details.
167. Pinned Messages
Future conversation feature.
Could show:
Pinned message banner
in header.
Not required for basic v1.
168. Bookmarks / Saved Messages
Could be local metadata.
Not necessary in first timeline version.
169. Threads
If future threads:
reply count
open thread
Need separate UI architecture later.
Do not overbuild now.
170. Message Translation
Future optional feature.
Do not couple base row to cloud translation.
171. AI Actions
Future optional:
summarize
rewrite
must respect privacy and explicit user action.
Not part of core timeline.
172. Unknown Message Type
Forward compatibility.
Render:
Unsupported message type
Update app to view
173. Unknown Optional Fields
Ignore safely.
174. Unsupported Attachment
Show metadata and:
Open externally
if safe.
175. Corrupt Message
Do not crash.
Show:
Could not display this message
with optional diagnostics.
176. Decryption Failure
Security-sensitive.
Show generic failure and route to diagnostics/security if needed.
Do not expose raw crypto details.
177. Blocked Sender Historical Messages
Existing history may remain visible.
Future incoming content blocked.
178. Request Conversation Timeline
Before acceptance:
restricted actions
no read receipt
no typing share
no auto-download
according to Part 28/30.
179. Message Request Banner
Show:
This person is not in your contacts
Accept
Block
Delete
above timeline.
180. Group Timeline
Show sender names/avatars more clearly.
181. Group Membership Events
Centered system rows:
Alice joined
Bob left
Carol changed group name
182. Group Security Change
If meaningful:
Security settings changed
with details action.
183. Calls in Group Timeline
Group call started/ended event if product wants durable history.
184. Delivery State in Group
Do not show dozens of receipt icons inline.
Use compact aggregate.
185. Message Details Group
Dedicated sheet/dialog for:
delivered/read participants
186. Accessibility — Message Row
Screen reader should expose coherent information:
Alice, 10:42 AM, "See you tomorrow", 2 reactions
187. Incoming/Outgoing Accessibility
Include:
You:
for outgoing if needed.
188. Delivery Accessibility
Announce:
Delivered
Read
Failed
for outgoing.
189. Attachment Accessibility
Example:
PDF file, timetable.pdf, 2.4 megabytes, downloaded
190. Voice Note Accessibility
Expose:
Voice message, 34 seconds, play
191. Reply Accessibility
Announce:
Replying to Alice: ...
192. Reaction Accessibility
Expose summarized counts and action.
193. Focus After Sending
Composer stays focused by default on desktop.
Android may keep keyboard open if user continues conversation.
194. Focus After Reply Navigation
Do not unexpectedly focus composer.
195. Focus After Search Jump
Focus timeline/result target appropriately.
196. New Message Accessibility
Use a controlled live region.
Do not read every rapidly arriving message automatically in large group unless user preference allows.
197. Selection Accessibility
Announce:
1 message selected
2 messages selected
198. Performance — Large History
Test:
10k
100k
1M
message histories where architecture scale requires.
Only visible window rendered.
199. Memory
Memory use should not grow linearly with total history.
200. Paging Cache
Maintain bounded nearby pages.
201. Page Eviction
Far-old loaded pages can be released while preserving anchor metadata.
202. Search Jump Cache
Keep temporary around-target window.
203. Attachment Thumbnail Cache
Bound separately.
204. Message Row Cache
Avoid caching giant rich render trees indefinitely.
205. High-Frequency Updates
Typing:
few Hz maximum
Transfer:
5–10 Hz
Audio level not part of normal message row.
206. Timestamp Refresh
Shared timer.
No per-message timer.
207. Scroll Performance
Heavy operations must not run on UI thread:
decryption
file hashing
thumbnail generation
search
208. Dioxus Desktop Interaction
Mouse:
hover actions
right click
select text
drag attachment
Keyboard:
Up/Down history navigation only when appropriate
Ctrl/Cmd+F search
Esc cancel reply/edit/selection
209. Android Interaction
Touch:
tap
long press
swipe where appropriate
Use native haptics sparingly.
210. Desktop Text Selection
Users should be able to select/copy message text naturally.
211. Android Text Selection
Long-press text can support copy if not conflicting with message action gesture.
May expose explicit Copy action instead for consistency.
212. Copy Formatting
Default copy:
plain text
Optional:
Copy with formatting
later.
213. Code Blocks
If supported:
monospace
horizontal scrolling
copy button
214. Long Messages
Collapse only if extremely long and UX benefits.
Default should show full content.
215. Spoilers
Optional future content type.
216. Sensitive Media Blur
Optional:
tap to reveal
per conversation/security setting.
217. Timeline Empty State
New conversation:
No messages yet
Say hello
218. Request Empty State
If no accepted messages but request context exists:
Message request
banner remains.
219. Core Loading
If recent page unavailable:
small loading skeleton
220. Older History Loading
Top loader/spinner.
Do not block current messages.
221. History End
At oldest:
no endless spinner
Optional subtle:
Beginning of conversation
222. History Gap
If local data missing:
Load older messages
or:
History unavailable on this device
depending sync model.
223. Archive Node History
If trusted archive can supply old messages:
Load from archive
future feature.
224. Message Integrity Problem
If message fails verification:
This message could not be verified
security handling from Part 28.
225. Screenshots / Testing States
Required screenshot fixtures:
empty
normal direct
group
unread boundary
reply
edited
deleted
reactions
image
file
voice note
failed outgoing
queued offline
typing
request
dark mode
RTL
large font
226. Timeline Interaction Tests
Verify:
prepend paging
anchor preservation
new message while scrolled up
jump to reply
search jump
selection
edit
delete
retry
227. Android Tests
Include:
IME open/close
rotation
process recreation
TalkBack
large font
long press selection
228. Desktop Tests
Include:
right click
keyboard search
text selection
multi-select
drag/drop
229. Multi-Device Tests
Scenario:
message sent on desktop
appears outgoing on phone
Scenario:
phone reads
desktop delivery/read state updates
230. Offline Tests
Send several messages offline.
Expected:
stable local order
queued state
no duplicate on reconnect
231. Duplicate Delivery Test
Same MessageId through multiple routes:
one row
232. Edit/Delete Race Tests
Remote edits/deletes while user viewing/selected.
No crash or stale action.
233. Pagination Race
Older page arrives while new messages append.
Anchor/order remain correct.
234. Search Jump Race
Target edited/deleted during navigation.
Fail gracefully.
235. Attachment Failure Test
Transfer failure updates only affected row.
236. Group Reaction Flood
Many reaction changes remain bounded/recompose local row only.
237. Rust Presentation API
#![allow(unused)] fn main() { pub trait ConversationPresentation { async fn open( &self, conversation: ConversationId, ) -> Result<ConversationScreenSnapshot, UiError>; async fn older( &self, conversation: ConversationId, cursor: MessageCursor, ) -> Result<MessagePage, UiError>; async fn around( &self, request: MessageAroundRequest, ) -> Result<MessagePage, UiError>; async fn send( &self, command: SendMessageCommand, ) -> Result<MessageView, UiError>; async fn retry( &self, message: MessageId, ) -> Result<(), UiError>; async fn edit( &self, command: EditMessageCommand, ) -> Result<MessageView, UiError>; async fn delete( &self, command: DeleteMessageCommand, ) -> Result<(), UiError>; } }
238. Timeline Events
#![allow(unused)] fn main() { pub enum ConversationUiEvent { MessageInserted(MessageView), MessageUpdated(MessageView), MessageRemoved(MessageId), DeliveryChanged { message: MessageId, state: MessageDeliveryView, }, ReactionsChanged { message: MessageId, reactions: Vec<ReactionSummary>, }, TypingChanged(TypingSummary), RecordingChanged(RecordingSummary), HeaderChanged(ConversationHeaderView), } }
239. Event Locality
Only update affected:
message row
header
typing region
240. Android ViewModel
Owns:
reply target
edit target
selection set
search navigation state
scroll-restoration metadata
UiEffects
Rust owns message truth.
241. Dioxus Presenter
Owns:
current selection
reply/edit presentation state
scroll anchor
search navigation presentation
242. Reply/Edit Draft State
Draft text may be UI-local or Rust durable draft service.
Architecture should support later persistence.
243. Scroll Restoration
Store:
anchor MessageId
offset
not row number.
244. Android Process Death
On recreation:
open conversation snapshot
restore anchor if valid
restore draft if policy supports
Do not restore old typing state.
245. Desktop Window Reopen
Same.
246. Security Boundary
Timeline never receives:
raw keys
transport secrets
internal ratchet state
247. Unknown Message Extensions
Plugin/extension content can render through safe registered renderer.
248. Plugin Renderer Boundary
Plugin should not inject arbitrary unsafe UI code into core process.
Use:
declarative safe rendering
or
sandboxed extension surface
from Parts 21–24.
249. Unsupported Plugin Message
Show fallback:
Message requires plugin X
with safe install/details action.
250. Telemetry Privacy
Do not collect:
message content
scroll-reading behavior
reply content
by default.
251. Safe Performance Metrics
timeline render latency
page load latency
recomposition count
scroll frame drops
without content.
252. Definition of Done
UI/UX Part 05 is complete when:
- the conversation timeline is driven by Rust paged message projections
- every message row uses stable
MessageId - logical ordering uses stable sequence rather than arrival time
- prepend pagination preserves scroll anchor
- incoming messages do not yank users reading history
- unread boundary comes from durable read cursor
- date separators and message grouping are well-defined
- replies navigate by MessageId and tolerate missing targets
- edit/delete/reaction capabilities come from Rust
- outgoing queued/sent/delivered/read/failed states are clearly represented
- retry does not create duplicate messages
- attachments, images, videos, files, and voice notes have defined states
- media transfer progress is throttled and localized to affected rows
- typing/recording remain ephemeral overlays, not timeline rows
- system/security/call events have distinct semantic rendering
- search can jump to messages outside current loaded page
- desktop Dioxus supports context menus, keyboard, selection, and text copy
- Android Compose supports long-press selection, IME-safe layout, lifecycle recreation, and touch interactions
- large histories are virtualized/paged and memory-bounded
- accessibility, RTL, large-font, screen-reader, and reduced-motion behavior are explicit
- process restart restores durable truth and scroll/draft presentation safely
- duplicate delivery, edit/delete races, pagination races, offline queues, and multi-device updates are covered by tests
253. Final Architecture
RUST CONVERSATION STATE
│
Paged Message Projection
│
┌──────────────────┼──────────────────┐
│ │ │
Durable Rows Delivery State Attachments
│ │ │
└──────────────────┼──────────────────┘
│
Conversation Snapshot
│
┌───────────────┴───────────────┐
│ │
Dioxus Compose
│ │
Virtualized Timeline LazyColumn
│ │
└──────────────┬────────────────┘
│
Stable MessageId
│
Reply / Edit / Search
Ephemeral overlay:
Typing
Recording
Presence
is rendered around the timeline but never becomes durable row truth.
254. Final Principle
The timeline should protect the user's reading position and mental context even while the distributed system changes underneath it.
The right model is:
stable logical ordering
+
durable message truth
+
anchor-preserving pagination
+
localized incremental updates
+
ephemeral overlays
+
platform-native interaction
not:
re-render and re-sort the entire conversation whenever anything changes
This gives both Dioxus desktop and Jetpack Compose Android a fast, predictable, accessible conversation experience over the shared Rust communication engine.