1use serde::{Deserialize, Serialize};
2
3#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
4pub enum Screen {
5 Welcome,
6 CreateAccount,
7 RestoreAccount,
8 AddDevice,
9 ChatList,
10 NewChat,
11 NewGroup,
12 CreateInvite,
13 JoinInvite,
14 Settings,
15 Chat { chat_id: String },
16 GroupDetails { group_id: String },
17 DeviceRoster,
18 AwaitingDeviceApproval,
19 DeviceRevoked,
20}
21
22#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
23pub struct Router {
24 pub default_screen: Screen,
25 pub screen_stack: Vec<Screen>,
26}
27
28#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
29pub struct BusyState {
30 pub creating_account: bool,
31 pub restoring_session: bool,
32 pub linking_device: bool,
33 pub creating_chat: bool,
34 pub creating_group: bool,
35 pub sending_message: bool,
36 pub updating_roster: bool,
37 pub updating_group: bool,
38 pub creating_invite: bool,
39 pub accepting_invite: bool,
40 pub syncing_network: bool,
41 pub uploading_attachment: bool,
42}
43
44#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
45pub struct PreferencesSnapshot {
46 pub send_typing_indicators: bool,
47 pub send_read_receipts: bool,
48 pub desktop_notifications_enabled: bool,
49 pub invite_acceptance_notifications_enabled: bool,
50 pub startup_at_login_enabled: bool,
51 pub nearby_bluetooth_enabled: bool,
52 pub nearby_lan_enabled: bool,
53 pub nostr_relay_urls: Vec<String>,
54 pub image_proxy_enabled: bool,
55 pub image_proxy_url: String,
56 pub image_proxy_key_hex: String,
57 pub image_proxy_salt_hex: String,
58 pub muted_chat_ids: Vec<String>,
59 pub pinned_chat_ids: Vec<String>,
60 pub debug_logging_enabled: bool,
61 pub accept_unknown_direct_messages: bool,
62 pub mobile_push_server_url: String,
68}
69
70impl Default for PreferencesSnapshot {
71 fn default() -> Self {
72 Self {
73 send_typing_indicators: false,
74 send_read_receipts: true,
75 desktop_notifications_enabled: true,
76 invite_acceptance_notifications_enabled: true,
77 startup_at_login_enabled: true,
78 nearby_bluetooth_enabled: false,
79 nearby_lan_enabled: false,
80 nostr_relay_urls: crate::core::configured_relays(),
81 image_proxy_enabled: true,
82 image_proxy_url: crate::image_proxy::DEFAULT_IMAGE_PROXY_URL.to_string(),
83 image_proxy_key_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_KEY_HEX.to_string(),
84 image_proxy_salt_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_SALT_HEX.to_string(),
85 muted_chat_ids: Vec::new(),
86 pinned_chat_ids: Vec::new(),
87 debug_logging_enabled: false,
88 accept_unknown_direct_messages: true,
89 mobile_push_server_url: String::new(),
90 }
91 }
92}
93
94#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
95pub struct OutgoingAttachment {
96 pub file_path: String,
97 pub filename: String,
98}
99
100#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
101pub struct AttachmentDownloadResult {
102 pub data_base64: Option<String>,
103 pub error: Option<String>,
104}
105
106#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
107pub enum DeviceAuthorizationState {
108 Authorized,
109 AwaitingApproval,
110 Revoked,
111}
112
113#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
114pub struct AccountSnapshot {
115 pub public_key_hex: String,
116 pub npub: String,
117 pub display_name: String,
118 pub picture_url: Option<String>,
119 pub device_public_key_hex: String,
120 pub device_npub: String,
121 pub has_owner_signing_authority: bool,
122 pub authorization_state: DeviceAuthorizationState,
123}
124
125#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
126pub struct DeviceEntrySnapshot {
127 pub device_pubkey_hex: String,
128 pub device_npub: String,
129 pub is_current_device: bool,
130 pub is_authorized: bool,
131 pub is_stale: bool,
132 pub added_at_secs: Option<u64>,
133}
134
135#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
136pub struct DeviceRosterSnapshot {
137 pub owner_public_key_hex: String,
138 pub owner_npub: String,
139 pub current_device_public_key_hex: String,
140 pub current_device_npub: String,
141 pub can_manage_devices: bool,
142 pub authorization_state: DeviceAuthorizationState,
143 pub devices: Vec<DeviceEntrySnapshot>,
144}
145
146#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
147pub enum DeliveryState {
148 Queued,
149 Pending,
150 Sent,
151 Received,
152 Seen,
153 Failed,
154}
155
156#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
157pub enum ChatKind {
158 Direct,
159 Group,
160}
161
162#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
163pub enum ChatMessageKind {
164 User,
165 System,
166}
167
168#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
169pub struct MessageAttachmentSnapshot {
170 pub nhash: String,
171 pub filename: String,
172 pub filename_encoded: String,
173 pub htree_url: String,
174 pub is_image: bool,
175 pub is_video: bool,
176 pub is_audio: bool,
177}
178
179#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
180pub struct MessageReactionSnapshot {
181 pub emoji: String,
182 pub count: u64,
183 pub reacted_by_me: bool,
184}
185
186#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
187pub struct MessageReactor {
188 pub author: String,
190 pub emoji: String,
192}
193
194#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
195pub struct MessageRecipientDeliverySnapshot {
196 pub owner_pubkey_hex: String,
198 pub delivery: DeliveryState,
199 pub updated_at_secs: u64,
200}
201
202#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
203#[serde(default)]
204pub struct MessageDeliveryTraceSnapshot {
205 pub outer_event_ids: Vec<String>,
206 pub pending_relay_event_ids: Vec<String>,
207 pub queued_protocol_targets: Vec<String>,
208 pub target_device_ids: Vec<String>,
209 pub transport_channels: Vec<String>,
210 pub last_transport_error: Option<String>,
211}
212
213#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
214pub struct ChatMessageSnapshot {
215 pub id: String,
216 pub chat_id: String,
217 pub kind: ChatMessageKind,
218 pub author: String,
219 pub body: String,
220 pub attachments: Vec<MessageAttachmentSnapshot>,
221 pub reactions: Vec<MessageReactionSnapshot>,
222 pub reactors: Vec<MessageReactor>,
223 pub is_outgoing: bool,
224 pub created_at_secs: u64,
225 pub expires_at_secs: Option<u64>,
226 pub delivery: DeliveryState,
227 pub recipient_deliveries: Vec<MessageRecipientDeliverySnapshot>,
228 pub delivery_trace: MessageDeliveryTraceSnapshot,
229 pub source_event_id: Option<String>,
236}
237
238#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
239pub struct TypingIndicatorSnapshot {
240 pub chat_id: String,
241 pub display_name: String,
242 pub expires_at_secs: u64,
243}
244
245#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
246pub struct ChatThreadSnapshot {
247 pub chat_id: String,
248 pub kind: ChatKind,
249 pub display_name: String,
250 pub subtitle: Option<String>,
251 pub picture_url: Option<String>,
252 pub member_count: u64,
253 pub last_message_preview: Option<String>,
254 pub last_message_at_secs: Option<u64>,
255 pub last_message_is_outgoing: Option<bool>,
256 pub last_message_delivery: Option<DeliveryState>,
257 pub unread_count: u64,
258 pub is_typing: bool,
259 pub is_muted: bool,
260 pub is_pinned: bool,
261 pub draft: String,
266}
267
268#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
269pub struct CurrentChatSnapshot {
270 pub chat_id: String,
271 pub kind: ChatKind,
272 pub display_name: String,
273 pub subtitle: Option<String>,
274 pub picture_url: Option<String>,
275 pub group_id: Option<String>,
276 pub member_count: u64,
277 pub message_ttl_seconds: Option<u64>,
278 pub is_muted: bool,
279 pub messages: Vec<ChatMessageSnapshot>,
280 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
281 pub draft: String,
284}
285
286#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
287pub struct GroupMemberSnapshot {
288 pub owner_pubkey_hex: String,
289 pub display_name: String,
290 pub npub: String,
291 pub is_admin: bool,
292 pub is_creator: bool,
293 pub is_local_owner: bool,
294}
295
296#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
297pub struct GroupDetailsSnapshot {
298 pub group_id: String,
299 pub name: String,
300 pub picture_url: Option<String>,
301 pub created_by_display_name: String,
302 pub created_by_npub: String,
303 pub can_manage: bool,
304 pub is_muted: bool,
305 pub revision: u64,
306 pub members: Vec<GroupMemberSnapshot>,
307}
308
309#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
310pub struct RelayConnectionSnapshot {
311 pub url: String,
312 pub status: String,
313}
314
315#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
316pub struct NetworkStatusSnapshot {
317 pub relay_set_id: String,
318 pub relay_urls: Vec<String>,
319 pub relay_connections: Vec<RelayConnectionSnapshot>,
320 pub connected_relay_count: u64,
321 pub all_relays_offline_since_secs: Option<u64>,
322 pub syncing: bool,
323 pub pending_outbound_count: u64,
324 pub pending_group_control_count: u64,
325 pub recent_event_count: u64,
326 pub recent_log_count: u64,
327 pub last_debug_category: Option<String>,
328 pub last_debug_detail: Option<String>,
329}
330
331#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
332pub struct MobilePushSessionSnapshot {
333 pub recipient_pubkey_hex: String,
334 pub display_name: String,
335 pub state_json: String,
336 pub tracked_sender_pubkeys: Vec<String>,
337 pub has_receiving_capability: bool,
338}
339
340#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
341pub struct MobilePushSyncSnapshot {
342 pub owner_pubkey_hex: Option<String>,
343 pub message_author_pubkeys: Vec<String>,
344 pub invite_response_pubkeys: Vec<String>,
345 pub sessions: Vec<MobilePushSessionSnapshot>,
346}
347
348#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
349pub struct PeerProfileDebugSnapshot {
350 pub owner_pubkey_hex: String,
351 pub owner_npub: String,
352 pub roster_device_count: u64,
353 pub known_device_count: u64,
354 pub active_session_count: u64,
355 pub session_count: u64,
356 pub receiving_session_count: u64,
357 pub tracked_sender_count: u64,
358 pub recent_handshake_device_count: u64,
359 pub last_handshake_at_secs: Option<u64>,
360 pub tracked_for_messages: bool,
361}
362
363#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
364pub struct MobilePushNotificationResolution {
365 pub should_show: bool,
366 pub title: String,
367 pub body: String,
368 pub payload_json: String,
369}
370
371#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
372pub struct MobilePushSubscriptionRequest {
373 pub method: String,
374 pub url: String,
375 pub authorization_header: String,
376 pub body_json: Option<String>,
377}
378
379#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
384pub struct MessageSearchHit {
385 pub chat_id: String,
386 pub message_id: String,
387 pub chat_display_name: String,
388 pub chat_picture_url: Option<String>,
389 pub chat_kind: ChatKind,
390 pub author_pubkey: String,
391 pub body: String,
392 pub is_outgoing: bool,
393 pub created_at_secs: u64,
394}
395
396#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
397pub struct SearchResultSnapshot {
398 pub query: String,
399 pub scope_chat_id: Option<String>,
400 pub contacts: Vec<ChatThreadSnapshot>,
401 pub groups: Vec<ChatThreadSnapshot>,
402 pub messages: Vec<MessageSearchHit>,
403 pub shortcut: Option<ChatInputShortcut>,
408}
409
410#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
416pub enum ChatInputShortcut {
417 DirectPeer {
420 peer_input: String,
421 display: String,
422 npub: String,
423 pubkey_hex: String,
424 },
425 Invite {
428 invite_input: String,
429 display: String,
430 },
431}
432
433impl SearchResultSnapshot {
434 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
435 Self {
436 query,
437 scope_chat_id,
438 contacts: Vec::new(),
439 groups: Vec::new(),
440 messages: Vec::new(),
441 shortcut: None,
442 }
443 }
444
445 pub fn is_empty(&self) -> bool {
446 self.contacts.is_empty()
447 && self.groups.is_empty()
448 && self.messages.is_empty()
449 && self.shortcut.is_none()
450 }
451}
452
453#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
454pub struct PublicInviteSnapshot {
455 pub url: String,
456}
457
458#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
459pub struct LinkDeviceSnapshot {
460 pub url: String,
461 pub device_input: String,
462}
463
464#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
465pub struct AppState {
466 pub rev: u64,
467 pub router: Router,
468 pub account: Option<AccountSnapshot>,
469 pub device_roster: Option<DeviceRosterSnapshot>,
470 pub busy: BusyState,
471 pub chat_list: Vec<ChatThreadSnapshot>,
472 pub current_chat: Option<CurrentChatSnapshot>,
473 pub group_details: Option<GroupDetailsSnapshot>,
474 pub public_invite: Option<PublicInviteSnapshot>,
475 pub link_device: Option<LinkDeviceSnapshot>,
476 pub network_status: Option<NetworkStatusSnapshot>,
477 pub mobile_push: MobilePushSyncSnapshot,
478 pub preferences: PreferencesSnapshot,
479 pub toast: Option<String>,
480}
481
482impl AppState {
483 pub fn empty() -> Self {
484 Self {
485 rev: 0,
486 router: Router {
487 default_screen: Screen::Welcome,
488 screen_stack: Vec::new(),
489 },
490 account: None,
491 device_roster: None,
492 busy: BusyState::default(),
493 chat_list: Vec::new(),
494 current_chat: None,
495 group_details: None,
496 public_invite: None,
497 link_device: None,
498 network_status: None,
499 mobile_push: MobilePushSyncSnapshot::default(),
500 preferences: PreferencesSnapshot::default(),
501 toast: None,
502 }
503 }
504}