diff --git a/assets/main.css b/assets/main.css index c5cf270..7f745a7 100644 --- a/assets/main.css +++ b/assets/main.css @@ -1,20 +1,47 @@ :root { + --text: white; + --correct: limegreen; --correct-shadow: green; + + --fail: crimson; + --fail-shadow: red; + --hover: gray; --selection: goldenrod; + --selection-shadow: gold; } body { background-color: #0f1116; - color: #ffffff; + color: var(--text); font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; margin: 0px; + overflow-x: hidden; } #counter { margin: 10px; font-size: 20px; + + > div { + display: inline-block; + + &.correct { + color: var(--correct); + text-shadow: var(--correct-shadow) 0px 0px 30px; + } + + &.wrong { + color: var(--fail); + text-shadow: var(--fail-shadow) 0px 0px 30px; + } + + &:not(:last-child)::after { + color: var(--text); + content: "/"; + } + } } #result { @@ -25,8 +52,6 @@ body { margin: auto; text-align: center; line-height: 1; - color: var(--correct); - text-shadow: var(--correct-shadow) 1px 1px 30px; opacity: 0; user-select: none; @@ -34,6 +59,16 @@ body { display: block; animation: 200ms fade-in forwards; } + + &.correct { + color: var(--correct); + text-shadow: var(--correct-shadow) 0px 0px 30px; + } + + &.wrong { + color: var(--fail); + text-shadow: var(--fail-shadow) 0px 0px 30px; + } } @keyframes fade-in { @@ -56,8 +91,16 @@ form { user-select: none; h1 { - &.success { + text-wrap: balance; + + &.correct { color: var(--correct); + text-shadow: var(--correct-shadow) 0px 0px 30px; + } + + &.wrong { + color: var(--fail); + text-shadow: var(--fail-shadow) 0px 0px 30px; } } @@ -70,9 +113,11 @@ form { align-self: center; box-sizing: border-box; border: solid 2px unset; + text-wrap: balance; &.selected { border: solid 2px var(--selection); + box-shadow: 0px 0px 13px -5px var(--selection-shadow); } &:hover:not(.selected) { diff --git a/src/main.rs b/src/main.rs index c85acc8..6b5fef1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,22 +85,47 @@ pub fn QuestionForm() -> Element { let total_questions = QUESTIONS.len(); let current_question = use_memo(move || questions().len()); let left_questions = use_memo(move || total_questions - current_question()); + let mut correct_questions = use_signal(|| 0); + let mut wrong_questions = use_signal(|| 0); - let mut success_animation = use_signal(|| false); + let mut correct_animation = use_signal(|| false); + let mut wrong_animation = use_signal(|| false); use_future(move || async move { loop { tokio::time::sleep(Duration::from_millis(400)).await; - if success_animation() { + let is_correct = correct_animation(); + let is_wrong = wrong_animation(); + if is_correct || is_wrong { tokio::time::sleep(Duration::from_millis(600)).await; - success_animation.set(false); + + // Uncheck all the answers current().answers.iter_mut().for_each(|a| a.checked = false); + + let mut correct_questions_lock = correct_questions.write(); + let mut wrong_questions_lock = wrong_questions.write(); + + // Get a new question or reroll the list if !questions().is_empty() { current.set(questions.remove(0)); + + // Update the correct/wrong counters + if is_correct { + *correct_questions_lock += 1; + } else if is_wrong { + *wrong_questions_lock += 1; + } } else { questions.set(get_questions()); current.set(questions.remove(0)); + + *correct_questions_lock = 0; + *wrong_questions_lock = 0; } + + // Reset animation flags + correct_animation.set(false); + wrong_animation.set(false); } } }); @@ -116,24 +141,34 @@ pub fn QuestionForm() -> Element { }); rsx! { - ResultPopup { is_visible: success_animation }, + ResultPopup { is_correct: correct_animation, is_wrong: wrong_animation }, div { id: "counter", - "{left_questions}/{total_questions}" + div { "{left_questions}" } + div { + class: "correct", + "{correct_questions}" + } + div { + class: "wrong", + "{wrong_questions}" + } + div { "{total_questions}" } }, form { id: "form", onsubmit: move |_| { if actual_correct() == total_correct() { - success_animation.set(true); + correct_animation.set(true); } else { - + wrong_animation.set(true); } }, h1 { - class: if success_animation() { "success" }, + class: if correct_animation() { "correct" }, + class: if wrong_animation() { "wrong" }, "{current().message}" } { answer_buttons } @@ -192,14 +227,18 @@ pub fn AnswerCheckbox(current: Signal, id: usize) -> Element { } #[component] -pub fn ResultPopup(is_visible: Signal) -> Element { - let is_visible = use_memo(move || is_visible()); +pub fn ResultPopup(is_correct: Signal, is_wrong: Signal) -> Element { + let is_correct = use_memo(move || is_correct()); + let is_wrong = use_memo(move || is_wrong()); + let is_visible = use_memo(move || is_correct() || is_wrong()); rsx! { div { id: "result", class: if is_visible() { "visible" }, - "✓" + class: if is_correct() { "correct" }, + class: if is_wrong() { "wrong" }, + if is_correct() { "✓" } else { "X" } } } }