use serde_json::Value;
use crate::reply::convs::ConvRow;
use crate::reply::stream::Stream;
use crate::reply::transcript::Transcript;
use crate::reply::{Read, Reply};
mod acts;
mod channel;
mod claim;
mod enroll;
mod notice;
mod records;
mod start;
mod tuning;
pub use channel::{Channel, Chunk, Held};
pub use enroll::{Enrolling, Grade, Shown};
pub use notice::Notice;
pub use start::{Phase, Start};
pub use tuning::{Edit, Tuning};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Aim {
pub channel: String,
pub address: String,
}
#[derive(Debug, Clone, Default)]
pub struct Model {
pub roster: Vec<Chunk>,
pub convs: Vec<ConvRow>,
pub answered: Option<Aim>,
pub roles: Option<Vec<crate::reply::roles::RoleRow>>,
pub tuning: Option<Tuning>,
pub records: bool,
pub steps: Option<crate::reply::steps::Steps>,
pub files: Option<crate::reply::files::Files>,
pub transcript: Transcript,
pub live: Option<Stream>,
pub notice: Option<Notice>,
pub aim: Option<Aim>,
pub focus: crate::ui::keys::Pane,
pub reveal: bool,
pub conversation: Option<String>,
pub draft: String,
pub typed: String,
pub start: Option<Start>,
pub enroll: Option<Enrolling>,
pub outbox: Vec<Value>,
}
impl Model {
pub fn revealing(&mut self, pane: crate::ui::keys::Pane) -> bool {
self.focus == pane && std::mem::take(&mut self.reveal)
}
pub fn holds(&self, channel: &str) -> bool {
self.roster
.iter()
.any(|chunk| chunk.channel.name == channel)
}
pub fn absorb(&mut self, channel: &Channel, read: Read) {
match read {
Read::Answer(reply) => self.file(channel, reply),
Read::Refusal(said) => self.notice = Some(Notice::Refused(said)),
Read::Unreadable(why) => self.notice = Some(Notice::Unreadable(why)),
}
}
fn file(&mut self, channel: &Channel, reply: Reply) {
match reply {
Reply::Workspaces(view) => self.seat(channel, view),
Reply::Conversations(rows) => {
self.convs = rows;
self.answered = self.aim.clone();
self.resolve();
}
Reply::Roles(rows) => self.roles = Some(rows),
Reply::Steps(listing) => self.steps = Some(listing),
Reply::Files(answer) => self.files = Some(answer),
Reply::Transcript(transcript) => self.transcript = transcript,
Reply::Follow(stream) => self.live = Some(stream),
Reply::Enrolled(material) => self.enrolled(&material),
Reply::Prepared(prepared) => self.fire(&prepared),
Reply::Started { conversation } => self.started(conversation),
Reply::Nudged => self.notice = None,
Reply::Outcome(outcome) => {
self.notice = (!outcome.ok()).then_some(Notice::Refused(outcome.stderr));
}
}
}
fn seat(&mut self, channel: &Channel, view: crate::reply::roster::Workspaces) {
let seated = Chunk {
channel: channel.clone(),
held: Held::Heard,
stale: view.stale,
growth: view.growth,
walls: view.rows,
};
match self
.roster
.iter_mut()
.find(|chunk| chunk.channel.name == channel.name)
{
Some(held) => *held = seated,
None => self.roster.push(seated),
}
}
pub fn unreachable(&mut self, channel: &Channel, why: String) {
match self
.roster
.iter_mut()
.find(|chunk| chunk.channel.name == channel.name)
{
Some(held) => held.held = Held::Unheld(why),
None => self.notice = Some(Notice::Unreachable(format!("{}: {why}", channel.name))),
}
}
pub fn aimed_at(&self, channel: &str, address: Option<&String>) -> bool {
match (&self.aim, address) {
(Some(aim), Some(address)) => aim.channel == channel && aim.address == *address,
_ => false,
}
}
}
#[cfg(test)]
mod tests;