use crate::state::{SendStatus, ServerId, TemporaryId};
use helix_core::effect::{SqlValue, StorageOp, UpsertSpec};
use helix_core::{Correlation, Effect, EffectSink, TimerId};
use serde_json::Value;
mod persistence;
pub use persistence::{
optimistic_message_persist_op, send_status_persist_op, upload_progress_persist_op,
};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TimelineReadbackContext {
pub window_token: Option<String>,
pub causation_id: Option<String>,
}
pub struct PendingSend {
pub temporary_id: TemporaryId,
pub status: SendStatus,
pub timeout_timer: TimerId,
pub persist_corr: Option<Correlation>,
pub connection_id: Option<String>,
pub timeline_readback: TimelineReadbackContext,
pub body: Option<Value>,
pub http_started: bool,
pub remaining_uploads: usize,
pub upload_failed: bool,
}
impl PendingSend {
pub fn new(
temporary_id: TemporaryId,
timeout_timer: TimerId,
connection_id: Option<String>,
) -> Self {
Self {
temporary_id,
status: SendStatus::Local,
timeout_timer,
persist_corr: None,
connection_id,
timeline_readback: TimelineReadbackContext::default(),
body: None,
http_started: false,
remaining_uploads: 0,
upload_failed: false,
}
}
pub fn reconcile(&mut self, server_id: ServerId, corr: Correlation, fx: &mut EffectSink) {
fx.push(Effect::Persist {
corr,
ops: vec![StorageOp::BatchUpsert(UpsertSpec {
version_column: None,
update_guard: None,
table: "message",
rows: vec![vec![
(
"temporary_id".to_string(),
SqlValue::Text(self.temporary_id.0.clone()),
),
(
"id".to_string(),
SqlValue::Text(server_id.as_str().to_string()),
),
(
"send_status".to_string(),
SqlValue::Text("sent".to_string()),
),
]],
conflict_key: Some("temporary_id"),
exclude_from_update: Vec::new(),
})],
});
fx.push(Effect::CancelTimer {
id: self.timeout_timer,
});
self.status = SendStatus::Sent;
}
pub fn mark_sent_pending_server_id(&mut self, corr: Correlation, fx: &mut EffectSink) {
fx.push(Effect::Persist {
corr,
ops: vec![StorageOp::BatchUpsert(UpsertSpec {
version_column: None,
update_guard: None,
table: "message",
rows: vec![vec![
(
"temporary_id".to_string(),
SqlValue::Text(self.temporary_id.0.clone()),
),
(
"send_status".to_string(),
SqlValue::Text("sent".to_string()),
),
]],
conflict_key: Some("temporary_id"),
exclude_from_update: Vec::new(),
})],
});
fx.push(Effect::CancelTimer {
id: self.timeout_timer,
});
self.status = SendStatus::Sent;
}
pub fn mark_failed_immediately(&mut self, corr: Correlation, fx: &mut EffectSink) -> bool {
if self.status == SendStatus::Sent || self.status == SendStatus::UnSend {
return false;
}
self.status = SendStatus::UnSend;
fx.push(Effect::Persist {
corr,
ops: vec![StorageOp::BatchUpsert(UpsertSpec {
version_column: None,
update_guard: None,
table: "message",
rows: vec![vec![
(
"temporary_id".to_string(),
SqlValue::Text(self.temporary_id.0.clone()),
),
(
"send_status".to_string(),
SqlValue::Text("unsend".to_string()),
),
]],
conflict_key: Some("temporary_id"),
exclude_from_update: Vec::new(),
})],
});
fx.push(Effect::CancelTimer {
id: self.timeout_timer,
});
true
}
pub fn on_timeout(&mut self, fx: &mut EffectSink) {
if self.upload_failed {
return;
}
if self.status == SendStatus::Sent || self.status == SendStatus::UnSend {
return;
}
self.persist_unsend(fx);
}
fn persist_unsend(&mut self, fx: &mut EffectSink) {
self.status = SendStatus::UnSend;
fx.push(Effect::PersistFire {
ops: vec![StorageOp::BatchUpsert(UpsertSpec {
version_column: None,
update_guard: None,
table: "message",
rows: vec![vec![
(
"temporary_id".to_string(),
SqlValue::Text(self.temporary_id.0.clone()),
),
(
"send_status".to_string(),
SqlValue::Text("unsend".to_string()),
),
]],
conflict_key: Some("temporary_id"),
exclude_from_update: Vec::new(),
})],
});
}
}
pub use crate::send::upload_props::props_persist_op;