1
0
mirror of https://github.com/pcvolkmer/arsnova-client.git synced 2025-04-19 19:16:51 +00:00

feat: add method to send feedback to service

This commit is contained in:
Paul-Christian Volkmer 2023-12-22 04:57:21 +01:00
parent a9b0e2f49a
commit 168717a78b
3 changed files with 154 additions and 39 deletions

View File

@ -64,6 +64,8 @@ async fn main() -> Result<(), ()> {
let (tx, rx) = channel::<Feedback>(10); let (tx, rx) = channel::<Feedback>(10);
let (fb_tx, fb_rx) = channel::<u8>(10);
let _ = tx let _ = tx
.clone() .clone()
.send(client.get_feedback(&cli.room).await.unwrap()) .send(client.get_feedback(&cli.room).await.unwrap())
@ -81,25 +83,43 @@ async fn main() -> Result<(), ()> {
let l2 = create_ui(&mut terminal, &title, rx); let l2 = create_ui(&mut terminal, &title, rx);
let l3 = tokio::spawn(async { let l3 = tokio::spawn(async move {
loop { loop {
if event::poll(std::time::Duration::from_millis(16)) if event::poll(std::time::Duration::from_millis(16))
.map_err(|_| ()) .map_err(|_| ())
.is_ok() .is_ok()
{ {
if let event::Event::Key(key) = event::read().map_err(|_| ()).unwrap() { if let event::Event::Key(key) = event::read().map_err(|_| ()).unwrap() {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Esc { if key.kind == KeyEventKind::Press {
break; match key.code {
KeyCode::Esc => break,
KeyCode::Char('a') | KeyCode::Char('1') => {
let _ = fb_tx.send(0).await;
}
KeyCode::Char('b') | KeyCode::Char('2') => {
let _ = fb_tx.send(1).await;
}
KeyCode::Char('c') | KeyCode::Char('3') => {
let _ = fb_tx.send(2).await;
}
KeyCode::Char('d') | KeyCode::Char('4') => {
let _ = fb_tx.send(3).await;
}
_ => {}
};
} }
} }
} }
} }
}); });
let l4 = client.register_feedback_receiver(&cli.room, fb_rx);
select! { select! {
_ = l1 => {}, _ = l1 => {},
_ = l2 => {}, _ = l2 => {},
_ = l3 => {}, _ = l3 => {},
_ = l4 => {}
} }
let _ = stdout().execute(LeaveAlternateScreen).map_err(|_| ()); let _ = stdout().execute(LeaveAlternateScreen).map_err(|_| ());
@ -113,7 +133,10 @@ async fn create_ui(
title: &str, title: &str,
mut rx: Receiver<Feedback>, mut rx: Receiver<Feedback>,
) -> Result<(), ()> { ) -> Result<(), ()> {
fn feedback_paragraph(feedback: &Feedback, idx: usize, width: usize) -> Paragraph<'static> { const ICONS: [&str; 4] = ["Super", "Gut", "Nicht so gut", "Schlecht"];
let feedback_paragraph =
|feedback: &Feedback, idx: usize, width: usize| -> Paragraph<'static> {
let value = match idx { let value = match idx {
0 => feedback.very_good, 0 => feedback.very_good,
1 => feedback.good, 1 => feedback.good,
@ -122,11 +145,16 @@ async fn create_ui(
_ => 0, _ => 0,
}; };
let icons = ICONS
.iter()
.map(|icon| format!("{: <12}", icon))
.collect::<Vec<_>>();
let icon = match idx { let icon = match idx {
0 => "Super ", 0 => &icons[0],
1 => "Gut ", 1 => &icons[1],
2 => "Nicht so gut", 2 => &icons[2],
3 => "Schlecht ", 3 => &icons[3],
_ => " ", _ => " ",
}; };
@ -145,7 +173,7 @@ async fn create_ui(
])), ])),
_ => Paragraph::default(), _ => Paragraph::default(),
} }
} };
loop { loop {
let feedback = match rx.recv().await { let feedback = match rx.recv().await {
@ -156,9 +184,11 @@ async fn create_ui(
let _ = terminal.draw(|frame| { let _ = terminal.draw(|frame| {
let layout = Layout::default() let layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(vec![ .constraints([
Constraint::Max(1), Constraint::Max(1),
Constraint::Max(6), Constraint::Max(6),
Constraint::Max(2),
Constraint::Max(1),
Constraint::Min(1), Constraint::Min(1),
Constraint::Max(1), Constraint::Max(1),
]) ])
@ -185,12 +215,12 @@ async fn create_ui(
Paragraph::new("Beenden mit <Esc>") Paragraph::new("Beenden mit <Esc>")
.on_blue() .on_blue()
.alignment(Alignment::Left), .alignment(Alignment::Left),
layout[3], layout[5],
); );
let feedback_layout = Layout::default() let feedback_layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(vec![ .constraints([
Constraint::Max(1), Constraint::Max(1),
Constraint::Max(1), Constraint::Max(1),
Constraint::Max(1), Constraint::Max(1),
@ -205,6 +235,31 @@ async fn create_ui(
feedback_layout[idx], feedback_layout[idx],
) )
}); });
let button_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Min(0),
])
.split(layout[3]);
ICONS.iter().enumerate().for_each(|(idx, label)| {
frame.render_widget(
Paragraph::new(Line::from(vec![
Span::raw(format!(" {} ", idx + 1))
.white()
.on_magenta()
.bold(),
Span::raw(format!("{: ^14}", label)).white().on_black(),
]))
.alignment(Alignment::Center),
button_layout[idx],
)
});
}); });
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -29,7 +29,7 @@ use reqwest::{IntoUrl, StatusCode};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use tokio::join; use tokio::join;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::{Receiver, Sender};
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
use url::Url; use url::Url;
@ -361,6 +361,66 @@ impl Client<LoggedIn> {
} }
} }
/// Register feedback channel receiver and send incoming feedback to service
///
/// This method fails on connection or response errors and if
/// no room is available with given room ID.
pub async fn register_feedback_receiver(
&self,
short_id: &str,
mut receiver: Receiver<u8>,
) -> Result<(), ClientError> {
let room_info = self.get_room_info(short_id).await?;
let ws_url = self.api_url.replace("http", "ws");
let (socket, _) = connect_async(Url::parse(&format!("{}/ws/websocket", ws_url)).unwrap())
.await
.map_err(|_| ConnectionError)?;
let (mut write, _) = socket.split();
let user_id = self.get_user_id().unwrap_or_default();
if write
.send(Message::Text(
WsConnectMessage::new(self.token.as_ref().unwrap()).to_string(),
))
.await
.is_ok()
{
return match write
.send(Message::Text(
WsSubscribeMessage::new(&room_info.id).to_string(),
))
.await
{
Ok(_) => loop {
if let Some(value) = receiver.recv().await {
let payload = json!({
"type": "CreateFeedback",
"payload": {
"roomId": room_info.id,
"userId": user_id,
"value": value
}
})
.to_string();
let _ = write
.send(Message::Text(format!(
"SEND\ndestination:/queue/feedback.command\ncontent-type:application/json\ncontent-length:{}\n\n{}\0",
payload.chars().count(),
payload,
))).await;
};
},
Err(_) => Err(ConnectionError),
};
}
Err(ConnectionError)
}
/// Registers a handler to get notifications on feedback change. /// Registers a handler to get notifications on feedback change.
/// ///
/// This is done by using websocket connections to ARSnova. /// This is done by using websocket connections to ARSnova.