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}
161
162#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
163pub struct DeviceRosterSnapshot {
164 pub owner_public_key_hex: String,
165 pub owner_npub: String,
166 pub current_device_public_key_hex: String,
167 pub current_device_npub: String,
168 pub can_manage_devices: bool,
169 pub authorization_state: DeviceAuthorizationState,
170 pub devices: Vec<DeviceEntrySnapshot>,
171}
172
173#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
174pub enum DeliveryState {
175 Queued,
176 Pending,
177 Sent,
178 Received,
179 Seen,
180 Failed,
181}
182
183#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
184pub enum ChatKind {
185 Direct,
186 Group,
187}
188
189#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
190pub enum ChatMessageKind {
191 User,
192 System,
193}
194
195#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
196pub struct MessageAttachmentSnapshot {
197 pub nhash: String,
198 pub filename: String,
199 pub filename_encoded: String,
200 pub htree_url: String,
201 pub is_image: bool,
202 pub is_video: bool,
203 pub is_audio: bool,
204}
205
206#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
207pub struct MessageReactionSnapshot {
208 pub emoji: String,
209 pub count: u64,
210 pub reacted_by_me: bool,
211}
212
213#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
214pub struct MessageReactor {
215 pub author: String,
217 pub emoji: String,
219}
220
221#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
222pub struct MessageRecipientDeliverySnapshot {
223 pub owner_pubkey_hex: String,
225 pub delivery: DeliveryState,
226 pub updated_at_secs: u64,
227}
228
229#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
230#[serde(default)]
231pub struct MessageDeliveryTraceSnapshot {
232 pub outer_event_ids: Vec<String>,
233 pub pending_relay_event_ids: Vec<String>,
234 pub queued_protocol_targets: Vec<String>,
235 pub target_device_ids: Vec<String>,
236 pub transport_channels: Vec<String>,
237 pub last_transport_error: Option<String>,
238}
239
240#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
241pub struct ChatMessageSnapshot {
242 pub id: String,
243 pub chat_id: String,
244 pub kind: ChatMessageKind,
245 pub author: String,
246 pub body: String,
247 pub attachments: Vec<MessageAttachmentSnapshot>,
248 pub reactions: Vec<MessageReactionSnapshot>,
249 pub reactors: Vec<MessageReactor>,
250 pub is_outgoing: bool,
251 pub created_at_secs: u64,
252 pub expires_at_secs: Option<u64>,
253 pub delivery: DeliveryState,
254 pub recipient_deliveries: Vec<MessageRecipientDeliverySnapshot>,
255 pub delivery_trace: MessageDeliveryTraceSnapshot,
256 pub source_event_id: Option<String>,
263}
264
265#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
266pub struct TypingIndicatorSnapshot {
267 pub chat_id: String,
268 pub display_name: String,
269 pub expires_at_secs: u64,
270}
271
272#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
273pub struct ChatThreadSnapshot {
274 pub chat_id: String,
275 pub kind: ChatKind,
276 pub display_name: String,
277 pub subtitle: Option<String>,
278 pub picture_url: Option<String>,
279 pub member_count: u64,
280 pub last_message_preview: Option<String>,
281 pub last_message_at_secs: Option<u64>,
282 pub last_message_is_outgoing: Option<bool>,
283 pub last_message_delivery: Option<DeliveryState>,
284 pub unread_count: u64,
285 pub is_typing: bool,
286 pub is_muted: bool,
287 pub is_pinned: bool,
288 pub draft: String,
293 pub is_request: bool,
300}
301
302#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
303pub struct CurrentChatSnapshot {
304 pub chat_id: String,
305 pub kind: ChatKind,
306 pub display_name: String,
307 pub subtitle: Option<String>,
308 pub picture_url: Option<String>,
309 pub group_id: Option<String>,
310 pub member_count: u64,
311 pub message_ttl_seconds: Option<u64>,
312 pub is_muted: bool,
313 pub messages: Vec<ChatMessageSnapshot>,
314 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
315 pub draft: String,
318 pub is_request: bool,
321}
322
323#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
324pub struct GroupMemberSnapshot {
325 pub owner_pubkey_hex: String,
326 pub display_name: String,
327 pub npub: String,
328 pub is_admin: bool,
329 pub is_creator: bool,
330 pub is_local_owner: bool,
331}
332
333#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
334pub struct GroupDetailsSnapshot {
335 pub group_id: String,
336 pub name: String,
337 pub picture_url: Option<String>,
338 pub created_by_display_name: String,
339 pub created_by_npub: String,
340 pub can_manage: bool,
341 pub is_muted: bool,
342 pub revision: u64,
343 pub members: Vec<GroupMemberSnapshot>,
344}
345
346#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
347pub struct MutualGroupsSnapshot {
348 pub groups: Vec<ChatThreadSnapshot>,
349}
350
351#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
352pub struct RelayConnectionSnapshot {
353 pub url: String,
354 pub status: String,
355}
356
357#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
358pub struct NetworkStatusSnapshot {
359 pub relay_set_id: String,
360 pub relay_urls: Vec<String>,
361 pub relay_connections: Vec<RelayConnectionSnapshot>,
362 pub connected_relay_count: u64,
363 pub all_relays_offline_since_secs: Option<u64>,
364 pub syncing: bool,
365 pub pending_outbound_count: u64,
366 pub pending_group_control_count: u64,
367 pub recent_event_count: u64,
368 pub recent_log_count: u64,
369 pub last_debug_category: Option<String>,
370 pub last_debug_detail: Option<String>,
371}
372
373#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
374pub struct MobilePushSessionSnapshot {
375 pub recipient_pubkey_hex: String,
376 pub display_name: String,
377 pub state_json: String,
378 pub tracked_sender_pubkeys: Vec<String>,
379 pub has_receiving_capability: bool,
380}
381
382#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
383pub struct MobilePushSyncSnapshot {
384 pub owner_pubkey_hex: Option<String>,
385 pub message_author_pubkeys: Vec<String>,
386 pub invite_response_pubkeys: Vec<String>,
387 pub sessions: Vec<MobilePushSessionSnapshot>,
388}
389
390#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
391pub struct PeerProfileDebugSnapshot {
392 pub owner_pubkey_hex: String,
393 pub owner_npub: String,
394 pub roster_device_count: u64,
395 pub known_device_count: u64,
396 pub active_session_count: u64,
397 pub session_count: u64,
398 pub receiving_session_count: u64,
399 pub tracked_sender_count: u64,
400 pub recent_handshake_device_count: u64,
401 pub last_handshake_at_secs: Option<u64>,
402 pub tracked_for_messages: bool,
403}
404
405#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
406pub struct MobilePushNotificationResolution {
407 pub should_show: bool,
408 pub title: String,
409 pub body: String,
410 pub payload_json: String,
411}
412
413#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
414pub struct MobilePushSubscriptionRequest {
415 pub method: String,
416 pub url: String,
417 pub authorization_header: String,
418 pub body_json: Option<String>,
419}
420
421#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
426pub struct MessageSearchHit {
427 pub chat_id: String,
428 pub message_id: String,
429 pub chat_display_name: String,
430 pub chat_picture_url: Option<String>,
431 pub chat_kind: ChatKind,
432 pub author_pubkey: String,
433 pub body: String,
434 pub is_outgoing: bool,
435 pub created_at_secs: u64,
436}
437
438#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
439pub struct SearchResultSnapshot {
440 pub query: String,
441 pub scope_chat_id: Option<String>,
442 pub contacts: Vec<ChatThreadSnapshot>,
443 pub groups: Vec<ChatThreadSnapshot>,
444 pub messages: Vec<MessageSearchHit>,
445 pub shortcut: Option<ChatInputShortcut>,
450}
451
452#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
458pub enum ChatInputShortcut {
459 DirectPeer {
462 peer_input: String,
463 display: String,
464 npub: String,
465 pubkey_hex: String,
466 },
467 Invite {
470 invite_input: String,
471 display: String,
472 },
473}
474
475impl SearchResultSnapshot {
476 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
477 Self {
478 query,
479 scope_chat_id,
480 contacts: Vec::new(),
481 groups: Vec::new(),
482 messages: Vec::new(),
483 shortcut: None,
484 }
485 }
486
487 pub fn is_empty(&self) -> bool {
488 self.contacts.is_empty()
489 && self.groups.is_empty()
490 && self.messages.is_empty()
491 && self.shortcut.is_none()
492 }
493}
494
495#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
496pub struct PublicInviteSnapshot {
497 pub url: String,
498}
499
500#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
501pub struct LinkDeviceSnapshot {
502 pub url: String,
503 pub device_input: String,
504}
505
506#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
507pub struct AppState {
508 pub rev: u64,
509 pub router: Router,
510 pub account: Option<AccountSnapshot>,
511 pub device_roster: Option<DeviceRosterSnapshot>,
512 pub busy: BusyState,
513 pub chat_list: Vec<ChatThreadSnapshot>,
514 pub current_chat: Option<CurrentChatSnapshot>,
515 pub group_details: Option<GroupDetailsSnapshot>,
516 pub public_invite: Option<PublicInviteSnapshot>,
517 pub link_device: Option<LinkDeviceSnapshot>,
518 pub network_status: Option<NetworkStatusSnapshot>,
519 pub mobile_push: MobilePushSyncSnapshot,
520 pub preferences: PreferencesSnapshot,
521 pub toast: Option<String>,
522}
523
524impl AppState {
525 pub fn empty() -> Self {
526 Self {
527 rev: 0,
528 router: Router {
529 default_screen: Screen::Welcome,
530 screen_stack: Vec::new(),
531 },
532 account: None,
533 device_roster: None,
534 busy: BusyState::default(),
535 chat_list: Vec::new(),
536 current_chat: None,
537 group_details: None,
538 public_invite: None,
539 link_device: None,
540 network_status: None,
541 mobile_push: MobilePushSyncSnapshot::default(),
542 preferences: PreferencesSnapshot::default(),
543 toast: None,
544 }
545 }
546}