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 pub upload_progress: Option<UploadProgress>,
43}
44
45#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
46pub struct UploadProgress {
47 pub bytes_uploaded: u64,
48 pub total_bytes: u64,
49}
50
51#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
52pub struct PreferencesSnapshot {
53 pub send_typing_indicators: bool,
54 pub send_read_receipts: bool,
55 pub desktop_notifications_enabled: bool,
56 pub invite_acceptance_notifications_enabled: bool,
57 pub startup_at_login_enabled: bool,
58 pub nearby_enabled: bool,
59 pub nearby_bluetooth_enabled: bool,
60 pub nearby_lan_enabled: bool,
61 pub nearby_mailbag_enabled: bool,
66 pub nostr_relay_urls: Vec<String>,
67 pub image_proxy_enabled: bool,
68 pub image_proxy_url: String,
69 pub image_proxy_key_hex: String,
70 pub image_proxy_salt_hex: String,
71 pub muted_chat_ids: Vec<String>,
72 pub pinned_chat_ids: Vec<String>,
73 pub blocked_owner_pubkeys: Vec<String>,
78 pub accepted_owner_pubkeys: Vec<String>,
83 pub debug_logging_enabled: bool,
84 pub accept_unknown_direct_messages: bool,
85 pub mobile_push_server_url: String,
91}
92
93impl Default for PreferencesSnapshot {
94 fn default() -> Self {
95 Self {
96 send_typing_indicators: false,
97 send_read_receipts: true,
98 desktop_notifications_enabled: true,
99 invite_acceptance_notifications_enabled: true,
100 startup_at_login_enabled: true,
101 nearby_enabled: true,
102 nearby_bluetooth_enabled: false,
103 nearby_lan_enabled: false,
104 nearby_mailbag_enabled: true,
105 nostr_relay_urls: crate::core::configured_relays(),
106 image_proxy_enabled: true,
107 image_proxy_url: crate::image_proxy::DEFAULT_IMAGE_PROXY_URL.to_string(),
108 image_proxy_key_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_KEY_HEX.to_string(),
109 image_proxy_salt_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_SALT_HEX.to_string(),
110 muted_chat_ids: Vec::new(),
111 pinned_chat_ids: Vec::new(),
112 blocked_owner_pubkeys: Vec::new(),
113 accepted_owner_pubkeys: Vec::new(),
114 debug_logging_enabled: false,
115 accept_unknown_direct_messages: true,
116 mobile_push_server_url: String::new(),
117 }
118 }
119}
120
121#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
122pub struct OutgoingAttachment {
123 pub file_path: String,
124 pub filename: String,
125}
126
127#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
128pub struct AttachmentDownloadResult {
129 pub data_base64: Option<String>,
130 pub error: Option<String>,
131}
132
133#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
134pub enum DeviceAuthorizationState {
135 Authorized,
136 AwaitingApproval,
137 Revoked,
138}
139
140#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
141pub struct AccountSnapshot {
142 pub public_key_hex: String,
143 pub npub: String,
144 pub display_name: String,
145 pub picture_url: Option<String>,
146 pub device_public_key_hex: String,
147 pub device_npub: String,
148 pub has_owner_signing_authority: bool,
149 pub authorization_state: DeviceAuthorizationState,
150}
151
152#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
153pub struct DeviceEntrySnapshot {
154 pub device_pubkey_hex: String,
155 pub device_npub: String,
156 pub is_current_device: bool,
157 pub is_authorized: bool,
158 pub is_stale: bool,
159 pub added_at_secs: Option<u64>,
160 pub device_label: Option<String>,
161 pub client_label: Option<String>,
162}
163
164#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
165pub struct DeviceRosterSnapshot {
166 pub owner_public_key_hex: String,
167 pub owner_npub: String,
168 pub current_device_public_key_hex: String,
169 pub current_device_npub: String,
170 pub can_manage_devices: bool,
171 pub authorization_state: DeviceAuthorizationState,
172 pub devices: Vec<DeviceEntrySnapshot>,
173}
174
175#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
176pub enum DeliveryState {
177 Queued,
178 Pending,
179 Sent,
180 Received,
181 Seen,
182 Failed,
183}
184
185#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
186pub enum ChatKind {
187 Direct,
188 Group,
189}
190
191#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
192pub enum ChatMessageKind {
193 User,
194 System,
195}
196
197#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
198pub struct MessageAttachmentSnapshot {
199 pub nhash: String,
200 pub filename: String,
201 pub filename_encoded: String,
202 pub htree_url: String,
203 pub is_image: bool,
204 pub is_video: bool,
205 pub is_audio: bool,
206}
207
208#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
209pub struct MessageReactionSnapshot {
210 pub emoji: String,
211 pub count: u64,
212 pub reacted_by_me: bool,
213}
214
215#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
216pub struct MessageReactor {
217 pub author: String,
219 pub emoji: String,
221}
222
223#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
224pub struct MessageRecipientDeliverySnapshot {
225 pub owner_pubkey_hex: String,
227 pub delivery: DeliveryState,
228 pub updated_at_secs: u64,
229}
230
231#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
232#[serde(default)]
233pub struct MessageDeliveryTraceSnapshot {
234 pub outer_event_ids: Vec<String>,
235 pub pending_relay_event_ids: Vec<String>,
236 pub queued_protocol_targets: Vec<String>,
237 pub target_device_ids: Vec<String>,
238 pub transport_channels: Vec<String>,
239 pub last_transport_error: Option<String>,
240}
241
242#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
243pub struct ChatMessageSnapshot {
244 pub id: String,
245 pub chat_id: String,
246 pub kind: ChatMessageKind,
247 pub author: String,
248 pub body: String,
249 pub attachments: Vec<MessageAttachmentSnapshot>,
250 pub reactions: Vec<MessageReactionSnapshot>,
251 pub reactors: Vec<MessageReactor>,
252 pub is_outgoing: bool,
253 pub created_at_secs: u64,
254 pub expires_at_secs: Option<u64>,
255 pub delivery: DeliveryState,
256 pub recipient_deliveries: Vec<MessageRecipientDeliverySnapshot>,
257 pub delivery_trace: MessageDeliveryTraceSnapshot,
258 pub source_event_id: Option<String>,
265}
266
267#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
268pub struct TypingIndicatorSnapshot {
269 pub chat_id: String,
270 pub display_name: String,
271 pub expires_at_secs: u64,
272}
273
274#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
275pub struct ChatThreadSnapshot {
276 pub chat_id: String,
277 pub kind: ChatKind,
278 pub display_name: String,
279 pub subtitle: Option<String>,
280 pub picture_url: Option<String>,
281 pub member_count: u64,
282 pub last_message_preview: Option<String>,
283 pub last_message_at_secs: Option<u64>,
284 pub last_message_is_outgoing: Option<bool>,
285 pub last_message_delivery: Option<DeliveryState>,
286 pub unread_count: u64,
287 pub is_typing: bool,
288 pub is_muted: bool,
289 pub is_pinned: bool,
290 pub draft: String,
295 pub is_request: bool,
302}
303
304#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
305pub struct CurrentChatSnapshot {
306 pub chat_id: String,
307 pub kind: ChatKind,
308 pub display_name: String,
309 pub subtitle: Option<String>,
310 pub picture_url: Option<String>,
311 pub group_id: Option<String>,
312 pub member_count: u64,
313 pub message_ttl_seconds: Option<u64>,
314 pub is_muted: bool,
315 pub messages: Vec<ChatMessageSnapshot>,
316 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
317 pub draft: String,
320 pub is_request: bool,
323}
324
325#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
326pub struct GroupMemberSnapshot {
327 pub owner_pubkey_hex: String,
328 pub display_name: String,
329 pub npub: String,
330 pub is_admin: bool,
331 pub is_creator: bool,
332 pub is_local_owner: bool,
333}
334
335#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
336pub struct GroupDetailsSnapshot {
337 pub group_id: String,
338 pub name: String,
339 pub picture_url: Option<String>,
340 pub created_by_display_name: String,
341 pub created_by_npub: String,
342 pub can_manage: bool,
343 pub is_muted: bool,
344 pub revision: u64,
345 pub members: Vec<GroupMemberSnapshot>,
346}
347
348#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
349pub struct MutualGroupsSnapshot {
350 pub groups: Vec<ChatThreadSnapshot>,
351}
352
353#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
354pub struct RelayConnectionSnapshot {
355 pub url: String,
356 pub status: String,
357}
358
359#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
360pub struct NetworkStatusSnapshot {
361 pub relay_set_id: String,
362 pub relay_urls: Vec<String>,
363 pub relay_connections: Vec<RelayConnectionSnapshot>,
364 pub connected_relay_count: u64,
365 pub all_relays_offline_since_secs: Option<u64>,
366 pub syncing: bool,
367 pub pending_outbound_count: u64,
368 pub pending_group_control_count: u64,
369 pub recent_event_count: u64,
370 pub recent_log_count: u64,
371 pub last_debug_category: Option<String>,
372 pub last_debug_detail: Option<String>,
373}
374
375#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
376pub struct MobilePushSessionSnapshot {
377 pub recipient_pubkey_hex: String,
378 pub display_name: String,
379 pub state_json: String,
380 pub tracked_sender_pubkeys: Vec<String>,
381 pub has_receiving_capability: bool,
382}
383
384#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
385pub struct MobilePushSyncSnapshot {
386 pub owner_pubkey_hex: Option<String>,
387 pub message_author_pubkeys: Vec<String>,
388 pub invite_response_pubkeys: Vec<String>,
389 pub sessions: Vec<MobilePushSessionSnapshot>,
390}
391
392#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
393pub struct PeerProfileDebugSnapshot {
394 pub owner_pubkey_hex: String,
395 pub owner_npub: String,
396 pub roster_device_count: u64,
397 pub known_device_count: u64,
398 pub active_session_count: u64,
399 pub session_count: u64,
400 pub receiving_session_count: u64,
401 pub tracked_sender_count: u64,
402 pub recent_handshake_device_count: u64,
403 pub last_handshake_at_secs: Option<u64>,
404 pub tracked_for_messages: bool,
405}
406
407#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
408pub struct MobilePushNotificationResolution {
409 pub should_show: bool,
410 pub title: String,
411 pub body: String,
412 pub payload_json: String,
413}
414
415#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
416pub struct MobilePushSubscriptionRequest {
417 pub method: String,
418 pub url: String,
419 pub authorization_header: String,
420 pub body_json: Option<String>,
421}
422
423#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
428pub struct MessageSearchHit {
429 pub chat_id: String,
430 pub message_id: String,
431 pub chat_display_name: String,
432 pub chat_picture_url: Option<String>,
433 pub chat_kind: ChatKind,
434 pub author_pubkey: String,
435 pub body: String,
436 pub is_outgoing: bool,
437 pub created_at_secs: u64,
438}
439
440#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
441pub struct SearchResultSnapshot {
442 pub query: String,
443 pub scope_chat_id: Option<String>,
444 pub contacts: Vec<ChatThreadSnapshot>,
445 pub groups: Vec<ChatThreadSnapshot>,
446 pub messages: Vec<MessageSearchHit>,
447 pub shortcut: Option<ChatInputShortcut>,
452}
453
454#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
460pub enum ChatInputShortcut {
461 DirectPeer {
464 peer_input: String,
465 display: String,
466 npub: String,
467 pubkey_hex: String,
468 },
469 Invite {
472 invite_input: String,
473 display: String,
474 },
475}
476
477impl SearchResultSnapshot {
478 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
479 Self {
480 query,
481 scope_chat_id,
482 contacts: Vec::new(),
483 groups: Vec::new(),
484 messages: Vec::new(),
485 shortcut: None,
486 }
487 }
488
489 pub fn is_empty(&self) -> bool {
490 self.contacts.is_empty()
491 && self.groups.is_empty()
492 && self.messages.is_empty()
493 && self.shortcut.is_none()
494 }
495}
496
497#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
498pub struct PublicInviteSnapshot {
499 pub url: String,
500}
501
502#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
503pub struct LinkDeviceSnapshot {
504 pub url: String,
505 pub device_input: String,
506}
507
508#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
509pub struct AppState {
510 pub rev: u64,
511 pub router: Router,
512 pub account: Option<AccountSnapshot>,
513 pub device_roster: Option<DeviceRosterSnapshot>,
514 pub busy: BusyState,
515 pub chat_list: Vec<ChatThreadSnapshot>,
516 pub current_chat: Option<CurrentChatSnapshot>,
517 pub group_details: Option<GroupDetailsSnapshot>,
518 pub public_invite: Option<PublicInviteSnapshot>,
519 pub link_device: Option<LinkDeviceSnapshot>,
520 pub network_status: Option<NetworkStatusSnapshot>,
521 pub mobile_push: MobilePushSyncSnapshot,
522 pub preferences: PreferencesSnapshot,
523 pub toast: Option<String>,
524}
525
526impl AppState {
527 pub fn empty() -> Self {
528 Self {
529 rev: 0,
530 router: Router {
531 default_screen: Screen::Welcome,
532 screen_stack: Vec::new(),
533 },
534 account: None,
535 device_roster: None,
536 busy: BusyState::default(),
537 chat_list: Vec::new(),
538 current_chat: None,
539 group_details: None,
540 public_invite: None,
541 link_device: None,
542 network_status: None,
543 mobile_push: MobilePushSyncSnapshot::default(),
544 preferences: PreferencesSnapshot::default(),
545 toast: None,
546 }
547 }
548}