Initial reports page at the end

master
Wynd 2025-06-18 01:33:15 +03:00
parent c2435f4c24
commit 46acb8c196
2 changed files with 201 additions and 65 deletions

View File

@ -25,6 +25,44 @@ body {
overflow-x: hidden;
}
#report {
margin: auto;
width: 100%;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
margin-top: 5vh;
> p {
font-size: 2vw;
&.correct {
color: var(--correct);
text-shadow: var(--correct-shadow) 0px 0px 30px;
}
&.wrong {
color: var(--fail);
text-shadow: var(--fail-shadow) 0px 0px 30px;
}
}
> div {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
> button {
width: 50%;
height: 100%;
min-height: 300px;
}
}
}
#counter {
margin: 10px;
font-size: 20px;
@ -99,11 +137,11 @@ form {
text-align: center;
display: flex;
flex-direction: column;
font-size: 24px;
font-size: 1vw;
user-select: none;
h1 {
font-size: 3vw;
font-size: 2vw;
text-wrap: balance;
&.correct {
@ -119,9 +157,9 @@ form {
> div {
width: 100%;
height: 80px;
height: 10vh;
display: flex;
margin-top: 10px;
margin-top: 3vh;
align-items: center;
align-self: center;
box-sizing: border-box;
@ -143,8 +181,10 @@ form {
flex-grow: 1;
}
}
}
> input[type="submit"] {
button,
input[type="submit"] {
margin-top: 40px;
height: 70px;
background-color: transparent;
@ -156,10 +196,17 @@ form {
outline: none;
}
}
}
@media only screen and (max-width: 540px) {
#report {
> p {
font-size: 6vw;
}
}
form {
font-size: 4vw;
h1 {
font-size: 5vw;
}
@ -172,12 +219,6 @@ form {
@media (hover: hover) {
form {
> input[type="submit"]:hover {
font-size: 48px;
color: var(--selection);
cursor: pointer;
}
> div {
&:hover:not(.selected) {
border: solid 1px var(--hover);
@ -189,4 +230,11 @@ form {
}
}
}
button:hover,
input[type="submit"]:hover {
font-size: 48px;
color: var(--selection);
cursor: pointer;
}
}

View File

@ -4,7 +4,7 @@ use std::{sync::OnceLock, time::Duration};
use async_std::task::sleep;
#[cfg(feature = "desktop")]
use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
use dioxus::prelude::*;
use dioxus::{logger::tracing, prelude::*};
#[cfg(feature = "bundled")]
use include_dir::{Dir, include_dir};
use models::{Question, Questions};
@ -118,22 +118,60 @@ fn main() {
fn App() -> Element {
let questions = get_rand_questions();
let correct_questions = use_signal(|| 0);
let wrong_questions = use_signal(|| 0);
let has_finished = use_signal(|| false);
let has_restart = use_signal(|| false);
rsx! {
document::Link { rel: "stylesheet", href: MAIN_CSS }
if !questions.is_empty() {
QuestionForm { questions }
p { "{has_restart}" }
if !questions.is_empty() && !has_finished() {
QuestionForm { questions, correct_questions, wrong_questions, has_finished, has_restart }
}
else if has_finished() {
Report {
questions,
correct_questions: correct_questions(),
wrong_questions: wrong_questions(),
has_restart,
}
}
}
}
#[allow(clippy::redundant_closure)]
#[component]
pub fn QuestionForm(questions: Vec<Question>) -> Element {
pub fn QuestionForm(
questions: Vec<Question>,
correct_questions: Signal<i32>,
wrong_questions: Signal<i32>,
has_finished: Signal<bool>,
has_restart: Signal<bool>,
) -> Element {
let mut questions = use_signal(|| questions);
let mut current = use_signal(move || questions.remove(0));
let mut current = use_signal(move || questions().remove(0));
// use_effect(move || {
// let mut has_restart_lock = has_restart.write();
// tracing::info!("called new questions effect {}", *has_restart_lock);
// if *has_restart_lock {
// tracing::info!("actually passing effect");
// let mut questions_lock = questions.write();
//
// *questions_lock = get_rand_questions();
// current.set(questions_lock.remove(0));
//
// correct_questions.set(0);
// wrong_questions.set(0);
// *has_restart_lock = false;
// has_finished.set(false);
// }
// });
let total_correct = use_memo(move || {
current
.read()
current()
.answers
.iter()
.filter(|a| a.is_correct.unwrap_or_default())
@ -155,12 +193,6 @@ pub fn QuestionForm(questions: Vec<Question>) -> Element {
correctly_checked.saturating_sub(incorrectly_checked)
});
let total_questions = get_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 correct_animation = use_signal(|| false);
let mut wrong_animation = use_signal(|| false);
@ -173,15 +205,21 @@ pub fn QuestionForm(questions: Vec<Question>) -> Element {
if is_correct || is_wrong {
sleep(Duration::from_millis(600)).await;
// Uncheck all the answers
current().answers.iter_mut().for_each(|a| a.checked = false);
let mut current_lock = current.write();
let mut correct_questions_lock = correct_questions.write();
let mut wrong_questions_lock = wrong_questions.write();
let mut questions_lock = questions.write();
let mut has_finished_lock = has_finished.write();
// Uncheck all the answers
current_lock
.answers
.iter_mut()
.for_each(|a| a.checked = false);
// Get a new question or reroll the list
if !questions().is_empty() {
current.set(questions.remove(0));
if !questions_lock.is_empty() {
*current_lock = questions_lock.remove(0);
// Update the correct/wrong counters
if is_correct {
@ -189,12 +227,10 @@ pub fn QuestionForm(questions: Vec<Question>) -> Element {
} else if is_wrong {
*wrong_questions_lock += 1;
}
} else {
questions.set(get_rand_questions());
current.set(questions.remove(0));
*correct_questions_lock = 0;
*wrong_questions_lock = 0;
if questions_lock.is_empty() {
*has_finished_lock = true;
}
}
// Reset animation flags
@ -215,20 +251,8 @@ pub fn QuestionForm(questions: Vec<Question>) -> Element {
});
rsx! {
ResultPopup { is_correct: correct_animation, is_wrong: wrong_animation },
div {
id: "counter",
div { "{left_questions}" }
div {
class: "correct",
"{correct_questions}"
}
div {
class: "wrong",
"{wrong_questions}"
}
div { "{total_questions}" }
},
Result { is_correct: correct_animation(), is_wrong: wrong_animation() },
Info { questions, correct_questions, wrong_questions },
form {
id: "form",
onsubmit: move |_| {
@ -303,20 +327,84 @@ pub fn AnswerCheckbox(current: Signal<Question>, id: usize) -> Element {
#[allow(clippy::redundant_closure)]
#[component]
pub fn ResultPopup(is_correct: Signal<bool>, is_wrong: Signal<bool>) -> 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());
pub fn Result(is_correct: bool, is_wrong: bool) -> Element {
let is_visible = is_correct || is_wrong;
rsx! {
div {
id: "result",
class: if is_visible() { "visible" },
class: if is_visible { "visible" },
div {
class: if is_correct() { "correct" },
class: if is_wrong() { "wrong" },
if is_correct() { "" } else { "X" }
class: if is_correct { "correct" },
class: if is_wrong { "wrong" },
if is_correct { "" } else { "X" }
}
}
}
}
#[allow(clippy::redundant_closure)]
#[component]
pub fn Info(
questions: Signal<Vec<Question>>,
correct_questions: Signal<i32>,
wrong_questions: Signal<i32>,
) -> Element {
let total_questions = get_questions().len();
let current_question = use_memo(move || questions().len());
let left_questions = use_memo(move || total_questions - current_question());
rsx! {
div {
id: "counter",
div { "{left_questions}" }
div {
class: "correct",
"{correct_questions}"
}
div {
class: "wrong",
"{wrong_questions}"
}
div { "{total_questions}" }
}
}
}
#[component]
pub fn Report(
questions: Vec<Question>,
correct_questions: i32,
wrong_questions: i32,
has_restart: Signal<bool>,
) -> Element {
let questions_size = questions.len();
rsx! {
div {
id: "report",
p { "Questions: {questions_size}" },
p {
class: "correct",
"Correct: {correct_questions}"
},
p {
class: "wrong",
"Wrong: {wrong_questions}"
},
div {
button {
// onclick: move |_| {
// *has_restart.write() = true;
// },
"Restart"
},
button {
"Restart with only wrong questions"
}
}
}
}