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 AwaitingDeviceApproval,
20 DeviceRevoked,
21}
22
23#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
24pub struct Router {
25 pub default_screen: Screen,
26 pub screen_stack: Vec<Screen>,
27}
28
29#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
30pub struct BusyState {
31 pub creating_account: bool,
32 pub restoring_session: bool,
33 pub linking_device: bool,
34 pub creating_chat: bool,
35 pub creating_group: bool,
36 pub sending_message: bool,
37 pub updating_roster: bool,
38 pub updating_group: bool,
39 pub creating_invite: bool,
40 pub accepting_invite: bool,
41 pub syncing_network: bool,
42 pub uploading_attachment: bool,
43 pub upload_progress: Option<UploadProgress>,
44}
45
46#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
47pub struct UploadProgress {
48 pub bytes_uploaded: u64,
49 pub total_bytes: u64,
50}
51
52#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
53pub struct PreferencesSnapshot {
54 pub send_typing_indicators: bool,
55 pub send_read_receipts: bool,
56 pub desktop_notifications_enabled: bool,
57 pub invite_acceptance_notifications_enabled: bool,
58 pub startup_at_login_enabled: bool,
59 pub nearby_enabled: bool,
60 pub nearby_bluetooth_enabled: bool,
61 pub nearby_lan_enabled: bool,
62 pub nearby_show_in_chat_list: bool,
63 pub nearby_mailbag_enabled: bool,
68 pub nostr_relay_urls: Vec<String>,
69 pub image_proxy_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: true,
99 send_read_receipts: true,
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_url: crate::image_proxy::DEFAULT_IMAGE_PROXY_URL.to_string(),
111 image_proxy_key_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_KEY_HEX.to_string(),
112 image_proxy_salt_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_SALT_HEX.to_string(),
113 muted_chat_ids: Vec::new(),
114 pinned_chat_ids: Vec::new(),
115 blocked_owner_pubkeys: Vec::new(),
116 accepted_owner_pubkeys: Vec::new(),
117 debug_logging_enabled: false,
118 accept_unknown_direct_messages: true,
119 mobile_push_server_url: String::new(),
120 }
121 }
122}
123
124#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
125pub struct OutgoingAttachment {
126 pub file_path: String,
127 pub filename: String,
128}
129
130#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
131pub struct AttachmentDownloadResult {
132 pub data_base64: Option<String>,
133 pub error: Option<String>,
134}
135
136#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
137pub enum DeviceAuthorizationState {
138 Authorized,
139 AwaitingApproval,
140 Revoked,
141}
142
143#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
144pub struct AccountSnapshot {
145 pub public_key_hex: String,
146 pub npub: String,
147 pub display_name: String,
148 pub picture_url: Option<String>,
149 pub about: Option<String>,
150 pub device_public_key_hex: String,
151 pub device_npub: String,
152 pub has_owner_signing_authority: bool,
153 pub authorization_state: DeviceAuthorizationState,
154}
155
156#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
157pub struct DeviceEntrySnapshot {
158 pub device_pubkey_hex: String,
159 pub device_npub: String,
160 pub is_current_device: bool,
161 pub is_authorized: bool,
162 pub is_stale: bool,
163 pub added_at_secs: Option<u64>,
164 pub device_label: Option<String>,
165 pub client_label: Option<String>,
166}
167
168#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
169pub struct DeviceRosterSnapshot {
170 pub owner_public_key_hex: String,
171 pub owner_npub: String,
172 pub current_device_public_key_hex: String,
173 pub current_device_npub: String,
174 pub can_manage_devices: bool,
175 pub authorization_state: DeviceAuthorizationState,
176 pub devices: Vec<DeviceEntrySnapshot>,
177}
178
179#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
180pub enum DeliveryState {
181 Queued,
182 Pending,
183 Sent,
184 Received,
185 Seen,
186 Failed,
187}
188
189#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
190pub enum ChatKind {
191 Direct,
192 Group,
193}
194
195#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
196pub enum ChatMessageKind {
197 User,
198 System,
199}
200
201#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
202pub struct MessageAttachmentSnapshot {
203 pub nhash: String,
204 pub filename: String,
205 pub filename_encoded: String,
206 pub htree_url: String,
207 pub is_image: bool,
208 pub is_video: bool,
209 pub is_audio: bool,
210}
211
212#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
213pub struct MessageReactionSnapshot {
214 pub emoji: String,
215 pub count: u64,
216 pub reacted_by_me: bool,
217}
218
219#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
220pub struct MessageReactor {
221 pub author: String,
223 #[serde(default, skip_serializing_if = "String::is_empty")]
225 pub display_name: String,
226 #[serde(default, skip_serializing_if = "Option::is_none")]
227 pub picture_url: Option<String>,
228 pub emoji: String,
230}
231
232#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
233pub struct MessageRecipientDeliverySnapshot {
234 pub owner_pubkey_hex: String,
236 #[serde(default, skip_serializing_if = "String::is_empty")]
238 pub display_name: String,
239 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub picture_url: Option<String>,
241 pub delivery: DeliveryState,
242 pub updated_at_secs: u64,
243}
244
245#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
246#[serde(default)]
247pub struct MessageDeliveryTraceSnapshot {
248 pub outer_event_ids: Vec<String>,
249 pub pending_relay_event_ids: Vec<String>,
250 pub queued_protocol_targets: Vec<String>,
251 pub transport_channels: Vec<String>,
252 pub last_transport_error: Option<String>,
253}
254
255#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
256pub struct ChatMessageSnapshot {
257 pub id: String,
258 pub chat_id: String,
259 pub kind: ChatMessageKind,
260 pub author: String,
261 pub author_owner_pubkey_hex: Option<String>,
263 pub author_picture_url: Option<String>,
264 pub body: String,
265 pub attachments: Vec<MessageAttachmentSnapshot>,
266 pub reactions: Vec<MessageReactionSnapshot>,
267 pub reactors: Vec<MessageReactor>,
268 pub is_outgoing: bool,
269 pub created_at_secs: u64,
270 pub expires_at_secs: Option<u64>,
271 pub delivery: DeliveryState,
272 pub recipient_deliveries: Vec<MessageRecipientDeliverySnapshot>,
273 pub delivery_trace: MessageDeliveryTraceSnapshot,
274 pub source_event_id: Option<String>,
281}
282
283#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
284pub struct TypingIndicatorSnapshot {
285 pub chat_id: String,
286 pub display_name: String,
287 pub expires_at_secs: u64,
288}
289
290#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
291pub struct ChatThreadSnapshot {
292 pub chat_id: String,
293 pub kind: ChatKind,
294 pub display_name: String,
295 pub nickname: Option<String>,
296 pub profile_name: Option<String>,
297 pub subtitle: Option<String>,
298 pub picture_url: Option<String>,
299 pub about: Option<String>,
300 pub member_count: u64,
301 pub last_message_preview: Option<String>,
302 pub last_message_at_secs: Option<u64>,
303 pub last_message_is_outgoing: Option<bool>,
304 pub last_message_delivery: Option<DeliveryState>,
305 pub unread_count: u64,
306 pub is_typing: bool,
307 pub is_muted: bool,
308 pub is_pinned: bool,
309 pub draft: String,
314 pub is_request: bool,
321}
322
323#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
324pub struct ChatParticipantSnapshot {
325 pub owner_pubkey_hex: String,
326 pub display_name: String,
327 pub picture_url: Option<String>,
328 pub is_local_owner: bool,
329}
330
331#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
332pub struct CurrentChatSnapshot {
333 pub chat_id: String,
334 pub kind: ChatKind,
335 pub display_name: String,
336 pub nickname: Option<String>,
337 pub profile_name: Option<String>,
338 pub subtitle: Option<String>,
339 pub picture_url: Option<String>,
340 pub about: Option<String>,
341 pub group_id: Option<String>,
342 pub member_count: u64,
343 pub message_ttl_seconds: Option<u64>,
344 pub is_muted: bool,
345 pub participants: Vec<ChatParticipantSnapshot>,
346 pub messages: Vec<ChatMessageSnapshot>,
347 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
348 pub draft: String,
351 pub is_request: bool,
354}
355
356#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
357pub struct GroupMemberSnapshot {
358 pub owner_pubkey_hex: String,
359 pub display_name: String,
360 pub npub: String,
361 pub picture_url: Option<String>,
362 pub is_admin: bool,
363 pub is_creator: bool,
364 pub is_local_owner: bool,
365}
366
367#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
368pub struct GroupDetailsSnapshot {
369 pub group_id: String,
370 pub name: String,
371 pub picture_url: Option<String>,
372 pub about: Option<String>,
375 pub created_by_display_name: String,
376 pub created_by_npub: String,
377 pub can_manage: bool,
378 pub is_muted: bool,
379 pub revision: u64,
380 pub members: Vec<GroupMemberSnapshot>,
381}
382
383#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
384pub struct MutualGroupsSnapshot {
385 pub groups: Vec<ChatThreadSnapshot>,
386}
387
388#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
389pub struct RelayConnectionSnapshot {
390 pub url: String,
391 pub status: String,
392}
393
394#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
395pub struct NetworkStatusSnapshot {
396 pub relay_set_id: String,
397 pub relay_urls: Vec<String>,
398 pub relay_connections: Vec<RelayConnectionSnapshot>,
399 pub connected_relay_count: u64,
400 pub all_relays_offline_since_secs: Option<u64>,
401 pub syncing: bool,
402 pub pending_outbound_count: u64,
403 pub pending_group_control_count: u64,
404 pub recent_event_count: u64,
405 pub recent_log_count: u64,
406 pub last_debug_category: Option<String>,
407 pub last_debug_detail: Option<String>,
408}
409
410#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
411pub struct MobilePushSessionSnapshot {
412 pub recipient_pubkey_hex: String,
413 pub display_name: String,
414 pub state_json: String,
415 pub tracked_sender_pubkeys: Vec<String>,
416 pub has_receiving_capability: bool,
417}
418
419#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
420pub struct MobilePushSyncSnapshot {
421 pub owner_pubkey_hex: Option<String>,
422 pub message_author_pubkeys: Vec<String>,
423 pub invite_response_pubkeys: Vec<String>,
424 pub sessions: Vec<MobilePushSessionSnapshot>,
425}
426
427#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
428pub struct PeerProfileDebugSnapshot {
429 pub owner_pubkey_hex: String,
430 pub owner_npub: String,
431 pub roster_device_count: u64,
432 pub known_device_count: u64,
433 pub active_session_count: u64,
434 pub session_count: u64,
435 pub receiving_session_count: u64,
436 pub tracked_sender_count: u64,
437 pub recent_handshake_device_count: u64,
438 pub last_handshake_at_secs: Option<u64>,
439 pub tracked_for_messages: bool,
440}
441
442#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
443pub struct MobilePushNotificationResolution {
444 pub should_show: bool,
445 pub title: String,
446 pub body: String,
447 pub payload_json: String,
448}
449
450#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
451pub struct MobilePushSubscriptionRequest {
452 pub method: String,
453 pub url: String,
454 pub authorization_header: String,
455 pub body_json: Option<String>,
456}
457
458#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
463pub struct MessageSearchHit {
464 pub chat_id: String,
465 pub message_id: String,
466 pub chat_display_name: String,
467 pub chat_picture_url: Option<String>,
468 pub chat_kind: ChatKind,
469 pub author_pubkey: String,
470 pub body: String,
471 pub is_outgoing: bool,
472 pub created_at_secs: u64,
473}
474
475#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
476pub struct SearchResultSnapshot {
477 pub query: String,
478 pub scope_chat_id: Option<String>,
479 pub contacts: Vec<ChatThreadSnapshot>,
480 pub groups: Vec<ChatThreadSnapshot>,
481 pub messages: Vec<MessageSearchHit>,
482 pub shortcut: Option<ChatInputShortcut>,
487}
488
489#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
495pub enum ChatInputShortcut {
496 DirectPeer {
499 peer_input: String,
500 display: String,
501 npub: String,
502 pubkey_hex: String,
503 },
504 Invite {
507 invite_input: String,
508 display: String,
509 },
510}
511
512impl SearchResultSnapshot {
513 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
514 Self {
515 query,
516 scope_chat_id,
517 contacts: Vec::new(),
518 groups: Vec::new(),
519 messages: Vec::new(),
520 shortcut: None,
521 }
522 }
523
524 pub fn is_empty(&self) -> bool {
525 self.contacts.is_empty()
526 && self.groups.is_empty()
527 && self.messages.is_empty()
528 && self.shortcut.is_none()
529 }
530}
531
532#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
533pub struct PublicInviteSnapshot {
534 pub url: String,
535}
536
537#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
538pub struct LinkDeviceSnapshot {
539 pub url: String,
540 pub device_input: String,
541}
542
543#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
544pub struct AppState {
545 pub rev: u64,
546 pub router: Router,
547 pub account: Option<AccountSnapshot>,
548 pub device_roster: Option<DeviceRosterSnapshot>,
549 pub busy: BusyState,
550 pub chat_list: Vec<ChatThreadSnapshot>,
551 pub current_chat: Option<CurrentChatSnapshot>,
552 pub group_details: Option<GroupDetailsSnapshot>,
553 pub public_invite: Option<PublicInviteSnapshot>,
554 pub link_device: Option<LinkDeviceSnapshot>,
555 pub network_status: Option<NetworkStatusSnapshot>,
556 pub mobile_push: MobilePushSyncSnapshot,
557 pub preferences: PreferencesSnapshot,
558 pub toast: Option<String>,
559}
560
561impl AppState {
562 pub fn empty() -> Self {
563 Self {
564 rev: 0,
565 router: Router {
566 default_screen: Screen::Welcome,
567 screen_stack: Vec::new(),
568 },
569 account: None,
570 device_roster: None,
571 busy: BusyState::default(),
572 chat_list: Vec::new(),
573 current_chat: None,
574 group_details: None,
575 public_invite: None,
576 link_device: None,
577 network_status: None,
578 mobile_push: MobilePushSyncSnapshot::default(),
579 preferences: PreferencesSnapshot::default(),
580 toast: None,
581 }
582 }
583}