Skip to main content

iris_chat_core/
state.rs

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}
43
44#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
45pub struct PreferencesSnapshot {
46    pub send_typing_indicators: bool,
47    pub send_read_receipts: bool,
48    pub desktop_notifications_enabled: bool,
49    pub invite_acceptance_notifications_enabled: bool,
50    pub startup_at_login_enabled: bool,
51    pub nearby_bluetooth_enabled: bool,
52    pub nearby_lan_enabled: bool,
53    pub nostr_relay_urls: Vec<String>,
54    pub image_proxy_enabled: bool,
55    pub image_proxy_url: String,
56    pub image_proxy_key_hex: String,
57    pub image_proxy_salt_hex: String,
58    pub muted_chat_ids: Vec<String>,
59    /// User-configurable notification server URL. Empty string means
60    /// "use the platform default" (notifications.iris.to in release,
61    /// notifications-sandbox.iris.to in debug). When non-empty, the
62    /// shells should pass this as the override to
63    /// `build_mobile_push_*_subscription_request`.
64    pub mobile_push_server_url: String,
65}
66
67impl Default for PreferencesSnapshot {
68    fn default() -> Self {
69        Self {
70            send_typing_indicators: false,
71            send_read_receipts: true,
72            desktop_notifications_enabled: true,
73            invite_acceptance_notifications_enabled: true,
74            startup_at_login_enabled: true,
75            nearby_bluetooth_enabled: false,
76            nearby_lan_enabled: false,
77            nostr_relay_urls: crate::core::configured_relays(),
78            image_proxy_enabled: true,
79            image_proxy_url: crate::image_proxy::DEFAULT_IMAGE_PROXY_URL.to_string(),
80            image_proxy_key_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_KEY_HEX.to_string(),
81            image_proxy_salt_hex: crate::image_proxy::DEFAULT_IMAGE_PROXY_SALT_HEX.to_string(),
82            muted_chat_ids: Vec::new(),
83            mobile_push_server_url: String::new(),
84        }
85    }
86}
87
88#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
89pub struct OutgoingAttachment {
90    pub file_path: String,
91    pub filename: String,
92}
93
94#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
95pub struct AttachmentDownloadResult {
96    pub data_base64: Option<String>,
97    pub error: Option<String>,
98}
99
100#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
101pub enum DeviceAuthorizationState {
102    Authorized,
103    AwaitingApproval,
104    Revoked,
105}
106
107#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
108pub struct AccountSnapshot {
109    pub public_key_hex: String,
110    pub npub: String,
111    pub display_name: String,
112    pub picture_url: Option<String>,
113    pub device_public_key_hex: String,
114    pub device_npub: String,
115    pub has_owner_signing_authority: bool,
116    pub authorization_state: DeviceAuthorizationState,
117}
118
119#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
120pub struct DeviceEntrySnapshot {
121    pub device_pubkey_hex: String,
122    pub device_npub: String,
123    pub is_current_device: bool,
124    pub is_authorized: bool,
125    pub is_stale: bool,
126    pub last_activity_secs: Option<u64>,
127}
128
129#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
130pub struct DeviceRosterSnapshot {
131    pub owner_public_key_hex: String,
132    pub owner_npub: String,
133    pub current_device_public_key_hex: String,
134    pub current_device_npub: String,
135    pub can_manage_devices: bool,
136    pub authorization_state: DeviceAuthorizationState,
137    pub devices: Vec<DeviceEntrySnapshot>,
138}
139
140#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
141pub enum DeliveryState {
142    Queued,
143    Pending,
144    Sent,
145    Received,
146    Seen,
147    Failed,
148}
149
150#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)]
151pub enum ChatKind {
152    Direct,
153    Group,
154}
155
156#[derive(uniffi::Enum, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
157pub enum ChatMessageKind {
158    User,
159    System,
160}
161
162#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
163pub struct MessageAttachmentSnapshot {
164    pub nhash: String,
165    pub filename: String,
166    pub filename_encoded: String,
167    pub htree_url: String,
168    pub is_image: bool,
169    pub is_video: bool,
170    pub is_audio: bool,
171}
172
173#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
174pub struct MessageReactionSnapshot {
175    pub emoji: String,
176    pub count: u64,
177    pub reacted_by_me: bool,
178}
179
180#[derive(uniffi::Record, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
181pub struct MessageReactor {
182    /// Hex-encoded pubkey of the user who reacted.
183    pub author: String,
184    /// Emoji content of their current (latest) reaction. Empty means unreacted.
185    pub emoji: String,
186}
187
188#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
189pub struct ChatMessageSnapshot {
190    pub id: String,
191    pub chat_id: String,
192    pub kind: ChatMessageKind,
193    pub author: String,
194    pub body: String,
195    pub attachments: Vec<MessageAttachmentSnapshot>,
196    pub reactions: Vec<MessageReactionSnapshot>,
197    pub reactors: Vec<MessageReactor>,
198    pub is_outgoing: bool,
199    pub created_at_secs: u64,
200    pub expires_at_secs: Option<u64>,
201    pub delivery: DeliveryState,
202    /// Hex ID of the outer relay event that carried this rumor. The
203    /// notification extension joins on this to find a body the
204    /// foreground app already decrypted, so it can render a real
205    /// preview instead of "New activity". `None` for messages that
206    /// didn't come over the wire (system notices, locally-composed
207    /// outgoing rumors).
208    pub source_event_id: Option<String>,
209}
210
211#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
212pub struct TypingIndicatorSnapshot {
213    pub chat_id: String,
214    pub display_name: String,
215    pub expires_at_secs: u64,
216}
217
218#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
219pub struct ChatThreadSnapshot {
220    pub chat_id: String,
221    pub kind: ChatKind,
222    pub display_name: String,
223    pub subtitle: Option<String>,
224    pub picture_url: Option<String>,
225    pub member_count: u64,
226    pub last_message_preview: Option<String>,
227    pub last_message_at_secs: Option<u64>,
228    pub last_message_is_outgoing: Option<bool>,
229    pub last_message_delivery: Option<DeliveryState>,
230    pub unread_count: u64,
231    pub is_typing: bool,
232    pub is_muted: bool,
233}
234
235#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
236pub struct CurrentChatSnapshot {
237    pub chat_id: String,
238    pub kind: ChatKind,
239    pub display_name: String,
240    pub subtitle: Option<String>,
241    pub picture_url: Option<String>,
242    pub group_id: Option<String>,
243    pub member_count: u64,
244    pub message_ttl_seconds: Option<u64>,
245    pub is_muted: bool,
246    pub messages: Vec<ChatMessageSnapshot>,
247    pub typing_indicators: Vec<TypingIndicatorSnapshot>,
248}
249
250#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
251pub struct GroupMemberSnapshot {
252    pub owner_pubkey_hex: String,
253    pub display_name: String,
254    pub npub: String,
255    pub is_admin: bool,
256    pub is_creator: bool,
257    pub is_local_owner: bool,
258}
259
260#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
261pub struct GroupDetailsSnapshot {
262    pub group_id: String,
263    pub name: String,
264    pub picture_url: Option<String>,
265    pub created_by_display_name: String,
266    pub created_by_npub: String,
267    pub can_manage: bool,
268    pub is_muted: bool,
269    pub revision: u64,
270    pub members: Vec<GroupMemberSnapshot>,
271}
272
273#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
274pub struct RelayConnectionSnapshot {
275    pub url: String,
276    pub status: String,
277}
278
279#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
280pub struct NetworkStatusSnapshot {
281    pub relay_set_id: String,
282    pub relay_urls: Vec<String>,
283    pub relay_connections: Vec<RelayConnectionSnapshot>,
284    pub connected_relay_count: u64,
285    pub all_relays_offline_since_secs: Option<u64>,
286    pub syncing: bool,
287    pub pending_outbound_count: u64,
288    pub pending_group_control_count: u64,
289    pub recent_event_count: u64,
290    pub recent_log_count: u64,
291    pub last_debug_category: Option<String>,
292    pub last_debug_detail: Option<String>,
293}
294
295#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
296pub struct MobilePushSessionSnapshot {
297    pub recipient_pubkey_hex: String,
298    pub display_name: String,
299    pub state_json: String,
300    pub tracked_sender_pubkeys: Vec<String>,
301    pub has_receiving_capability: bool,
302}
303
304#[derive(uniffi::Record, Clone, Debug, Default, PartialEq, Eq)]
305pub struct MobilePushSyncSnapshot {
306    pub owner_pubkey_hex: Option<String>,
307    pub message_author_pubkeys: Vec<String>,
308    pub invite_response_pubkeys: Vec<String>,
309    pub sessions: Vec<MobilePushSessionSnapshot>,
310}
311
312#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
313pub struct MobilePushNotificationResolution {
314    pub should_show: bool,
315    pub title: String,
316    pub body: String,
317    pub payload_json: String,
318}
319
320#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
321pub struct MobilePushSubscriptionRequest {
322    pub method: String,
323    pub url: String,
324    pub authorization_header: String,
325    pub body_json: Option<String>,
326}
327
328#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
329pub struct PublicInviteSnapshot {
330    pub url: String,
331}
332
333#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
334pub struct LinkDeviceSnapshot {
335    pub url: String,
336    pub device_input: String,
337}
338
339#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)]
340pub struct AppState {
341    pub rev: u64,
342    pub router: Router,
343    pub account: Option<AccountSnapshot>,
344    pub device_roster: Option<DeviceRosterSnapshot>,
345    pub busy: BusyState,
346    pub chat_list: Vec<ChatThreadSnapshot>,
347    pub current_chat: Option<CurrentChatSnapshot>,
348    pub group_details: Option<GroupDetailsSnapshot>,
349    pub public_invite: Option<PublicInviteSnapshot>,
350    pub link_device: Option<LinkDeviceSnapshot>,
351    pub network_status: Option<NetworkStatusSnapshot>,
352    pub mobile_push: MobilePushSyncSnapshot,
353    pub preferences: PreferencesSnapshot,
354    pub toast: Option<String>,
355}
356
357impl AppState {
358    pub fn empty() -> Self {
359        Self {
360            rev: 0,
361            router: Router {
362                default_screen: Screen::Welcome,
363                screen_stack: Vec::new(),
364            },
365            account: None,
366            device_roster: None,
367            busy: BusyState::default(),
368            chat_list: Vec::new(),
369            current_chat: None,
370            group_details: None,
371            public_invite: None,
372            link_device: None,
373            network_status: None,
374            mobile_push: MobilePushSyncSnapshot::default(),
375            preferences: PreferencesSnapshot::default(),
376            toast: None,
377        }
378    }
379}