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 DirectChatInfo { chat_id: String },
17 GroupDetails { group_id: String },
18 DeviceRoster,
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_show_in_chat_list: bool,
62 pub nearby_mailbag_enabled: bool,
67 pub nostr_relay_urls: Vec<String>,
68 pub image_proxy_enabled: bool,
69 pub image_proxy_fallback_enabled: bool,
70 pub image_proxy_url: String,
71 pub image_proxy_key_hex: String,
72 pub image_proxy_salt_hex: String,
73 pub muted_chat_ids: Vec<String>,
74 pub pinned_chat_ids: Vec<String>,
75 pub blocked_owner_pubkeys: Vec<String>,
80 pub accepted_owner_pubkeys: Vec<String>,
85 pub debug_logging_enabled: bool,
86 pub accept_unknown_direct_messages: bool,
87 pub mobile_push_server_url: String,
93}
94
95impl Default for PreferencesSnapshot {
96 fn default() -> Self {
97 Self {
98 send_typing_indicators: false,
99 send_read_receipts: false,
100 desktop_notifications_enabled: true,
101 invite_acceptance_notifications_enabled: true,
102 startup_at_login_enabled: true,
103 nearby_enabled: true,
104 nearby_bluetooth_enabled: false,
105 nearby_lan_enabled: false,
106 nearby_show_in_chat_list: true,
107 nearby_mailbag_enabled: true,
108 nostr_relay_urls: crate::core::configured_relays(),
109 image_proxy_enabled: true,
110 image_proxy_fallback_enabled: false,
111 image_proxy_url: crate::image_proxy::DEFAULT_IMAGE_PROXY_URL.to_string(),
112 image_proxy_key_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_KEY_HEX.to_string(),
113 image_proxy_salt_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_SALT_HEX.to_string(),
114 muted_chat_ids: Vec::new(),
115 pinned_chat_ids: Vec::new(),
116 blocked_owner_pubkeys: Vec::new(),
117 accepted_owner_pubkeys: Vec::new(),
118 debug_logging_enabled: false,
119 accept_unknown_direct_messages: true,
120 mobile_push_server_url: String::new(),
121 }
122 }
123}
124
125#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
126pub struct OutgoingAttachment {
127 pub file_path: String,
128 pub filename: String,
129}
130
131#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
132pub struct AttachmentDownloadResult {
133 pub data_base64: Option<String>,
134 pub error: Option<String>,
135}
136
137#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
138pub enum DeviceAuthorizationState {
139 Authorized,
140 AwaitingApproval,
141 Revoked,
142}
143
144#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
145pub struct AccountSnapshot {
146 pub public_key_hex: String,
147 pub npub: String,
148 pub display_name: String,
149 pub picture_url: Option<String>,
150 pub about: Option<String>,
151 pub device_public_key_hex: String,
152 pub device_npub: String,
153 pub has_owner_signing_authority: bool,
154 pub authorization_state: DeviceAuthorizationState,
155}
156
157#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
158pub struct DeviceEntrySnapshot {
159 pub device_pubkey_hex: String,
160 pub device_npub: String,
161 pub is_current_device: bool,
162 pub is_authorized: bool,
163 pub is_stale: bool,
164 pub added_at_secs: Option<u64>,
165 pub device_label: Option<String>,
166 pub client_label: Option<String>,
167}
168
169#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
170pub struct DeviceRosterSnapshot {
171 pub owner_public_key_hex: String,
172 pub owner_npub: String,
173 pub current_device_public_key_hex: String,
174 pub current_device_npub: String,
175 pub can_manage_devices: bool,
176 pub authorization_state: DeviceAuthorizationState,
177 pub devices: Vec<DeviceEntrySnapshot>,
178}
179
180#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
181pub enum DeliveryState {
182 Queued,
183 Pending,
184 Sent,
185 Received,
186 Seen,
187 Failed,
188}
189
190#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
191pub enum ChatKind {
192 Direct,
193 Group,
194}
195
196#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
197pub enum ChatMessageKind {
198 User,
199 System,
200}
201
202#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
203pub struct MessageAttachmentSnapshot {
204 pub nhash: String,
205 pub filename: String,
206 pub filename_encoded: String,
207 pub htree_url: String,
208 pub is_image: bool,
209 pub is_video: bool,
210 pub is_audio: bool,
211}
212
213#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
214pub struct MessageReactionSnapshot {
215 pub emoji: String,
216 pub count: u64,
217 pub reacted_by_me: bool,
218}
219
220#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
221pub struct MessageReactor {
222 pub author: String,
224 #[serde(default, skip_serializing_if = "String::is_empty")]
226 pub display_name: String,
227 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub picture_url: Option<String>,
229 pub emoji: String,
231}
232
233#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
234pub struct MessageRecipientDeliverySnapshot {
235 pub owner_pubkey_hex: String,
237 #[serde(default, skip_serializing_if = "String::is_empty")]
239 pub display_name: String,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub picture_url: Option<String>,
242 pub delivery: DeliveryState,
243 pub updated_at_secs: u64,
244}
245
246#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
247#[serde(default)]
248pub struct MessageDeliveryTraceSnapshot {
249 pub outer_event_ids: Vec<String>,
250 pub pending_relay_event_ids: Vec<String>,
251 pub queued_protocol_targets: Vec<String>,
252 pub transport_channels: Vec<String>,
253 pub last_transport_error: Option<String>,
254}
255
256#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
257pub struct ChatMessageSnapshot {
258 pub id: String,
259 pub chat_id: String,
260 pub kind: ChatMessageKind,
261 pub author: String,
262 pub author_owner_pubkey_hex: Option<String>,
264 pub author_picture_url: Option<String>,
265 pub body: String,
266 pub attachments: Vec<MessageAttachmentSnapshot>,
267 pub reactions: Vec<MessageReactionSnapshot>,
268 pub reactors: Vec<MessageReactor>,
269 pub is_outgoing: bool,
270 pub created_at_secs: u64,
271 pub expires_at_secs: Option<u64>,
272 pub delivery: DeliveryState,
273 pub recipient_deliveries: Vec<MessageRecipientDeliverySnapshot>,
274 pub delivery_trace: MessageDeliveryTraceSnapshot,
275 pub source_event_id: Option<String>,
282}
283
284#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
285pub struct TypingIndicatorSnapshot {
286 pub chat_id: String,
287 pub display_name: String,
288 pub expires_at_secs: u64,
289}
290
291#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
292pub struct ChatThreadSnapshot {
293 pub chat_id: String,
294 pub kind: ChatKind,
295 pub display_name: String,
296 pub nickname: Option<String>,
297 pub profile_name: Option<String>,
298 pub subtitle: Option<String>,
299 pub picture_url: Option<String>,
300 pub about: Option<String>,
301 pub member_count: u64,
302 pub last_message_preview: Option<String>,
303 pub last_message_at_secs: Option<u64>,
304 pub last_message_is_outgoing: Option<bool>,
305 pub last_message_delivery: Option<DeliveryState>,
306 pub unread_count: u64,
307 pub is_typing: bool,
308 pub is_muted: bool,
309 pub is_pinned: bool,
310 pub draft: String,
315 pub is_request: bool,
322}
323
324#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
325pub struct ChatParticipantSnapshot {
326 pub owner_pubkey_hex: String,
327 pub display_name: String,
328 pub picture_url: Option<String>,
329 pub is_local_owner: bool,
330}
331
332#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
336pub enum DirectChatCapabilityState {
337 Checking,
338 Available,
339 Unavailable,
340 CheckFailed,
341}
342
343#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
344pub struct CurrentChatSnapshot {
345 pub chat_id: String,
346 pub kind: ChatKind,
347 pub display_name: String,
348 pub nickname: Option<String>,
349 pub profile_name: Option<String>,
350 pub subtitle: Option<String>,
351 pub picture_url: Option<String>,
352 pub about: Option<String>,
353 pub group_id: Option<String>,
354 pub member_count: u64,
355 pub message_ttl_seconds: Option<u64>,
356 pub is_muted: bool,
357 pub participants: Vec<ChatParticipantSnapshot>,
358 pub messages: Vec<ChatMessageSnapshot>,
359 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
360 pub draft: String,
363 pub is_request: bool,
366 pub direct_chat_capability: Option<DirectChatCapabilityState>,
367}
368
369#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
370pub struct GroupMemberSnapshot {
371 pub owner_pubkey_hex: String,
372 pub display_name: String,
373 pub npub: String,
374 pub picture_url: Option<String>,
375 pub is_admin: bool,
376 pub is_creator: bool,
377 pub is_local_owner: bool,
378}
379
380#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
381pub struct GroupDetailsSnapshot {
382 pub group_id: String,
383 pub name: String,
384 pub picture_url: Option<String>,
385 pub about: Option<String>,
388 pub created_by_display_name: String,
389 pub created_by_npub: String,
390 pub can_manage: bool,
391 pub is_muted: bool,
392 pub revision: u64,
393 pub members: Vec<GroupMemberSnapshot>,
394}
395
396#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
397pub struct MutualGroupsSnapshot {
398 pub groups: Vec<ChatThreadSnapshot>,
399}
400
401#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
402pub struct RelayConnectionSnapshot {
403 pub url: String,
404 pub status: String,
405}
406
407#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
408pub struct NetworkStatusSnapshot {
409 pub relay_set_id: String,
410 pub relay_urls: Vec<String>,
411 pub relay_connections: Vec<RelayConnectionSnapshot>,
412 pub connected_relay_count: u64,
413 pub all_relays_offline_since_secs: Option<u64>,
414 pub syncing: bool,
415 pub pending_outbound_count: u64,
416 pub pending_group_control_count: u64,
417 pub recent_event_count: u64,
418 pub recent_log_count: u64,
419 pub last_debug_category: Option<String>,
420 pub last_debug_detail: Option<String>,
421}
422
423#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
424pub struct MobilePushSessionSnapshot {
425 pub recipient_pubkey_hex: String,
426 pub display_name: String,
427 pub state_json: String,
428 pub tracked_sender_pubkeys: Vec<String>,
429 pub has_receiving_capability: bool,
430}
431
432#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
433pub struct MobilePushSyncSnapshot {
434 pub owner_pubkey_hex: Option<String>,
435 pub message_author_pubkeys: Vec<String>,
436 pub invite_response_pubkeys: Vec<String>,
437 pub sessions: Vec<MobilePushSessionSnapshot>,
438}
439
440#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
441pub struct PeerProfileDebugSnapshot {
442 pub owner_pubkey_hex: String,
443 pub owner_npub: String,
444 pub direct_send_readiness: String,
445 pub roster_device_count: u64,
446 pub known_device_count: u64,
447 pub active_session_count: u64,
448 pub session_count: u64,
449 pub receiving_session_count: u64,
450 pub tracked_sender_count: u64,
451 pub recent_handshake_device_count: u64,
452 pub last_handshake_at_secs: Option<u64>,
453 pub tracked_for_messages: bool,
454}
455
456#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
457pub struct MobilePushNotificationResolution {
458 pub should_show: bool,
459 pub title: String,
460 pub body: String,
461 pub payload_json: String,
462}
463
464#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
465pub struct MobilePushSubscriptionRequest {
466 pub method: String,
467 pub url: String,
468 pub authorization_header: String,
469 pub body_json: Option<String>,
470}
471
472#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
477pub struct MessageSearchHit {
478 pub chat_id: String,
479 pub message_id: String,
480 pub chat_display_name: String,
481 pub chat_picture_url: Option<String>,
482 pub chat_kind: ChatKind,
483 pub author_pubkey: String,
484 pub body: String,
485 pub is_outgoing: bool,
486 pub created_at_secs: u64,
487}
488
489#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
493pub struct FollowedUserSearchResult {
494 pub owner_pubkey_hex: String,
495 pub display_label: String,
496 pub profile_label: Option<String>,
497 pub picture_url: Option<String>,
498 pub about: Option<String>,
499 pub user_id: String,
500}
501
502#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
503pub struct SearchResultSnapshot {
504 pub query: String,
505 pub scope_chat_id: Option<String>,
506 pub people: Vec<FollowedUserSearchResult>,
507 pub contacts: Vec<ChatThreadSnapshot>,
508 pub groups: Vec<ChatThreadSnapshot>,
509 pub messages: Vec<MessageSearchHit>,
510 pub shortcut: Option<ChatInputShortcut>,
515}
516
517#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
523pub enum ChatInputShortcut {
524 DirectPeer {
527 peer_input: String,
528 display: String,
529 npub: String,
530 pubkey_hex: String,
531 },
532 Invite {
535 invite_input: String,
536 display: String,
537 },
538}
539
540impl SearchResultSnapshot {
541 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
542 Self {
543 query,
544 scope_chat_id,
545 people: Vec::new(),
546 contacts: Vec::new(),
547 groups: Vec::new(),
548 messages: Vec::new(),
549 shortcut: None,
550 }
551 }
552
553 pub fn is_empty(&self) -> bool {
554 self.people.is_empty()
555 && self.contacts.is_empty()
556 && self.groups.is_empty()
557 && self.messages.is_empty()
558 && self.shortcut.is_none()
559 }
560}
561
562#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
563pub struct PublicInviteSnapshot {
564 pub url: String,
565}
566
567#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
568pub struct LinkDeviceSnapshot {
569 pub url: String,
570 pub device_input: String,
571}
572
573#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
574pub struct AppState {
575 pub rev: u64,
576 pub router: Router,
577 pub account: Option<AccountSnapshot>,
578 pub device_roster: Option<DeviceRosterSnapshot>,
579 pub busy: BusyState,
580 pub chat_list: Vec<ChatThreadSnapshot>,
581 pub current_chat: Option<CurrentChatSnapshot>,
582 pub group_details: Option<GroupDetailsSnapshot>,
583 pub public_invite: Option<PublicInviteSnapshot>,
584 pub link_device: Option<LinkDeviceSnapshot>,
585 pub network_status: Option<NetworkStatusSnapshot>,
586 pub mobile_push: MobilePushSyncSnapshot,
587 pub preferences: PreferencesSnapshot,
588 pub user_discovery_revision: u64,
591 pub user_discovery_syncing: bool,
592 pub toast: Option<String>,
593}
594
595impl AppState {
596 pub fn empty() -> Self {
597 Self {
598 rev: 0,
599 router: Router {
600 default_screen: Screen::Welcome,
601 screen_stack: Vec::new(),
602 },
603 account: None,
604 device_roster: None,
605 busy: BusyState::default(),
606 chat_list: Vec::new(),
607 current_chat: None,
608 group_details: None,
609 public_invite: None,
610 link_device: None,
611 network_status: None,
612 mobile_push: MobilePushSyncSnapshot::default(),
613 preferences: PreferencesSnapshot::default(),
614 user_discovery_revision: 0,
615 user_discovery_syncing: false,
616 toast: None,
617 }
618 }
619}