fix device

This commit is contained in:
Daniel Bulant 2026-05-26 17:28:52 +02:00
parent 3b7d669a5d
commit f055fc7c0f
No known key found for this signature in database
3 changed files with 108 additions and 24 deletions

View file

@ -246,7 +246,7 @@ function handleApiMessage(e: MessageEvent) {
return;
}
console.log("API recv", message.type);
console.log("API recv", message);
if (message.type !== "device_event") return;
const socket = sockets.get(message.deviceId);
if (!socket) {
@ -274,6 +274,7 @@ function handleApiMessage(e: MessageEvent) {
if (event.type === "party_status") {
const quizData = event.party?.data ?? null;
console.log("API quizData", quizData);
if (!quizData) return;
if (quizData.status === "results") {
writeProxyOutput(socket, "Results");

View file

@ -38,26 +38,26 @@ pub struct QuestionData {
}
#[derive(Deserialize)]
pub struct QuestionDataNet<'a> {
pub text: &'a str,
pub struct QuestionDataNet {
pub text: String,
pub q_type: QuestionType,
pub points: i32,
pub index: usize,
}
#[derive(Deserialize)]
pub enum ProxyOutput<'a> {
ConnectPrompt(&'a str),
WaitingForParty(&'a str),
Question(QuestionDataNet<'a>),
pub enum ProxyOutput {
ConnectPrompt(String),
WaitingForParty(String),
Question(QuestionDataNet),
Results,
Error(&'a str),
Error(String),
}
impl<'a> From<QuestionDataNet<'a>> for QuestionData {
fn from(value: QuestionDataNet<'a>) -> Self {
impl From<QuestionDataNet> for QuestionData {
fn from(value: QuestionDataNet) -> Self {
QuestionData {
text: OwnedStr::from_str(value.text).unwrap(),
text: OwnedStr::from_str(value.text.as_str()).unwrap(),
q_type: value.q_type,
points: value.points,
index: value.index,
@ -135,7 +135,7 @@ impl DeviceState {
&mut self.wheel
}
pub fn apply_proxy_output(&mut self, data: ProxyOutput<'_>) {
pub fn apply_proxy_output(&mut self, data: ProxyOutput) {
match data {
ProxyOutput::ConnectPrompt(device_id) => {
let mut owned_device_id = OwnedStr::new();
@ -161,11 +161,32 @@ impl DeviceState {
}
ProxyOutput::Question(data) => {
let data: QuestionData = data.into();
let mut future_wheel = WheelData::empty();
if let QuestionType::Numeric { min, max } = data.q_type {
future_wheel.max = max;
future_wheel.min = min;
future_wheel.value = (min + max) / 2;
let same_question = self
.question
.as_ref()
.is_some_and(|question| question.index == data.index);
let mut future_wheel = if same_question {
self.wheel
} else {
WheelData::empty()
};
match data.q_type {
QuestionType::Numeric { min, max } => {
future_wheel.max = max;
future_wheel.min = min;
if same_question {
future_wheel.value = future_wheel.value.clamp(min, max);
} else {
future_wheel.value = (min + max) / 2;
}
}
QuestionType::Choice => {
future_wheel = WheelData::empty();
}
}
if !same_question {
self.last_index = data.index;
self.title_offset = 0;
}
self.question = Some(data);
self.view = ViewState::Question;
@ -235,10 +256,7 @@ impl DeviceState {
break;
}
}
return Some((
display_name,
OwnedStr::from_str("Awaiting party").unwrap(),
));
return Some((display_name, OwnedStr::from_str("Awaiting party").unwrap()));
}
if self.view == ViewState::Results {
@ -255,7 +273,10 @@ impl DeviceState {
let question = self.question.as_ref()?;
let title_line = if question.text.len() > 16 {
// overscroll, should show spaces after the end
self.title_offset %= question.text.len() - 31;
let scroll_len = question.text.len().saturating_sub(15);
if scroll_len > 0 {
self.title_offset %= scroll_len;
}
let end = usize::min(self.title_offset + 16, question.text.len());
OwnedStr::from_str(&question.text[self.title_offset..end]).unwrap()
} else {
@ -306,8 +327,8 @@ pub enum WriteType<'a> {
DeviceId(&'a str),
}
pub fn parse_proxy_output<'a>(input: &'a str) -> Result<ProxyOutput<'a>, serde_json::Error> {
serde_json::from_str::<ProxyOutput<'a>>(input)
pub fn parse_proxy_output(input: &str) -> Result<ProxyOutput, serde_json::Error> {
serde_json::from_str::<ProxyOutput>(input)
}
pub fn serialize_write(data: &WriteType<'_>) -> Result<String, serde_json::Error> {
@ -453,6 +474,67 @@ mod tests {
assert!(state.question().is_none());
}
#[test]
fn parses_numeric_question_with_escaped_quotes() {
let data = parse_proxy_output(
r#"{"Question":{"text":"How many players have \"Porter Robinson\" as a favourite artist?","points":10,"index":2,"q_type":{"Numeric":{"min":0,"max":2}}}}"#,
)
.unwrap();
let mut state = DeviceState::new();
state.apply_proxy_output(data);
let question = state.question().unwrap();
assert_eq!(question.index, 2);
assert_eq!(
question.text.as_str(),
"How many players have \"Porter Robinson\" as a favourite artist?",
);
assert_eq!(state.wheel().value, 1);
}
#[test]
fn preserves_numeric_wheel_for_same_question_updates() {
let mut state = DeviceState::new();
state.apply_proxy_output(
parse_proxy_output(
r#"{"Question":{"text":"Q","points":10,"index":2,"q_type":{"Numeric":{"min":0,"max":10}}}}"#,
)
.unwrap(),
);
state.wheel_mut().value = 7;
state.apply_proxy_output(
parse_proxy_output(
r#"{"Question":{"text":"Q updated","points":10,"index":2,"q_type":{"Numeric":{"min":0,"max":10}}}}"#,
)
.unwrap(),
);
assert_eq!(state.wheel().value, 7);
}
#[test]
fn resets_numeric_wheel_for_new_questions() {
let mut state = DeviceState::new();
state.apply_proxy_output(
parse_proxy_output(
r#"{"Question":{"text":"Q","points":10,"index":2,"q_type":{"Numeric":{"min":0,"max":10}}}}"#,
)
.unwrap(),
);
state.wheel_mut().value = 7;
state.apply_proxy_output(
parse_proxy_output(
r#"{"Question":{"text":"Next","points":10,"index":3,"q_type":{"Numeric":{"min":0,"max":10}}}}"#,
)
.unwrap(),
);
assert_eq!(state.wheel().value, 5);
}
#[test]
fn parses_and_renders_waiting_for_party() {
let data = parse_proxy_output(r#"{"WaitingForParty":"User"}"#).unwrap();

View file

@ -89,6 +89,7 @@ pub async fn tcp_read_loop(
};
if let Some(last) = str.lines().last() {
let Ok(data) = device_state::parse_proxy_output(last) else {
println!("parse proxy output failed: {}", last);
continue;
};
let mut state = STATE.lock().await;