UI/UX Part 01 — Product UX Foundation & Cross-Platform Interaction Architecture

Reusable P2P Communication Platform

Status: UI/UX architecture specification
UI Series: Part 01
Desktop UI: Dioxus
Android UI: Pure Kotlin + Jetpack Compose
Core runtime: Rust


1. Core Decision

The product will use:

Desktop:
    Rust
    Dioxus

Android:
    Kotlin
    Jetpack Compose

Shared:
    Rust domain/core/runtime
    Rust networking
    Rust storage
    Rust security
    Rust sync
    Rust calls/media policy
    Rust search
    Rust backup

The UI frameworks are intentionally different.

The shared truth remains:

Rust domain model
+
Rust application services
+
Rust runtime state

The two UIs are presentation clients of the same core.


2. Why This Split Is Strong

A single cross-platform UI framework can reduce UI code duplication, but it can also force each platform into the lowest common denominator.

Android has highly platform-specific concerns:

permissions
IME
keyboard
predictive back
notifications
foreground services
share sheet
camera
Bluetooth
NFC
Picture-in-Picture
audio routing
activity/process lifecycle
accessibility
adaptive layouts

Jetpack Compose handles these naturally.

Desktop has different concerns:

multi-window
keyboard-first navigation
mouse/trackpad
system tray
drag-and-drop
window sizing
desktop notifications
file pickers
global shortcuts
large-screen density

Dioxus keeps the desktop application Rust-first.


3. Prime Rule

UI owns presentation state. Rust owns product truth.

UI may own:

current tab
selected row
open dialog
scroll position
temporary form input
animation state
hover state
local expansion

Rust owns:

messages
conversation state
delivery/read state
identity
security
contact state
file transfers
call state
presence truth
search results
backup state
notification policy
plugin state
device state

4. Avoid Two Independent Products

Do not build:

Compose ViewModel
    ↓
Android-only business logic
    ↓
Android-only persistence

while desktop has a separate implementation.

Instead:

Compose
    ↓
Android UI Adapter
    ↓
Rust Core

Dioxus
    ↓
Desktop UI Adapter
    ↓
Rust Core

5. Shared Presentation Contract

Both UIs consume stable screen-ready concepts:

ConversationSummary
MessageView
ContactSummary
CallSnapshot
PresenceSummary
TransferProgress
SearchHit
SecurityEvent
BackupStatus

Never expose raw database rows.


6. Shared Command Model

Examples:

#![allow(unused)]
fn main() {
pub enum AppCommand {
    SendMessage(...),
    EditMessage(...),
    DeleteMessage(...),
    MarkRead(...),
    StartCall(...),
    AcceptCall(...),
    RejectCall(...),
    StartFileTransfer(...),
    CancelTransfer(...),
    Search(...),
    BlockPeer(...),
    VerifyPeer(...),
}
}

Both frontends ultimately invoke the same commands.


7. Shared Event Model

Examples:

#![allow(unused)]
fn main() {
pub enum AppEvent {
    ConversationUpdated(...),
    MessageInserted(...),
    MessageChanged(...),
    PresenceChanged(...),
    CallChanged(...),
    TransferChanged(...),
    SecurityAlert(...),
    BackupChanged(...),
}
}

8. Snapshot + Event Pattern

Recommended screen lifecycle:

open screen
    ↓
request current snapshot
    ↓
render immediately
    ↓
subscribe to incremental events
    ↓
update projection

The UI should not reconstruct full application truth from event streams alone.


9. Shared UI Layer in Rust

Suggested crates:

crates/
├── comm-ui-model/
├── comm-ui-service/
├── comm-ui-events/
├── comm-ui-actions/
├── comm-ui-format/
└── comm-ui-testkit/

Applications:

apps/
├── desktop-dioxus/
└── android-compose/

10. comm-ui-model

Contains UI-safe DTOs.

Example:

