add pings, reconnects, and a next reconnect status in settings menu

This commit is contained in:
Jack Chakany 2024-11-20 12:00:35 -05:00
parent db78b2a4d3
commit 8f31087662
4 changed files with 73 additions and 13 deletions

View file

@ -93,13 +93,13 @@ enum HootStatus {
fn update_app(app: &mut Hoot, ctx: &egui::Context) {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let ctx = ctx.clone();
let wake_up = move || {
ctx.request_repaint();
};
if app.status == HootStatus::Initalizing {
info!("Initalizing Hoot...");
let ctx = ctx.clone();
let wake_up = move || {
ctx.request_repaint();
};
match app.account_manager.load_keys() {
Ok(..) => {}
Err(v) => error!("something went wrong trying to load keys: {}", v),
@ -109,7 +109,7 @@ fn update_app(app: &mut Hoot, ctx: &egui::Context) {
.add_url("wss://relay.damus.io".to_string(), wake_up.clone());
let _ = app
.relays
.add_url("wss://relay-dev.hoot.sh".to_string(), wake_up);
.add_url("wss://relay-dev.hoot.sh".to_string(), wake_up.clone());
if app.account_manager.loaded_keys.len() > 0 {
let mut pks: Vec<nostr::PublicKey> = Vec::new();
@ -129,6 +129,8 @@ fn update_app(app: &mut Hoot, ctx: &egui::Context) {
info!("Hoot Ready");
}
app.relays.keepalive(wake_up);
let new_val = app.relays.try_recv();
if new_val.is_some() {
info!("{:?}", new_val.clone());

View file

@ -3,7 +3,7 @@ use ewebsock::{WsEvent, WsMessage};
use tracing::{debug, error, info};
mod pool;
pub use pool::RelayPool;
pub use pool::{RelayPool, RELAY_RECONNECT_SECONDS};
mod message;
pub use message::{ClientMessage, RelayMessage};
@ -42,11 +42,20 @@ impl Relay {
status: RelayStatus::Connecting,
};
relay.ping();
relay
}
// TODO: investigate whether this can cause a message to be dropped due to the writer being
// overwritten
pub fn reconnect(&mut self, wake_up: impl Fn() + Send + Sync + 'static) {
let (sender, reciever) =
ewebsock::connect_with_wakeup(self.url.clone(), ewebsock::Options::default(), wake_up)
.unwrap();
self.reader = reciever;
self.writer = sender;
}
pub fn send(&mut self, message: WsMessage) -> Result<()> {
if self.status != RelayStatus::Connected {
return Err(Error::RelayNotConnected);
@ -67,6 +76,7 @@ impl Relay {
}
Error(ref error) => {
error!("error in websocket connection to {}: {}", self.url, error);
self.status = RelayStatus::Disconnected;
}
Closed => {
info!("connection to {} closed", self.url);

View file

@ -4,11 +4,16 @@ use crate::relay::Subscription;
use crate::relay::{Relay, RelayStatus};
use ewebsock::{WsEvent, WsMessage};
use std::collections::HashMap;
use tracing::error;
use tracing::{error, debug};
use std::time::{Instant, Duration};
pub const RELAY_RECONNECT_SECONDS: u64 = 5;
pub struct RelayPool {
pub relays: HashMap<String, Relay>,
pub subscriptions: HashMap<String, Subscription>,
last_reconnect_attempt: Instant,
last_ping: Instant,
}
impl RelayPool {
@ -16,6 +21,37 @@ impl RelayPool {
Self {
relays: HashMap::new(),
subscriptions: HashMap::new(),
last_reconnect_attempt: Instant::now(),
last_ping: Instant::now(),
}
}
pub fn get_last_reconnect_attempt(&mut self) -> Instant {
return self.last_reconnect_attempt;
}
pub fn keepalive(&mut self, wake_up: impl Fn() + Send + Sync + Clone + 'static) {
let now = Instant::now();
// Check disconnected relays
if now.duration_since(self.last_reconnect_attempt) >= Duration::from_secs(RELAY_RECONNECT_SECONDS) {
for relay in self.relays.values_mut() {
if relay.status != RelayStatus::Connected {
relay.status = RelayStatus::Connecting;
relay.reconnect(wake_up.clone());
}
}
self.last_reconnect_attempt = now;
}
// Ping connected relays
if now.duration_since(self.last_ping) >= Duration::from_secs(30) {
for relay in self.relays.values_mut() {
if relay.status == RelayStatus::Connected {
relay.ping();
}
}
self.last_ping = now;
}
}
@ -53,11 +89,12 @@ impl RelayPool {
pub fn try_recv(&mut self) -> Option<String> {
for relay in self.relays.values_mut() {
let relay_url = relay.url.clone();
if let Some(event) = relay.try_recv() {
use WsEvent::*;
match event {
Message(message) => {
return self.handle_message(message);
return self.handle_message(relay_url, message);
}
Opened => {
for sub in self.subscriptions.clone() {
@ -91,7 +128,7 @@ impl RelayPool {
None
}
fn handle_message(&mut self, message: WsMessage) -> Option<String> {
fn handle_message(&mut self, url: String, message: WsMessage) -> Option<String> {
use WsMessage::*;
match message {
Text(txt) => {
@ -100,13 +137,16 @@ impl RelayPool {
Binary(..) => {
error!("recived binary messsage, your move semisol");
}
Ping(d) => {
let pong_msg = WsMessage::Pong(d);
Ping(m) => {
let pong_msg = WsMessage::Pong(m);
match self.send(pong_msg) {
Ok(_) => {}
Err(e) => error!("error when sending websocket message {:?}", e),
}
}
Pong(m) => {
debug!("pong recieved from {} after approx {} seconds", &url, self.last_ping.elapsed().as_secs());
}
_ => {
// who cares
}

View file

@ -86,6 +86,7 @@ impl SettingsScreen {
ui.label("Your Relays:");
ui.vertical(|ui| {
let mut relay_to_remove: Option<String> = None;
let last_ping = app.relays.get_last_reconnect_attempt();
for (url, relay) in app.relays.relays.iter() {
ui.horizontal(|ui| {
use crate::relay::RelayStatus::*;
@ -103,6 +104,13 @@ impl SettingsScreen {
painter.circle_filled(c, r, conn_fill);
ui.label(url);
// TODO: this only updates when next frame is rendered, which can be more than
// a few seconds between renders. Make it so it updates every second.
if relay.status == crate::relay::RelayStatus::Disconnected {
let next_ping = crate::relay::RELAY_RECONNECT_SECONDS - last_ping.elapsed().as_secs();
ui.label(format!("(Attempting reconnect in {} seconds)", next_ping));
}
if ui.button("Remove Relay").clicked() {
relay_to_remove = Some(url.to_string());
}