WIP: feat(tui): update state model from server messages
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful

This commit is contained in:
Paul Campbell 2024-08-12 21:25:24 +01:00
parent 622e144986
commit 3c4b3db812
7 changed files with 84 additions and 9 deletions

View file

@ -12,7 +12,7 @@ keywords = { workspace = true }
categories = { workspace = true } categories = { workspace = true }
[features] [features]
default = ["forgejo", "github"] default = ["forgejo", "github", "tui"]
forgejo = ["git-next-forge-forgejo"] forgejo = ["git-next-forge-forgejo"]
github = ["git-next-forge-github"] github = ["git-next-forge-github"]
tui = ["ratatui"] tui = ["ratatui"]

View file

@ -69,9 +69,9 @@ impl Handler<ReceiveValidAppConfig> for ServerActor {
WebhookActor::new(socket_address, webhook_router.recipient()).start(); WebhookActor::new(socket_address, webhook_router.recipient()).start();
self.webhook_actor_addr.replace(webhook_actor_addr); self.webhook_actor_addr.replace(webhook_actor_addr);
let shout = app_config.shout().clone(); let shout = app_config.shout().clone();
self.app_config.replace(app_config); self.app_config.replace(app_config.clone());
self.alerts.do_send(UpdateShout::new(shout)); self.alerts.do_send(UpdateShout::new(shout));
self.send_server_updates(); self.send_server_updates(app_config.into());
} }
} }

View file

@ -40,6 +40,8 @@ message!(Shutdown, "Notification to shutdown the server actor");
#[derive(Clone, Debug, PartialEq, Eq, Message)] #[derive(Clone, Debug, PartialEq, Eq, Message)]
#[rtype(result = "()")] #[rtype(result = "()")]
pub enum ServerUpdate { pub enum ServerUpdate {
/// List of all configured forges and aliases
ForgeRepoList { list: Vec<(ForgeAlias, RepoAlias)> },
/// Status of a repo /// Status of a repo
UpdateRepoSummary { UpdateRepoSummary {
forge_alias: ForgeAlias, forge_alias: ForgeAlias,
@ -56,6 +58,22 @@ pub enum ServerUpdate {
Ping, Ping,
} }
impl From<AppConfig> for ServerUpdate {
fn from(app_config: AppConfig) -> Self {
Self::ForgeRepoList {
list: app_config
.forges()
.flat_map(|(forge_alias, forge_config)| {
forge_config
.repos()
.map(|(repo_alias, _server_repo_config)| (forge_alias.clone(), repo_alias))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(),
}
}
}
message!( message!(
SubscribeToUpdates, SubscribeToUpdates,
Recipient<ServerUpdate>, Recipient<ServerUpdate>,

View file

@ -243,9 +243,9 @@ impl ServerActor {
} }
} }
fn send_server_updates(&self) { fn send_server_updates(&self, server_update: ServerUpdate) {
self.subscribers.iter().for_each(|subscriber| { self.subscribers.iter().for_each(move |subscriber| {
subscriber.do_send(ServerUpdate::Ping); subscriber.do_send(server_update.clone());
}); });
} }
} }

View file

@ -2,7 +2,13 @@ use std::time::Instant;
use actix::Handler; use actix::Handler;
use crate::{server::actor::messages::ServerUpdate, tui::Tui}; use crate::{
server::actor::messages::ServerUpdate,
tui::{
actor::model::{ForgeRepoKey, RepoState, State},
Tui,
},
};
// //
impl Handler<ServerUpdate> for Tui { impl Handler<ServerUpdate> for Tui {
@ -10,16 +16,33 @@ impl Handler<ServerUpdate> for Tui {
fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result {
match msg { match msg {
ServerUpdate::ForgeRepoList { list } => {
self.state = State::new(
list.into_iter()
.map(|(forge_alias, repo_alias)| ForgeRepoKey::new(forge_alias, repo_alias))
.map(|key| (key, RepoState::Loading))
.collect(),
);
}
ServerUpdate::UpdateRepoSummary { ServerUpdate::UpdateRepoSummary {
forge_alias, forge_alias,
repo_alias, repo_alias,
branches, branches,
log, log,
} => todo!(), } => {
self.state.repos().insert(
ForgeRepoKey::new(forge_alias, repo_alias),
RepoState::Loaded { branches, log },
);
}
ServerUpdate::RemoveRepo { ServerUpdate::RemoveRepo {
forge_alias, forge_alias,
repo_alias, repo_alias,
} => todo!(), } => {
self.state
.repos()
.remove(&ForgeRepoKey::new(forge_alias, repo_alias));
}
ServerUpdate::Ping => { ServerUpdate::Ping => {
self.last_ping = Instant::now(); self.last_ping = Instant::now();
} }

View file

@ -1,6 +1,7 @@
// //
mod handlers; mod handlers;
pub mod messages; pub mod messages;
mod model;
use std::{ use std::{
io::{stderr, Stderr}, io::{stderr, Stderr},
@ -10,6 +11,7 @@ use std::{
use actix::{Actor, Context}; use actix::{Actor, Context};
use model::State;
use ratatui::{ use ratatui::{
crossterm::{ crossterm::{
execute, execute,
@ -24,6 +26,7 @@ pub struct Tui {
terminal: Option<Terminal<CrosstermBackend<Stderr>>>, terminal: Option<Terminal<CrosstermBackend<Stderr>>>,
signal_shutdown: Sender<()>, signal_shutdown: Sender<()>,
last_ping: Instant, last_ping: Instant,
state: State,
} }
impl Actor for Tui { impl Actor for Tui {
type Context = Context<Self>; type Context = Context<Self>;
@ -59,6 +62,7 @@ impl Tui {
terminal: None, terminal: None,
signal_shutdown, signal_shutdown,
last_ping: Instant::now(), last_ping: Instant::now(),
state: State::default(),
} }
} }
} }

View file

@ -0,0 +1,30 @@
//
use git_next_core::{git::graph::Log, ForgeAlias, RepoAlias, RepoBranches};
use std::collections::BTreeMap;
use derive_more::derive::Constructor;
#[derive(Clone, Debug, PartialEq, Eq, Default, Constructor)]
pub struct State {
repos: ReposState,
}
impl State {
pub fn repos(&mut self) -> &mut ReposState {
&mut self.repos
}
}
pub type ReposState = BTreeMap<ForgeRepoKey, RepoState>;
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
pub struct ForgeRepoKey {
forge_alias: ForgeAlias,
repo_alias: RepoAlias,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RepoState {
Loading,
Loaded { branches: RepoBranches, log: Log },
}