#![allow(unused)]
fn main() {
pub struct ConversationSummary {
    pub id: ConversationId,
    pub title: String,
    pub subtitle: Option<String>,
    pub unread_count: u32,
    pub muted: bool,
    pub pinned: bool,
    pub presence: Option<PresenceSummary>,
    pub last_activity: Timestamp,
}
}

Do not expose:

database connections
crypto secrets
Iroh endpoint internals
actor handles
platform pointers

11. Desktop Boundary

Dioxus can call Rust presentation/application services directly.

Dioxus component
    ↓
Presenter / Hook
    ↓
Rust Presentation Service
    ↓
Rust Application/Core

If using daemon mode:

Dioxus UI
    ↓
typed local IPC
    ↓
Rust daemon

The UI semantics stay the same.


12. Android Boundary

Recommended Android path:

Jetpack Compose
    ↓
ViewModel
    ↓
RustBridge / Repository
    ↓
JNI
    ↓
Rust Presentation Service
    ↓
Rust Application/Core

JNI should expose coarse-grained semantic operations.

Good:

getConversationList()
sendMessage()
observeConversation()
startCall()

Bad:

queryRawSql()
getIrohStream()
decryptRawPacket()

13. Kotlin ViewModel Role

Android ViewModel owns:

screen lifecycle
StateFlow
navigation-facing state
temporary UI state
permission prompts
platform intents

It should not own:

message delivery rules
security state
call state machine
sync truth
file-transfer truth

14. Compose State

Recommended:

Rust event stream
    ↓
JNI event adapter
    ↓
Kotlin coroutine
    ↓
StateFlow<ScreenUiState>
    ↓
Compose

Avoid unrestricted callbacks from arbitrary Rust worker threads directly into Compose.


15. Desktop State

Dioxus consumes the same semantic snapshots/events with Rust-native mechanisms such as signals/resources/channels.


16. Shared Semantics, Independent Layouts

These semantics must be identical:

what Delivered means
what Read means
what Blocked means
when a message is editable
call state
security warning severity
transfer state

But layout should differ by platform.

Do not force pixel parity.


17. Platform-Native UX

Android should feel Android-native.

Desktop should feel desktop-native.

Share:

product semantics
terminology
information hierarchy
design language

Do not force identical:

navigation
density
gestures
window structure

18. Shared Design Language

Define conceptual tokens for:

spacing
corner radius
typography hierarchy
semantic colors
status terminology
icon meanings
motion principles

Implement separately.

Desktop Dioxus:

CSS/theme variables

Android Compose:

MaterialTheme + custom tokens

19. Primary Product Areas

Recommended top-level areas:

Chats
Calls
Contacts
Files
Search
Devices
Settings

Optional:

Plugins
Emergency
Diagnostics

20. Desktop Navigation

Preferred architecture:

Left rail/sidebar
    Chats
    Calls
    Contacts
    Files

Secondary pane
    list

Main pane
    active content

Optional details pane
    info/search/media/security

Desktop should exploit:

keyboard
mouse
hover
split panes
context menus
resizable layouts
multi-selection

21. Android Navigation

Phone:

Chats
Calls
Contacts

as primary destinations.

Secondary features:

Files
Devices
Settings
Search

through app bar/navigation hierarchy.

Tablet/foldable:

navigation rail
+
list pane
+
detail pane

when width allows.


22. Conversation Screen Semantics

Shared structure:

header
message timeline
presence/typing
composer
attachments
call actions

Desktop may use wide split panes.

Android phone should use a focused single-pane conversation.


23. Message Timeline Ownership

Rust owns:

paged messages
stable MessageId
message revisions
delivery/read state

UI owns:

viewport
scroll anchor
selection
animations

Key rows by MessageId, never list index.


24. Paging

Do not load complete history.

Use:

load recent window
scroll upward
load older page
preserve anchor

If user is reading old history and new messages arrive:

show N new messages

Do not yank the viewport.


25. Read Detection

Part 30 owns semantics.

