main: switch the app to use a message sink for UI messages, even though this can be blocking
This commit is contained in:
+31
-27
@@ -84,19 +84,22 @@ struct StageEventArgs {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct App {
|
struct App {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
|
end_time: DateTime<Utc>,
|
||||||
|
|
||||||
next_reply_options: Vec<PossibleResponse>,
|
next_reply_options: Vec<PossibleResponse>,
|
||||||
reply_state: ListState,
|
reply_state: ListState,
|
||||||
conversation_state: ListState,
|
conversation_state: ListState,
|
||||||
user_input: Input,
|
user_input: Input,
|
||||||
end_time: DateTime<Utc>,
|
|
||||||
throbber_state: ThrobberState,
|
throbber_state: ThrobberState,
|
||||||
prediction_request_sink: watch::Sender<Scene>,
|
|
||||||
is_requesting: bool,
|
is_requesting: bool,
|
||||||
audio_level: f64,
|
audio_level: f64,
|
||||||
recording_audio: bool,
|
recording_audio: bool,
|
||||||
audio_control_sink: watch::Sender<AudioRecordRequest>,
|
|
||||||
focus_state: FocusState,
|
focus_state: FocusState,
|
||||||
tts_request_sink: mpsc::Sender<String>
|
|
||||||
|
audio_control_sink: watch::Sender<AudioRecordRequest>,
|
||||||
|
prediction_request_sink: watch::Sender<Scene>,
|
||||||
|
tts_request_sink: mpsc::Sender<String>,
|
||||||
|
sys_message_sink: mpsc::Sender<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -106,7 +109,7 @@ enum FocusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn new(prediction_request_sink: watch::Sender<Scene>, audio_control_sink: watch::Sender<AudioRecordRequest>, tts_request_sink: mpsc::Sender<String>) -> Self {
|
fn new(prediction_request_sink: watch::Sender<Scene>, audio_control_sink: watch::Sender<AudioRecordRequest>, tts_request_sink: mpsc::Sender<String>, sys_message_sink: mpsc::Sender<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scene: Scene::default(),
|
scene: Scene::default(),
|
||||||
next_reply_options: Vec::new(),
|
next_reply_options: Vec::new(),
|
||||||
@@ -121,7 +124,8 @@ impl App {
|
|||||||
recording_audio: false,
|
recording_audio: false,
|
||||||
audio_control_sink,
|
audio_control_sink,
|
||||||
focus_state: FocusState::UserInput,
|
focus_state: FocusState::UserInput,
|
||||||
tts_request_sink
|
tts_request_sink,
|
||||||
|
sys_message_sink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,65 +359,65 @@ impl App {
|
|||||||
match command {
|
match command {
|
||||||
"/bandcamp" => {
|
"/bandcamp" => {
|
||||||
self.add_bandcamp_artifact(arg).await;
|
self.add_bandcamp_artifact(arg).await;
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage(format!("Added Bandcamp artifact from {}", arg)));
|
self.sys_message_sink.send(format!("Added Bandcamp artifact from {}", arg)).await.unwrap();
|
||||||
self.scene.insert_conversation(ConversationEntry::ShipComputer(format!("Incoming transmission.")));
|
self.scene.insert_conversation(ConversationEntry::ShipComputer(format!("Incoming transmission.")));
|
||||||
},
|
},
|
||||||
"/episode" => {
|
"/episode" => {
|
||||||
if let Ok(episode_number) = arg.trim().parse::<u32>() {
|
if let Ok(episode_number) = arg.trim().parse::<u32>() {
|
||||||
self.scene.direction.episode_number = episode_number;
|
self.scene.direction.episode_number = episode_number;
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage(format!("Updated episode number: {}", self.scene.direction.episode_number)));
|
self.sys_message_sink.send(format!("Updated episode number: {}", self.scene.direction.episode_number)).await.unwrap();
|
||||||
self.reload_mixxx_playlist();
|
self.reload_mixxx_playlist();
|
||||||
} else {
|
} else {
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Invalid episode number format. Use /episode [number]".into()));
|
self.sys_message_sink.send("Invalid episode number format. Use /episode [number]".into()).await.unwrap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/reload" => {
|
"/reload" => {
|
||||||
self.load();
|
self.load().await;
|
||||||
self.reload_mixxx_playlist();
|
self.reload_mixxx_playlist();
|
||||||
},
|
},
|
||||||
"/timer" => {
|
"/timer" => {
|
||||||
if let Ok(minutes) = arg.trim().parse::<i64>() {
|
if let Ok(minutes) = arg.trim().parse::<i64>() {
|
||||||
self.end_time = Utc::now() + Duration::minutes(minutes);
|
self.end_time = Utc::now() + Duration::minutes(minutes);
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage(format!("Set timer for {} minutes.", minutes)));
|
self.sys_message_sink.send(format!("Set timer for {} minutes.", minutes)).await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Invalid timer format. Use /timer [minutes]".into()));
|
self.sys_message_sink.send("Invalid timer format. Use /timer [minutes]".into()).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"/clear" => {
|
"/clear" => {
|
||||||
match arg.trim() {
|
match arg.trim() {
|
||||||
"playlist" => {
|
"playlist" => {
|
||||||
self.scene.direction.current_playlist.clear();
|
self.scene.direction.current_playlist.clear();
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Cleared current playlist.".into()));
|
self.sys_message_sink.send("Cleared current playlist.".into()).await.unwrap();
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
"artifacts" => {
|
"artifacts" => {
|
||||||
self.scene.direction.artifacts.clear();
|
self.scene.direction.artifacts.clear();
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Cleared artifacts.".into()));
|
self.sys_message_sink.send("Cleared artifacts.".into()).await.unwrap();
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
"all" => {
|
"all" => {
|
||||||
self.scene = Scene::default();
|
self.scene = Scene::default();
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Cleared all data.".into()));
|
self.sys_message_sink.send("Cleared all data.".into()).await.unwrap();
|
||||||
},
|
},
|
||||||
"conversation" => {
|
"conversation" => {
|
||||||
self.scene.conversation.clear();
|
self.scene.conversation.clear();
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Cleared conversation.".into()));
|
self.sys_message_sink.send("Cleared conversation.".into()).await.unwrap();
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Unknown clear command. Use /clear [playlist|artifacts|all]".into()));
|
self.sys_message_sink.send("Unknown clear command. Use /clear [playlist|artifacts|all]".into()).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
"/narrative" => {
|
"/narrative" => {
|
||||||
self.scene.direction.narrative = arg.to_string();
|
self.scene.direction.narrative = arg.to_string();
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage(format!("Updated stage direction: {}", self.scene.direction.narrative)));
|
self.sys_message_sink.send(format!("Updated stage direction: {}", self.scene.direction.narrative)).await.unwrap();
|
||||||
},
|
},
|
||||||
"/event" => {
|
"/event" => {
|
||||||
self.scene.insert_conversation(ConversationEntry::StageDirection(arg.to_string()));
|
self.scene.insert_conversation(ConversationEntry::StageDirection(arg.to_string()));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Unknown command. Available commands: /bandcamp [url], /episode [number], /narrative [text], /reset".into()));
|
self.sys_message_sink.send("Unknown command. Available commands: /bandcamp [url], /episode [number], /narrative [text], /reset".into()).await.unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,15 +449,15 @@ impl App {
|
|||||||
std::fs::write("save.json", save_data).unwrap();
|
std::fs::write("save.json", save_data).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&mut self) {
|
async fn load(&mut self) {
|
||||||
if let Ok(save_data) = std::fs::read_to_string("save.json") {
|
if let Ok(save_data) = std::fs::read_to_string("save.json") {
|
||||||
if let Ok(scene) = serde_json::from_str(&save_data) {
|
if let Ok(scene) = serde_json::from_str(&save_data) {
|
||||||
self.scene = scene;
|
self.scene = scene;
|
||||||
// FIXME: These should get wiped out when we save as well, or even better, be completely excluded via a custom serde implementation.
|
// FIXME: These should get wiped out when we save as well, or even better, be completely excluded via a custom serde implementation.
|
||||||
self.scene.conversation.retain(|line| { if let ConversationEntry::SystemMessage(_) = line { false } else { true }});
|
self.scene.conversation.retain(|line| { if let ConversationEntry::SystemMessage(_) = line { false } else { true }});
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Loaded stored session.".into()));
|
self.sys_message_sink.send("Loaded stored session.".into()).await.unwrap();
|
||||||
} else {
|
} else {
|
||||||
self.scene.insert_conversation(ConversationEntry::SystemMessage("Failed to load saved session!".into()));
|
self.sys_message_sink.send("Failed to load saved session!".into()).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,13 +538,13 @@ async fn main() {
|
|||||||
|
|
||||||
let mut terminal: Terminal<CrosstermBackend<std::io::Stdout>> = ratatui::init();
|
let mut terminal: Terminal<CrosstermBackend<std::io::Stdout>> = ratatui::init();
|
||||||
|
|
||||||
let (sys_message_sender, mut sys_message_receiver) = tokio::sync::mpsc::channel(5);
|
let (sys_message_sink, mut sys_message_src) = tokio::sync::mpsc::channel(32);
|
||||||
let tts_request_sender = start_tts().await;
|
let tts_request_sender = start_tts().await;
|
||||||
let (prediction_request_in, mut prediction_out) = prediction::start_prediction().await;
|
let (prediction_request_in, mut prediction_out) = prediction::start_prediction().await;
|
||||||
let (mut audio_state_receiver, audio_control_in, mut transcription_out) = transcription::start_transcription(sys_message_sender).await;
|
let (mut audio_state_receiver, audio_control_in, mut transcription_out) = transcription::start_transcription(sys_message_sink.clone()).await;
|
||||||
|
|
||||||
let mut app = App::new(prediction_request_in, audio_control_in, tts_request_sender);
|
let mut app = App::new(prediction_request_in, audio_control_in, tts_request_sender, sys_message_sink);
|
||||||
app.load();
|
app.load().await;
|
||||||
|
|
||||||
let mut events = EventStream::new();
|
let mut events = EventStream::new();
|
||||||
let mut last_tick = Instant::now();
|
let mut last_tick = Instant::now();
|
||||||
@@ -564,7 +568,7 @@ async fn main() {
|
|||||||
_ = audio_state_receiver.changed() => {
|
_ = audio_state_receiver.changed() => {
|
||||||
app.audio_level = *audio_state_receiver.borrow_and_update();
|
app.audio_level = *audio_state_receiver.borrow_and_update();
|
||||||
},
|
},
|
||||||
maybe_message = sys_message_receiver.recv() => {
|
maybe_message = sys_message_src.recv() => {
|
||||||
if let Some(message) = maybe_message {
|
if let Some(message) = maybe_message {
|
||||||
app.scene.insert_conversation(ConversationEntry::SystemMessage(message));
|
app.scene.insert_conversation(ConversationEntry::SystemMessage(message));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user