Compare commits

...

14 Commits

6 changed files with 557 additions and 205 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
*~
/dist
# Added by cargo

169
Cargo.lock generated
View File

@ -31,6 +31,95 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "futures"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -57,6 +146,18 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gloo-timers"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -95,7 +196,10 @@ dependencies = [
name = "kikikoz"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.7",
"gloo-timers",
"js-sys",
"rand",
"sycamore",
"web-sys",
@ -122,6 +226,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "once_cell"
version = "1.13.0"
@ -134,6 +244,18 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -199,6 +321,15 @@ dependencies = [
"rand_core",
]
[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]]
name = "slotmap"
version = "1.0.6"
@ -221,14 +352,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3fad8d7500e6f00f7415f3f3b4d7c465b9edce7eaa9f2d725ced0f99fae3c2"
dependencies = [
"ahash",
"futures",
"indexmap",
"js-sys",
"paste",
"sycamore-core",
"sycamore-futures",
"sycamore-macro",
"sycamore-reactive",
"sycamore-web",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
@ -242,6 +376,18 @@ dependencies = [
"sycamore-reactive",
]
[[package]]
name = "sycamore-futures"
version = "0.8.0-beta.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faebade534e638217448ebc6b4cc7a52471ce055a04090e9e628e783bf629e4e"
dependencies = [
"futures",
"sycamore-reactive",
"tokio",
"wasm-bindgen-futures",
]
[[package]]
name = "sycamore-macro"
version = "0.8.0-beta.7"
@ -294,6 +440,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e"
dependencies = [
"autocfg",
"once_cell",
"pin-project-lite",
]
[[package]]
name = "unicode-ident"
version = "1.0.2"
@ -349,6 +506,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.81"

View File

@ -6,12 +6,19 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sycamore = "0.8.0-beta.7"
sycamore = { version = "0.8.0-beta.7", features = ["suspense"]}
rand = {version = "0.7", features = ["wasm-bindgen"]}
getrandom = { version = "0.2", features = ["js"] }
gloo-timers = { version = "0.2.3", features = ["futures"] }
js-sys = {version = "0.3"}
futures = {version = "0.3"}
[dependencies.web-sys]
version = "0.3"
features = [
'KeyboardEvent',
]
]
[profile.release]
lto = true
opt-level = 's'

View File