UI reports what is actually read.

Desktop factors:

window focused
conversation active
message visible

Android factors:

activity resumed
conversation visible
LazyColumn viewport
device/app state

26. Composer

UI may hold draft text locally.

When user sends:

UI command
→ Rust creates durable pending message
→ Rust returns MessageView
→ UI renders pending state

Do not create a fake UI-only message that later needs difficult reconciliation.


27. Message States

Visual states:

Queued
Sending
Sent
Delivered
Read
Failed

Failure actions:

Retry
Delete
Details

28. Message Actions

Depending permissions/type:

Reply
Copy
Edit
Delete
Forward
Save attachment
Info

Android:

long press
selection mode
bottom sheet

Desktop:

right click
hover actions
keyboard shortcuts
multi-select

29. Calls

Part 29 remains authoritative.

UI renders:

Incoming
Outgoing ringing
Connecting
Active
Reconnecting
Held
Ended

Android Compose integrates:

full-screen call UI
PiP
notification actions
audio route chooser

Desktop Dioxus may provide:

main call screen
floating/minimized call panel
separate call window

30. Raw Media Must Bypass Ordinary UI State

Android:

Surface/native renderer

Desktop:

native video/GPU renderer handle

UI receives:

RendererId
participant
mute state
quality state

not raw video frames or PCM.


31. File Transfer UX

Common states:

Preparing
Waiting for peer
Transferring
Paused
Verifying
Complete
Failed

Show:

progress
bytes
speed
ETA when stable

Do not let UI own transfer state.


32. Large Files Across Android JNI

Use:

file descriptor
stream handle
content URI adapter

Never transfer giant file contents through ByteArray.


33. Search

Part 32 supplies search engine.

Desktop:

keyboard-first global search
multi-section results
wide detail view

Android:

search app bar/destination
filter chips
single-column results

Search hit navigation uses stable logical IDs such as MessageId.


34. Contacts and Identity

UI should distinguish:

saved contact
verified identity
nearby discovered peer
unknown request
blocked peer

A saved display name does not equal cryptographic verification.


35. Device Management

Devices screen should show:

This device
Other authorized devices
Last active
Verification
Revoke

High-risk actions such as revoke require clear confirmation.


36. Pairing / Nearby UX

Separate:

Add Contact
Link My Device
Nearby Devices
Scan QR
Tap NFC

Own-device linking must not look identical to adding a contact.


37. Pairing Flow

Discover / Scan
    ↓
Authenticate
    ↓
Confirm SAS
    ↓
Name device/contact
    ↓
Complete

Errors:

Expired code
Already used
Identity mismatch
Unsupported version

38. Security UX

Do not overload the normal chat UI with crypto details.

Show security state when actionable:

Verified
Identity changed
New device added
Device revoked
Unverified authority

Provide a dedicated Security Center.


39. Security Center

Recommended sections:

My Devices
Trusted Contacts
Identity / Fingerprint
Security Events
Blocked Peers
Recovery

40. Emergency UX

Part 17 deserves dedicated visual treatment.

Potential surfaces:

SOS
Verified Alerts
Emergency Contacts
Offline Mesh Status

Prevent accidental SOS with an intentional interaction:

press and hold
confirmation
short countdown

depending product use case.


41. Settings Information Architecture

Organize by user intent:

Account
Privacy & Security
Notifications
Calls & Media
Storage & Data
Devices
Appearance
Plugins
Advanced
About

Do not expose backend module names such as:

DTN Scheduler
ALPN
Ratchet Store
Resource Limiter

to ordinary users.


42. Progressive Disclosure

Normal users see simple defaults.

Advanced users can open:

Network Diagnostics
Protocol/Codec Information
Developer Mode
Plugin Developer Tools

43. Error Architecture

Classify UI-facing failures:

#![allow(unused)]
fn main() {
pub enum UiFailureClass {
    Transient,
    ActionRequired,
    Permission,
    Security,
    Storage,
    Unsupported,
    Fatal,
}
}

