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: false,
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 target_device_ids: 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::Record, Clone, Debug, PartialEq, Eq)]
333pub struct CurrentChatSnapshot {
334 pub chat_id: String,
335 pub kind: ChatKind,
336 pub display_name: String,
337 pub nickname: Option<String>,
338 pub profile_name: Option<String>,
339 pub subtitle: Option<String>,
340 pub picture_url: Option<String>,
341 pub about: Option<String>,
342 pub group_id: Option<String>,
343 pub member_count: u64,
344 pub message_ttl_seconds: Option<u64>,
345 pub is_muted: bool,
346 pub participants: Vec<ChatParticipantSnapshot>,
347 pub messages: Vec<ChatMessageSnapshot>,
348 pub typing_indicators: Vec<TypingIndicatorSnapshot>,
349 pub draft: String,
352 pub is_request: bool,
355}
356
357#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
358pub struct GroupMemberSnapshot {
359 pub owner_pubkey_hex: String,
360 pub display_name: String,
361 pub npub: String,
362 pub picture_url: Option<String>,
363 pub is_admin: bool,
364 pub is_creator: bool,
365 pub is_local_owner: bool,
366}
367
368#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
369pub struct GroupDetailsSnapshot {
370 pub group_id: String,
371 pub name: String,
372 pub picture_url: Option<String>,
373 pub about: Option<String>,
376 pub created_by_display_name: String,
377 pub created_by_npub: String,
378 pub can_manage: bool,
379 pub is_muted: bool,
380 pub revision: u64,
381 pub members: Vec<GroupMemberSnapshot>,
382}
383
384#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
385pub struct MutualGroupsSnapshot {
386 pub groups: Vec<ChatThreadSnapshot>,
387}
388
389#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
390pub struct RelayConnectionSnapshot {
391 pub url: String,
392 pub status: String,
393}
394
395#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
396pub struct NetworkStatusSnapshot {
397 pub relay_set_id: String,
398 pub relay_urls: Vec<String>,
399 pub relay_connections: Vec<RelayConnectionSnapshot>,
400 pub connected_relay_count: u64,
401 pub all_relays_offline_since_secs: Option<u64>,
402 pub syncing: bool,
403 pub pending_outbound_count: u64,
404 pub pending_group_control_count: u64,
405 pub recent_event_count: u64,
406 pub recent_log_count: u64,
407 pub last_debug_category: Option<String>,
408 pub last_debug_detail: Option<String>,
409}
410
411#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
412pub struct MobilePushSessionSnapshot {
413 pub recipient_pubkey_hex: String,
414 pub display_name: String,
415 pub state_json: String,
416 pub tracked_sender_pubkeys: Vec<String>,
417 pub has_receiving_capability: bool,
418}
419
420#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
421pub struct MobilePushSyncSnapshot {
422 pub owner_pubkey_hex: Option<String>,
423 pub message_author_pubkeys: Vec<String>,
424 pub invite_response_pubkeys: Vec<String>,
425 pub sessions: Vec<MobilePushSessionSnapshot>,
426}
427
428#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
429pub struct PeerProfileDebugSnapshot {
430 pub owner_pubkey_hex: String,
431 pub owner_npub: String,
432 pub roster_device_count: u64,
433 pub known_device_count: u64,
434 pub active_session_count: u64,
435 pub session_count: u64,
436 pub receiving_session_count: u64,
437 pub tracked_sender_count: u64,
438 pub recent_handshake_device_count: u64,
439 pub last_handshake_at_secs: Option<u64>,
440 pub tracked_for_messages: bool,
441}
442
443#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
444pub struct MobilePushNotificationResolution {
445 pub should_show: bool,
446 pub title: String,
447 pub body: String,
448 pub payload_json: String,
449}
450
451#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
452pub struct MobilePushSubscriptionRequest {
453 pub method: String,
454 pub url: String,
455 pub authorization_header: String,
456 pub body_json: Option<String>,
457}
458
459#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
464pub struct MessageSearchHit {
465 pub chat_id: String,
466 pub message_id: String,
467 pub chat_display_name: String,
468 pub chat_picture_url: Option<String>,
469 pub chat_kind: ChatKind,
470 pub author_pubkey: String,
471 pub body: String,
472 pub is_outgoing: bool,
473 pub created_at_secs: u64,
474}
475
476#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
477pub struct SearchResultSnapshot {
478 pub query: String,
479 pub scope_chat_id: Option<String>,
480 pub contacts: Vec<ChatThreadSnapshot>,
481 pub groups: Vec<ChatThreadSnapshot>,
482 pub messages: Vec<MessageSearchHit>,
483 pub shortcut: Option<ChatInputShortcut>,
488}
489
490#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
496pub enum ChatInputShortcut {
497 DirectPeer {
500 peer_input: String,
501 display: String,
502 npub: String,
503 pubkey_hex: String,
504 },
505 Invite {
508 invite_input: String,
509 display: String,
510 },
511}
512
513impl SearchResultSnapshot {
514 pub fn empty(query: String, scope_chat_id: Option<String>) -> Self {
515 Self {
516 query,
517 scope_chat_id,
518 contacts: Vec::new(),
519 groups: Vec::new(),
520 messages: Vec::new(),
521 shortcut: None,
522 }
523 }
524
525 pub fn is_empty(&self) -> bool {
526 self.contacts.is_empty()
527 && self.groups.is_empty()
528 && self.messages.is_empty()
529 && self.shortcut.is_none()
530 }
531}
532
533#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
534pub struct PublicInviteSnapshot {
535 pub url: String,
536}
537
538#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
539pub struct LinkDeviceSnapshot {
540 pub url: String,
541 pub device_input: String,
542}
543
544#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
545pub struct AppState {
546 pub rev: u64,
547 pub router: Router,
548 pub account: Option<AccountSnapshot>,
549 pub device_roster: Option<DeviceRosterSnapshot>,
550 pub busy: BusyState,
551 pub chat_list: Vec<ChatThreadSnapshot>,
552 pub current_chat: Option<CurrentChatSnapshot>,
553 pub group_details: Option<GroupDetailsSnapshot>,
554 pub public_invite: Option<PublicInviteSnapshot>,
555 pub link_device: Option<LinkDeviceSnapshot>,
556 pub network_status: Option<NetworkStatusSnapshot>,
557 pub mobile_push: MobilePushSyncSnapshot,
558 pub preferences: PreferencesSnapshot,
559 pub toast: Option<String>,
560}
561
562impl AppState {
563 pub fn empty() -> Self {
564 Self {
565 rev: 0,
566 router: Router {
567 default_screen: Screen::Welcome,
568 screen_stack: Vec::new(),
569 },
570 account: None,
571 device_roster: None,
572 busy: BusyState::default(),
573 chat_list: Vec::new(),
574 current_chat: None,
575 group_details: None,
576 public_invite: None,
577 link_device: None,
578 network_status: None,
579 mobile_push: MobilePushSyncSnapshot::default(),
580 preferences: PreferencesSnapshot::default(),
581 toast: None,
582 }
583 }
584}