@ -20,16 +20,16 @@
in pkgs.mkShell {
buildInputs = with pkgs; [
cargo
rustfmt
wasm-bindgen-cli
trunk
nodePackages.sass
rust-analyzer
(rust-bin.stable.latest.default.override {
extensions = [ "rust-src" ];
targets = [ "wasm32-unknown-unknown" ];
})
# ( )
];
shellHook = "";

View File

@ -1,12 +1,46 @@
body {
margin: 0;
}
details.aide[open] {
columns: 2;
background-color: rgba(255, 255, 255, 0.5);
border-radius: .5rem;
}
details.aide > summary {
font-weight: 1000;
}
details.configuration {
padding: 1ex;
}
details.configuration[open] {
background-color: rgba(0, 0, 0, 0.2);
border-radius: .5rem;
}
details.configuration > summary {
width: 100%;
text-align: right;
}
body > div {
background-color: aliceblue;
display: flex;
min-height: 100vh;
padding: 8px;
}
section#les_queues {
display: grid;
grid-template-columns: 1fr 1fr;
display: flex;
}
#les_queues > div {
flex-grow: 1;
}
h1 {
@ -22,6 +56,13 @@ p.orateurice {
text-align: center;
}
p.orateurice button{
margin: auto;
display: block;
margin-top: 10px;
}
li.dernier_arrive {
font-style: italic;
}
@ -34,6 +75,17 @@ li.dernier_arrive {
background-color: lightgreen;
}
.active section.queue h3::before {
content: ">>> " ;
font-style: italic;
}
.active section.queue h3 {
font-style: italic;
}
.attente section.queue {
background-color: pink;
}
@ -49,3 +101,8 @@ section.queue {
.hidden {
display: none;
}
#bouton_next {
font-size: 1.5rem;
font-weight: bold;
}

View File

@ -1,270 +1,388 @@
use sycamore::prelude::*;
use sycamore::builder::prelude::*;
use sycamore::rt::JsCast;
use rand::prelude::*;
use futures::future;
use futures::stream::StreamExt;
use gloo_timers::future::IntervalStream;
use rand::distributions::Alphanumeric;
enum NomQueue {
A, B
}
use rand::prelude::*;
use sycamore::builder::prelude::*;
use sycamore::futures::spawn_local_scoped;
use sycamore::prelude::*;
use sycamore::rt::JsCast;
#[derive(Clone, Copy, PartialEq, Eq)]
enum ModeQueue {
Chrono,
Aléatoire
Aléatoire,
}
impl ModeQueue {
fn set_aléatoire(&mut self, b: bool) {
match b {
false => { *self = ModeQueue::Chrono},
true => { *self = ModeQueue::Aléatoire},
}
match b {
false => *self = ModeQueue::Chrono,
true => *self = ModeQueue::Aléatoire,
}
}
}
impl std::fmt::Display for ModeQueue {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let s = match self {
ModeQueue::Chrono => "Chrono",
ModeQueue::Aléatoire => "Aléatoire"
};
write!(fmt, "{}", s)
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let s = match self {
ModeQueue::Chrono => "Chrono",
ModeQueue::Aléatoire => "Aléatoire",
};
write!(fmt, "{}", s)
}
}
#[derive(Clone)]
struct ÉtatQueue<'a> {
nom: String,
id: usize,
mode: &'a Signal<ModeQueue>,
contenu: &'a Signal<Vec<String>>,
nouvelleau: &'a Signal<String>,
priorité: &'a Signal<u8>,
crédit_parole: &'a Signal<f32>,
à_détruire: &'a Signal<bool>,
}
impl<'a> PartialEq for ÉtatQueue<'a> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<'a> Eq for ÉtatQueue<'a> {}
impl<'a> ÉtatQueue<'a> {
fn new<'b>(cx: BoundedScope<'a, 'b>, nom: &str) -> Self {
ÉtatQueue {
nom: nom.into(),
mode: create_signal(cx, ModeQueue::Aléatoire),
contenu: create_signal(cx, vec![]),
nouvelleau: create_signal(cx, "".into()),
priorité: create_signal(cx, 1.into()),
}
ÉtatQueue {
nom: nom.into(),
mode: create_signal(cx, ModeQueue::Aléatoire),
contenu: create_signal(cx, vec![]),
nouvelleau: create_signal(cx, "".into()),
priorité: create_signal(cx, 1),
crédit_parole: create_signal(cx, 0.0),
id: thread_rng().gen(),
à_détruire: create_signal(cx, false),
}
}
fn len(&self) -> usize {
return self.contenu.get().len()
return self.contenu.get().len();
}
fn enqueue(&mut self, rng: &mut ThreadRng) {
//
let s = &self.nouvelleau;
let n = self.len();
let k = match *self.mode.get() {
ModeQueue::Chrono => n,
ModeQueue::Aléatoire => {
rng.gen_range(0, n + 1)
}
};
self.contenu.modify().insert(k, s.to_string())
//
let s = &self.nouvelleau;
let n = self.len();
let k = match *self.mode.get() {
ModeQueue::Chrono => n,
ModeQueue::Aléatoire => rng.gen_range(0, n + 1),
};
self.contenu.modify().insert(k, s.to_string())
}
fn dequeue(&mut self) -> Option<String> {
if self.len() > 0 {
Some(self.contenu.modify().remove(0))
} else {
None
}
*self.crédit_parole.modify() -= 1.0;
if self.len() > 0 {
Some(self.contenu.modify().remove(0))
} else {
None
}
}
}
#[derive(Prop)]
struct QueueProp<'a>{
q: ÉtatQueue<'a>
struct QueueProp<'a> {
q: ÉtatQueue<'a>,
}
fn validate_queue<'a>(mut queue: ÉtatQueue<'a>) -> impl FnMut(web_sys::Event) + 'a {
move |ev: web_sys::Event| {
let mut rng = thread_rng();
let event : web_sys::KeyboardEvent = ev.unchecked_into();
if event.key_code() == 13 {
queue.enqueue(&mut rng);
queue.nouvelleau.set(String::new())
}
}
let mut rng = thread_rng();
let event: web_sys::KeyboardEvent = ev.unchecked_into();
if event.key_code() == 13 && !event.ctrl_key() {
queue.enqueue(&mut rng);
queue.nouvelleau.set(String::new())
}
}
}
// #[component]
// fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
// let queue_courante = use_context::<Signal<NomQueue>>(cx);
// }
#[component]
fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View<G> {
let queue: ÉtatQueue = q.q;
let prio_str = create_signal(cx, String::new());
let est_aléatoire = create_signal(cx, true); //*queue.q.mode.get() == ModeQueue::Aléatoire);
let nom = queue.q.nom;
let est_aléatoire = create_signal(cx, true);
let nom = queue.nom;
let suffix = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(15)
.map(char::from)
.collect::<String>();
create_effect(cx, ||
if let Ok(p) = prio_str.get().parse::<u8>() {
queue.q.priorité.set(p)
});
create_effect(cx, || prio_str.set(format!("{}", queue.q.priorité.get())));
create_effect(cx, ||
queue.q.mode.modify().set_aléatoire(*est_aléatoire.get()));
create_effect(cx, || est_aléatoire.set(*queue.q.mode.get() == ModeQueue::Aléatoire));
create_effect(cx, || {
if let Ok(p) = prio_str.get().parse::<u8>() {
queue.priorité.set(p)
}
});
create_effect(cx, || prio_str.set(format!("{}", queue.priorité.get())));
create_effect(cx, || {
queue.mode.modify().set_aléatoire(*est_aléatoire.get())
});
create_effect(cx, || {
est_aléatoire.set(*queue.mode.get() == ModeQueue::Aléatoire)
});
section()
.c(h3().dyn_t(move || nom.clone()))
.c(input()
.attr("type", "checkbox")
.dyn_attr("id", { let suffix = suffix.clone();
move || Some(format!("mode_queue_{}", &suffix.clone()))
})
.bind_checked(est_aléatoire)
)
.c(label()
.dyn_attr("for", {
let suffix = suffix.clone();
move || Some(format!("mode_queue_{}", &suffix.clone()))
})
.t("Entrée aléatoire")
)
.c(br())
.c(input()
.dyn_attr("id", { let suffix = suffix.clone();
move || Some(format!("prio_queue_{}", &suffix.clone()))
})
.attr("type", "range")
.attr("min", "1")
.attr("max", "7")
.bind_value(prio_str)
)
.c(
label()
.dyn_attr("for", { let suffix = suffix.clone();
move || Some(format!("prio_queue_{}", &suffix.clone()))
})
.dyn_t(|| format!("Priorité: {}", queue.q.priorité.get()))
)
.view(cx)
.c(h3().dyn_t(move || format!("{} ", nom)).c(button()
.t("Supprimer")
.on("click", move |_ev| (queue.à_détruire.set(true)))))
.c(input()
.attr("type", "checkbox")
.dyn_attr("id", {
let suffix = suffix.clone();
move || Some(format!("mode_queue_{}", &suffix.clone()))
})
.bind_checked(est_aléatoire))
.c(label()
.dyn_attr("for", {
let suffix = suffix.clone();
move || Some(format!("mode_queue_{}", &suffix.clone()))
})
.t("Entrée aléatoire"))
.c(br())
.c(input()
.dyn_attr("id", {
let suffix = suffix.clone();
move || Some(format!("prio_queue_{}", &suffix.clone()))
})
.attr("type", "range")
.attr("min", "1")
.attr("max", "7")
.bind_value(prio_str))
.c(label()
.dyn_attr("for", {
let suffix = suffix.clone();
move || Some(format!("prio_queue_{}", &suffix.clone()))
})
.dyn_t(|| format!("Priorité: {}", queue.priorité.get())))
.view(cx)
}
#[component]
fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
let ok_queue = validate_queue(queue.q.clone());
view! { cx,
div {
section(class="queue") {
h3 {(queue.q.nom)}
input(type="text", bind:value = queue.q.nouvelleau, on:keypress=ok_queue) {}
ul {
Keyed {
iterable: queue.q.contenu,
view: |cx, item: String| view! {cx, li { (item) }},
key: |x| x.clone()
}
}
}
}
div {
section(class="queue") {
h3 {(format!("{}", queue.q.nom))}
input(type="text", bind:value = queue.q.nouvelleau, on:keypress=ok_queue) {}
ul {
Keyed {
iterable: queue.q.contenu,
view: |cx, item: String| view! {cx, li { (item) }},
key: |x| x.clone()
}
}
}
}
}
}
#[derive(Prop, Clone, Copy)]
struct BarreCôtéProp<'a> {
queues: &'a Signal<Vec<ÉtatQueue<'a>>>,
}
#[component]
fn Explications<'a, G: Html>(cx: Scope<'a>) -> View<G> {
details()
.class("aide")
.c(summary().t("Comment utiliser Kikikoz"))
.c(p().t("Kikikoz est un outil qui vous aide à répartir la parole dans une réunion. Il s'exécute en toute discrétion dans votre navigateur: ").c(strong().t("il n'envoie aucune donnée où que ce soit.")))
.c(p().t("Quand vous lancez Kikikoz, l'interface que vous voyez correspond à une réunion avec un tour de parole divisé en deux files. C'est une pratique que l'on observe souvent, notamment pour répartir équitablement la parole entre femmes et hommes."))
.c(p().t("Quand une personne lève la main, inscrivez son nom dans le champ de texte correspondant à la file d'attente idoine, puis pressez ENTRÉE."))
.c(p().t("Pour donner la parole à la personne suivante, il suffit de cliquer sur le bouton \"suivantə\", ou de taper CTRL+ENTRÉE. Son nom et son temps de parole apparaissent alors en gros caractères"))
.c(p().t("La parole alterne entre les deux files. La prochaine personne à parler est donc celle qui est en tête de la file avec un fond vert et un titre en italique. Vous l'avez peut-être remarqué, quand une personne lève la main, elle ne va pas à la fin de la file, mais à une position aléatoire. C'est inhabituel, mais pas moins juste. Il y a des chances que ce qu'elle à a dire soit plus pertinent, puisqu'elle lève la main après avoir entendu plus de choses. Vous pouvez changer ce comportement pour chaque file."))
.c(p().t("Pour faire une pause dans la réunion (quand personne n'a la parole), cliquez sur \"pause\"."))
.c(p().t("Vous pouvez configurer toutes sortes de détails via le panneau configaration. Vous pouvez y ajouter ou supprimer des files, décider si l'ajout de personnes dans chaque file doit se faire à une position aléatoire ou à la fin. Vous pouvez également attribuer une priorité différente aux files, si vous décidez que la parole doit revenir à l'une d'entre elles plus souvent qu'aux autres."))
.view(cx)
}
#[component]
fn BarreCôté<'a, G: Html>(cx: Scope<'a>, p: BarreCôtéProp<'a>) -> View<G> {
let nom_nouvelle_file = create_signal(cx, String::new());
let nouvelle_file = {
let queues = p.queues.clone();
move |_ev| {
let q = ÉtatQueue::new(cx, &nom_nouvelle_file.get());
queues.modify().push(q);
*nom_nouvelle_file.modify() = String::new();
}
};
let nouvelle_file_clav = move |ev: web_sys::Event| {
let event: web_sys::KeyboardEvent = ev.clone().unchecked_into();
if event.key_code() == 13 && !event.ctrl_key() {
nouvelle_file(ev)
}
};
view! {cx,
aside {
details(class="configuration") {
summary { "Configuration " }
Keyed {
iterable: p.queues,
key: |q| q.id,
view: |cx, q| view!{cx, ConfigQueue{ q }}
}
section {
h3 {"Nouvelle file" }
input(type="text", bind:value=nom_nouvelle_file, on:keypress=nouvelle_file_clav) {}
button(on:click=nouvelle_file) { "Créer" }
}
}
}
}
}
fn main() {
sycamore::render(|cx| {
let prochainə = create_signal(cx, String::from("personne encore"));
let conf_visible = create_signal(cx, false);
let queue_A = ÉtatQueue::new(cx, "Gauche");
let queue_B = ÉtatQueue::new(cx, "Droite");
let queue_courante = create_signal(cx, NomQueue::A);
provide_context_ref(cx, queue_courante);
let next = {
let mut my_queue_A = queue_A.clone();
let mut my_queue_B = queue_B.clone();
move |_ev| {
let (queue, next) = match *queue_courante.get() {
NomQueue::A => (&mut my_queue_A, NomQueue::B),
NomQueue::B => (&mut my_queue_B, NomQueue::A)
};
match queue.dequeue() {
Some(p) => prochainə.set(p),
None => ()
}
queue_courante.set(next);
}
};
let queue_A_pour_aff = queue_A.clone();
let queue_A_pour_conf = queue_A.clone();
let queue_B_pour_aff = queue_B.clone();
let queue_B_pour_conf = queue_B.clone();
let heure_courante = create_signal(cx, 0.0);
let _ = spawn_local_scoped(cx, async {
IntervalStream::new(1000)
.for_each(|_| {
heure_courante.set(js_sys::Date::now());
future::ready(())
})
.await
});
let date_prise_parole = create_signal(cx, 0.0);
let courantə = create_signal(cx, None);
// let courantə2 = create_signal(cx, None).clone();
let queues = create_signal(
cx,
vec![ÉtatQueue::new(cx, "Gauche"), ÉtatQueue::new(cx, "Droite")],
);
create_effect(cx, || {
for q in queues.get().iter() {
q.à_détruire.track()
}
queues.modify().retain(|q| !(*q.à_détruire.get()));
});
let total_priorités: &ReadSignal<usize> = create_memo(cx, {
let queues = queues.clone();
move || {
queues
.get()
.iter()
.map(|q| *q.priorité.get() as usize)
.sum()
}
});
let queue_courante: &ReadSignal<(usize, usize)> = create_memo(cx, {
move || {
let (idx, (id, _q)) = queues
.get()
.iter()
.map(|q| (q.id, *q.crédit_parole.get()))
.enumerate()
.max_by(|(_ia, (_id_a, a)), (_ib, (_id_b, b))| {
a.partial_cmp(b).expect("Tried to compare a NaN")
})
.expect("bla");
(idx, id)
}
});
provide_context_ref(cx, queue_courante);
let pause = move |_ev| {
courantə.set(None);
};
let next = move |_ev| {
//js_sys::alert("coucou");
let mut queue: ÉtatQueue = queues.get()[queue_courante.get().0].clone();
match queue.dequeue() {
Some(p) => {
courantə.set(Some(p));
date_prise_parole.set(js_sys::Date::now())
}
None => {
courantə.set(None);
}
}
for q in queues.get().iter() {
*q.crédit_parole.modify() +=
(*q.priorité.get() as f32) / (*total_priorités.get() as f32)
}
};
view! { cx,
div(on:keypress= {let next = next.clone();
move |ev: web_sys::Event| {
let event: web_sys::KeyboardEvent = ev.clone().unchecked_into();
if event.key_code() == 13 && event.ctrl_key() {
next(ev)
}
}
}) {
main {
h1 { "Kikikoz" }
p(class="orateurice") {
(
match &*courantə.get() {
None => "En attente".into(),
Some(p) => {
let temps_parole = std::time::Duration::from_millis((*heure_courante.get() - *date_prise_parole.get()) as u64).as_secs();
format!("On écoute {} depuis {:2}:{:02}", p, temps_parole / 60, temps_parole % 60)
}
}
)
br {}
button(on:click=next, id="bouton_next") { "Suivantə" }
" "
button(on:click=pause) { "Pause" }
}
section(id="les_queues") {
Keyed {
iterable: queues, //_pour_aff,
key: |q| q.id,
view: {
let queue_courante = queue_courante.clone();
move |cx, q|
view!{ cx,
div(class=(if queue_courante.get().1 == q.id {"active"} else {"attente"})) {
Queue {q}
}
}
}
}
}
br {}
Explications {}
br {}
footer {
a(href="https://git.tausendblum.site/florent.becker/kikikoz") { "Voir le code source."}
}
}
BarreCôté {
queues
}
}
view! { cx,
main {
h1 { "Kikikoz" }
p(class="orateurice") {
"On écoute " (prochainə.get()) "."
br {}
button(on:click=next) { "Suivantə" }
}
section(id="les_queues") {
div(class=(match *queue_courante.get(){NomQueue::A=> "active", NomQueue::B=>"attente"})) {
Queue {
q: queue_A_pour_aff
}
}
div(class=(match *queue_courante.get(){NomQueue::B=>"active", NomQueue::A=>"attente"})) {
Queue {
q: queue_B_pour_aff
}
}
}
}
aside {
button(on:click=|_| conf_visible.set(! *conf_visible.get())) {
(
if *conf_visible.get() {
"cacher"
} else {
"configuration"
}
)
}
div(class=if *conf_visible.get() { "visible" } else { "hidden" }) {
h2 { "Configuration" }
ConfigQueue {
q: queue_A_pour_conf
}
ConfigQueue {
q: queue_B_pour_conf
}
// section {
// h3 { "Queue B" }
// input(type="checkbox", id="mélanger_queue_B") {}
// label(for="enable_queue_B") {"Randomiser"}
// br {}
// input(type="range", id="prio_queue_B", min="1", max="7", bind:value=queue_B.priorité) {}
// label(for="prio_queue_B") {"Priorité "} // (queue_B.priorité.get())}
// }
}
}
}
}
});
}