44. Error Presentation

Transient:

small banner/snackbar
retry automatically where safe

Action required:

clear banner + action

Security:

strong warning
no silent retry

Fatal:

recovery screen
diagnostics
backup restore path

45. Offline UX

Offline is a normal operating state.

Avoid treating it as application failure.

Examples:

Queued
Will send when a connection is available

Use subtle status unless user needs action.


46. Local-First UX

Existing local conversations, files, contacts, and search should appear without waiting for Internet.

Prefer:

slightly stale local data

over:

blank spinner waiting for network

47. Loading States

Use progressive/skeleton states for lists.

Avoid fullscreen spinners for ordinary local reads.


48. Empty States

Each major destination needs an intentional empty state:

No conversations yet
No files yet
No linked devices
No search results

Each should offer one useful next action.


49. Accessibility

Accessibility is architectural, not polish.

Android Compose:

TalkBack
semantics
focus order
content descriptions
minimum touch targets
font scaling
reduced animation

Desktop Dioxus:

keyboard navigation
screen reader semantics
focus indicators
high contrast
UI scale

50. Desktop Keyboard-First Interaction

Important shortcuts:

Ctrl/Cmd+K → Global Search
Ctrl/Cmd+N → New Conversation
Ctrl/Cmd+F → Search Conversation
Ctrl/Cmd+, → Settings
Esc → Close dialog/panel

Every critical action must remain available without hover-only UI.


51. Localization

Visible strings belong to platform/UI localization resources.

Rust should return:

stable error/status codes
structured parameters

not fully formatted English strings.

Rust:

PeerUnavailable { peer: ... }

UI:

localized human text

52. Time and Number Formatting

UI layer formats:

12/24-hour time
dates
relative time
file sizes
durations
pluralization

Rust supplies canonical values.


53. Android Process Death

Compose/ViewModel state can disappear.

Rust durable truth reloads.

SavedStateHandle should restore presentation concerns such as:

current destination
selected conversation
draft ID
scroll anchor

not stale:

typing
presence
fake call state

54. Android Platform Adapter Responsibilities

Kotlin owns platform mechanics for:

Activity
permissions
notifications
foreground services
share sheet
file picker
Camera permission
Bluetooth permission
NFC
BiometricPrompt
PiP
predictive back

Rust owns the semantic decision.


55. Platform Request Pattern

Example:

User taps Video Call
    ↓
Rust validates call state
    ↓
Rust requests CameraPermission
    ↓
Kotlin launches Android permission flow
    ↓
result returned to Rust
    ↓
Rust proceeds or degrades

56. File Picker Pattern

User taps Attach
    ↓
Compose opens Android system picker
    ↓
URI/FD returned
    ↓
Rust file subsystem receives safe handle
    ↓
normal file-transfer pipeline

57. Desktop Platform Adapter Responsibilities

Examples:

file dialogs
drag/drop
clipboard
system notifications
tray
window management
open URL

58. Command/Event Flow

Recommended:

UI
→ semantic command
→ Rust application service
→ state transition
→ event/snapshot update
→ UI

Avoid two-way mutable shared state.


59. High-Frequency State

Examples:

call quality
audio level
transfer progress
typing

should be coalesced/throttled before reaching UI.

Do not emit hundreds of updates per second into Compose or Dioxus.


60. Virtualization

Android:

LazyColumn
LazyGrid

Desktop:

virtualized long lists

Required for:

large message histories
contacts
files
search results

61. Media Thumbnails

Use appropriately sized cached thumbnails.

Do not decode original multi-megabyte media just to draw a tiny row preview.


62. Multi-Window Desktop

Potential windows:

Main
Call
Media Viewer
Settings
Diagnostics

Window state is presentation state.

Persist:

size
position
last section

locally.


63. Responsive Android

Use adaptive Compose layouts for:

phone
tablet
foldable
desktop-like Android window

