use crate::actions::AppAction;
use crate::state::{
AccountSnapshot, AppState, ChatKind, ChatMessageKind, ChatMessageSnapshot, ChatThreadSnapshot,
CurrentChatSnapshot, DeliveryState, DeviceAuthorizationState, DeviceEntrySnapshot,
DeviceRosterSnapshot, GroupDetailsSnapshot, GroupMemberSnapshot, LinkDeviceSnapshot,
MessageAttachmentSnapshot, MessageDeliveryTraceSnapshot, MessageReactionSnapshot,
MessageReactor, MessageRecipientDeliverySnapshot, MobilePushNotificationResolution,
MobilePushSubscriptionRequest, MobilePushSyncSnapshot, NetworkStatusSnapshot,
OutgoingAttachment, PreferencesSnapshot, PublicInviteSnapshot, RelayConnectionSnapshot, Router,
Screen, TypingIndicatorSnapshot,
};
use crate::updates::{AppUpdate, CoreMsg, InternalEvent};
use flume::Sender;
use nostr::{EventBuilder, UnsignedEvent};
use nostr_double_ratchet::{
add_group_admin, add_group_member, apply_app_keys_snapshot_with_required_device,
apply_metadata_update, build_direct_message_backfill_filter, is_app_keys_event,
parse_group_metadata, remove_group_admin, remove_group_member, update_group_data,
validate_metadata_creation, validate_metadata_update, AppKeys, CreateGroupOptions, DeviceEntry,
DirectMessageSubscriptionTracker, FanoutGroupMetadataOptions, GroupData, GroupDecryptedEvent,
GroupSendEvent, GroupUpdate, Invite, MetadataValidation, NdrProtocolBackfillOptions,
NdrRuntime, SendOptions, SessionManagerEvent, SessionState, StorageAdapter,
APP_KEYS_EVENT_KIND, CHAT_MESSAGE_KIND, CHAT_SETTINGS_KIND, GROUP_METADATA_KIND,
GROUP_SENDER_KEY_DISTRIBUTION_KIND, INVITE_EVENT_KIND, INVITE_RESPONSE_KIND,
MESSAGE_EVENT_KIND, REACTION_KIND, RECEIPT_KIND, TYPING_KIND,
};
use nostr_sdk::prelude::{
Client, ClientMessage, Event, Filter, Keys, Kind, PublicKey, RelayNotification,
RelayPoolNotification, RelayStatus, RelayUrl, SubscriptionId, Timestamp, ToBech32,
};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::time::{sleep, Duration};
mod account;
mod attachment_upload;
mod attachments;
mod chat_reactions;
mod chat_receipts;
mod chat_settings;
mod chat_typing;
mod chats;
mod config;
mod groups;
mod identity;
mod invites;
mod lifecycle;
mod message_expiry;
mod mobile_push;
mod model;
mod nearby;
mod payloads;
mod persistence;
mod profile;
mod profile_helpers;
mod projection;
mod protocol;
mod protocol_filters;
mod publish_helpers;
mod publishing;
mod relay;
mod routing;
mod storage;
mod support;
#[cfg(test)]
mod tests;
pub(crate) const NEARBY_PRESENCE_KIND: u16 = 22242;
type OwnerPubkey = PublicKey;
type DevicePubkey = PublicKey;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub(super) struct UnixSeconds(u64);
impl UnixSeconds {
pub(super) fn get(self) -> u64 {
self.0
}
}
#[cfg(test)]
use account::known_app_keys_from_ndr;
use account::known_app_keys_to_ndr;
use attachment_upload::{
display_filename, upload_file_to_hashtree, upload_profile_picture_to_hashtree,
};
use attachments::*;
use config::*;
pub(crate) use config::{build_summary, configured_relays, relay_set_id, trusted_test_build_flag};
use identity::*;
pub(crate) use identity::{normalize_peer_input_for_display, parse_peer_input};
pub(crate) use mobile_push::{
build_mobile_push_create_subscription_request, build_mobile_push_delete_subscription_request,
build_mobile_push_list_subscriptions_request, build_mobile_push_update_subscription_request,
decrypt_mobile_push_notification, mobile_push_stored_subscription_id_key,
resolve_mobile_push_notification, resolve_mobile_push_server_url,
};
pub(crate) use model::ProtocolSubscriptionPlan;
use model::*;
use payloads::*;
use profile_helpers::*;
use protocol_filters::*;
use publish_helpers::*;
use storage::{
import_legacy_ndr_storage, open_database, AppStore, DataDirLock, SqliteStorageAdapter,
};
pub struct AppCore {
update_tx: Sender<AppUpdate>,
core_sender: Sender<CoreMsg>,
shared_state: Arc<RwLock<AppState>>,
runtime: tokio::runtime::Runtime,
data_dir: PathBuf,
state: AppState,
logged_in: Option<LoggedInState>,
pending_linked_device: Option<PendingLinkedDeviceState>,
threads: BTreeMap<String, ThreadRecord>,
active_chat_id: Option<String>,
screen_stack: Vec<Screen>,
next_message_id: u64,
owner_profiles: BTreeMap<String, OwnerProfileRecord>,
app_keys: BTreeMap<String, KnownAppKeys>,
groups: BTreeMap<String, GroupData>,
typing_indicators: BTreeMap<String, TypingIndicatorRecord>,
typing_floor_secs: BTreeMap<String, u64>,
chat_message_ttl_seconds: BTreeMap<String, u64>,
preferences: PreferencesSnapshot,
recent_handshake_peers: BTreeMap<String, RecentHandshakePeer>,
seen_event_ids: HashSet<String>,
seen_event_order: VecDeque<String>,
device_invite_poll_token: u64,
message_expiry_token: u64,
protocol_reconnect_token: u64,
defer_owner_app_keys_publish: bool,
protocol_subscription_runtime: ProtocolSubscriptionRuntime,
direct_message_subscriptions: DirectMessageSubscriptionTracker,
relay_status_watch_urls: HashSet<String>,
relay_connected_count: u64,
all_relays_offline_since_secs: Option<u64>,
pending_relay_publishes: BTreeMap<String, PendingRelayPublish>,
pending_relay_publish_inflight: HashSet<String>,
event_transport_channels: BTreeMap<String, String>,
pending_mobile_push_events: VecDeque<Event>,
debug_log: VecDeque<DebugLogEntry>,
debug_event_counters: DebugEventCounters,
batch_depth: u32,
batch_dirty_state: bool,
batch_dirty_persist: bool,
setup_user_done: HashSet<String>,
last_emitted_state: Option<AppState>,
app_store: AppStore,
_data_dir_lock: DataDirLock,
cached_mobile_push: MobilePushSyncSnapshot,
mobile_push_dirty: bool,
}
async fn connect_client_with_timeout(client: &Client, timeout: Duration) {
client.connect().await;
client.wait_for_connection(timeout).await;
}