Hoot struct, event loop architecture, threading model, and how data flows through the system.
The Hoot struct
TheHoot struct (src/main.rs:62) is the main application state container that implements the eframe::App trait.
Structure definition
State management
TheHootState struct (src/types.rs:40) stores UI component states:
- Multiple compose windows tracked by unique IDs
- Multiple add account dialogs
- Per-page state persistence
- Clean separation of concerns
Application status
TheHootStatus enum (src/types.rs:67) tracks initialization state:
Initialization flow
The application initialization follows a strict sequence (src/main.rs:352):1. Application creation
2. Pre-unlock phase
In thePreUnlock state (src/event_processing.rs:42):
3. Database unlock
User enters password in unlock screen (src/ui/unlock_database.rs), then:4. Initialization phase
Once unlocked (src/event_processing.rs:63):5. Ready state
Application is now fully operational and processes events normally.Event loop
Hoot’s event loop follows egui’s immediate-mode pattern with two main functions:update_app()
Processes background events and updates state (src/event_processing.rs:33):render_app()
Renders UI based on current state (src/main.rs:274):Frame timing
Every frame:update_app()processes relay messages, images, NIP-05 verificationsrender_app()draws UI with updated data- egui submits draw commands to GPU
- Application sleeps until next event (wake-up callback or timer)
Threading model
Hoot uses multiple threading strategies for different tasks:Main UI thread
Runs immediate-mode egui with synchronous operations:- UI rendering
- Database queries (rusqlite is synchronous)
- Event processing
- State updates
WebSocket connections
Useewebsock with wake-up callbacks:
- WebSocket thread receives data
- Calls wake-up callback
- egui schedules repaint
- Main thread processes message in next frame
Profile image fetching
Background threads prevent UI blocking (src/image_loader.rs):Gift-wrap operations
Usepollster to block on async Nostr SDK operations:
Data flow
Data flows through Hoot in several key patterns:1. Receiving messages
2. Sending messages
3. Profile metadata loading
4. Gift-wrap subscription
When accounts are loaded (src/main.rs:431):Key methods
refresh_* methods
Update UI data from database:resolve_name()
Resolves display names with fallback priority (src/main.rs:461):Storage locations
Hoot stores data in platform-specific directories:Application data
- Linux:
~/.local/share/systems.chakany.hoot/ - macOS:
~/Library/Application Support/systems.chakany.hoot/ - Windows:
%APPDATA%\systems.chakany.hoot\
Database file
Key storage
Private keys are stored in platform keychains:- macOS: Keychain API via
security-frameworkcrate - Windows: Credential Manager
- Linux: Secret Service API (with file-based fallback)
Error handling
Hoot uses Rust’sResult type with the anyhow crate for flexible error handling:
Performance considerations
Wake-up callbacks
Minimize unnecessary repaints:- WebSocket message received
- Image download completed
- User interaction
- Timer expires (keepalive, reconnect)
Batched operations
Process all available messages in one frame:Lazy loading
Profile metadata and images load on-demand:Next steps
Database schema
Explore the database schema and query patterns
Relay system
Learn about Nostr relay communication and subscriptions