Do not hardcode phone-only dimensions.


64. UI Security Boundary

UI must never receive:

private keys
ratchet secrets
raw recovery material unless explicitly displaying recovery screen
unredacted crypto internals

Sensitive screen content should be minimized in logs/crash reports.


65. Clipboard and Recovery UX

Copying:

recovery key
fingerprint

is explicit.

Android may optionally block screenshots on recovery screens if product policy chooses.


66. Authentication Gate

Sensitive actions/screens may request:

biometric
device credential

through platform adapter.


67. UI Testing Strategy

Test:

semantics
navigation
state mapping
accessibility
error flows
process death
permissions
offline behavior

not just screenshots.


68. Android Tests

Use:

ViewModel tests
Compose UI tests
JNI adapter tests
process-death tests
permission tests
adaptive-layout tests

69. Desktop Tests

Use:

component logic tests
keyboard navigation tests
window-layout tests
local IPC adapter tests

70. Shared Contract Tests

Given a Rust presentation snapshot, desktop and Android should expose equivalent product semantics.

They do not need identical pixels.


71. Performance Quality Gates

Measure:

app shell startup
conversation open
message scroll
search latency
contact-list scroll
call-screen updates

Existing local conversation should open without waiting for network.


72. UI Series Roadmap

Recommended next parts:

UI/UX 01 — Product UX Foundation & Cross-Platform Interaction Architecture
UI/UX 02 — Desktop Dioxus App Shell, Navigation & Window Architecture
UI/UX 03 — Android Compose App Shell, Navigation & Lifecycle Architecture
UI/UX 04 — Conversation List / Inbox UX
UI/UX 05 — Conversation / Message Timeline UX
UI/UX 06 — Message Composer, Attachments, Voice Notes & Drafts UX
UI/UX 07 — Calls & Realtime Media UX
UI/UX 08 — Contacts, Requests, Verification & Identity UX
UI/UX 09 — Groups, Membership & Roles UX
UI/UX 10 — Files, Media Gallery & Transfer UX
UI/UX 11 — Search & Local Knowledge Retrieval UX
UI/UX 12 — Nearby, QR/NFC Pairing & Device Linking UX
UI/UX 13 — Notifications, Background & Incoming Call UX
UI/UX 14 — Presence, Typing, Receipts & Status UX
UI/UX 15 — Security Center, Devices, Keys & Recovery UX
UI/UX 16 — Backup, Restore, Export & Migration UX
UI/UX 17 — Emergency / SOS / Offline Mesh UX
UI/UX 18 — Settings, Privacy, Notifications & Data Controls UX
UI/UX 19 — Plugin / Module Ecosystem UX
UI/UX 20 — Diagnostics, Network Paths & Advanced Developer UX
UI/UX 21 — Accessibility & Inclusive Interaction Architecture
UI/UX 22 — Design System, Tokens, Typography, Icons & Motion
UI/UX 23 — Responsive / Adaptive Desktop, Tablet, Foldable & Phone Layouts
UI/UX 24 — Error, Loading, Empty, Offline & Degraded-State UX
UI/UX 25 — Onboarding, First Run & Permission Education UX
UI/UX 26 — Performance, Virtualization & Large-Data UI Architecture
UI/UX 27 — UI Testing, Screenshot/Interaction Tests & Release Quality Gates

73. Why UI/UX Should Be Designed Now

The backend is already deep enough that continuing backend-only design risks:

awkward presentation APIs
duplicated UI projections
missing user flows
overengineered invisible features
late discovery of lifecycle problems

UI/UX design now provides feedback into:

API shapes
events
pagination
error models
permissions
notifications
calls
search
security

74. UI-Driven Backend Validation

Every screen should answer:

What snapshot does it need?
What commands can the user issue?
What events update it?
What happens offline?
What happens after process death?
What is sensitive?
What is platform-specific?

If the backend cannot answer cleanly, the backend contract needs refinement.


75. Shared Presentation Service

The best place to maximize reuse is not widget code.

It is:

screen-ready DTOs
validation
commands
events
business semantics

Example:

#![allow(unused)]
fn main() {
pub trait ConversationPresentation {
    async fn list_conversations(
        &self,
        page: ConversationPageRequest,
    ) -> Result<ConversationPage, UiError>;

    async fn open_conversation(
        &self,
        id: ConversationId,
    ) -> Result<ConversationScreenSnapshot, UiError>;

    async fn send_message(
        &self,
        command: SendMessageCommand,
    ) -> Result<MessageView, UiError>;
}
}

76. Android Event Bridge

Recommended:

Rust bounded event channel
    ↓
JNI bridge
    ↓
Coroutine
    ↓
StateFlow
    ↓
Compose

The bridge should be lifecycle-aware and bounded.


77. Desktop Event Bridge

Dioxus can consume the same Rust event channel/presentation layer directly or through local daemon IPC.


78. Shared Validation

Rust remains authority for:

message length
attachment count
file size
call eligibility
security policy
peer permissions

UI may duplicate lightweight prevalidation only for responsiveness.


79. Recomposition/Re-render Safety

Compose recomposition and Dioxus re-rendering must never:

send a message twice
start a call twice
open duplicate subscriptions
repeat destructive commands

Side effects belong in explicit event handlers/effects.


80. Subscription Lifecycle

Screen-specific subscriptions should exist only while useful.

Examples:

typing → current conversation
transfer progress → visible transfer
presence → visible contacts/conversation

Global streams:

active call
security alerts
core health

can remain app-wide.


81. Definition of Done

UI/UX Part 01 is complete when:

  • desktop uses Dioxus and Android uses pure Kotlin + Jetpack Compose
  • both UIs consume one shared Rust application/domain core
  • no business logic is independently reimplemented in Android
  • UI owns presentation state while Rust owns product truth
  • shared UI-safe DTOs exist
  • commands/events/snapshots are typed
  • Android JNI is semantic and coarse-grained
  • Kotlin ViewModels convert Rust events into Compose-friendly StateFlow
  • Dioxus consumes the same presentation service directly or via local IPC
  • platform navigation/layouts are free to differ
  • Android remains Android-native
  • desktop remains keyboard/mouse/window optimized
  • message, call, file, search, presence, security, and backup semantics remain identical
  • platform permissions/OS services are isolated behind adapters
  • raw media and large file bytes never flow through ordinary UI state
  • accessibility, localization, font scaling, reduced motion, and keyboard navigation are first-class
  • loading/offline/error/degraded states are part of every screen contract
  • backend features are validated against real screen requirements before further deep backend expansion

82. Final Architecture

                           RUST CORE
                               │
                    Presentation Service
                    Commands / Events
                     ┌─────────┴─────────┐
                     │                   │
                DESKTOP               ANDROID
                     │                   │
                  Dioxus              JNI Bridge
                     │                   │
             Rust Presenter            ViewModel
                                         │
                                      StateFlow
                                         │
                                  Jetpack Compose

Platform integrations:

Desktop:
    windows
    tray
    file dialogs
    clipboard
    notifications
    drag/drop

Android:
    Activity
    permissions
    notifications
    foreground services
    camera/audio
    NFC/Bluetooth
    file picker
    share sheet
    PiP

Both operate on the same domain identities:

ConversationId
MessageId
CallId
TransferId
DeviceId
SearchHit
SecurityEvent

83. Final Principle

The objective is not maximum UI-code sharing.

The objective is maximum:

business-logic sharing
semantic consistency
platform quality
maintainability

The ideal split is:

Rust:
    truth
    networking
    storage
    security
    sync
    calls
    search
    backup
    screen-ready presentation models

Dioxus:
    desktop interaction and presentation

Kotlin + Jetpack Compose:
    Android interaction and presentation

That gives both platforms native-quality UX without creating two separate implementations of the communication system.