Formatting and minor code changes
This commit is contained in:
@@ -11,13 +11,24 @@ fn endofgame_nuke() {
|
|||||||
|
|
||||||
let expected = endofgame::EndOfGame {
|
let expected = endofgame::EndOfGame {
|
||||||
map: "de_nuke".to_owned(),
|
map: "de_nuke".to_owned(),
|
||||||
teams: [(2, endofgame::TeamInfo {
|
teams: [
|
||||||
end_score: 13,
|
(
|
||||||
start_side: "CT".into(),
|
2,
|
||||||
}), (3, endofgame::TeamInfo {
|
endofgame::TeamInfo {
|
||||||
end_score: 8,
|
end_score: 13,
|
||||||
start_side: "TERRORIST".into()
|
start_side: "CT".into(),
|
||||||
})].into_iter().collect(),
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
3,
|
||||||
|
endofgame::TeamInfo {
|
||||||
|
end_score: 8,
|
||||||
|
start_side: "TERRORIST".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
players: vec![
|
players: vec![
|
||||||
(
|
(
|
||||||
endofgame::PlayerInfo {
|
endofgame::PlayerInfo {
|
||||||
|
|||||||
@@ -36,10 +36,33 @@ pub static ANALYSIS_METHODS: std::sync::LazyLock<[std::sync::Arc<dyn Analysis +
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
pub async fn poll_next_task(
|
#[derive(Debug)]
|
||||||
upload_folder: &std::path::Path,
|
pub enum TaskError<AE> {
|
||||||
|
Diesel(diesel::result::Error),
|
||||||
|
RunningAction(AE),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AE> From<diesel::result::Error> for TaskError<AE> {
|
||||||
|
fn from(value: diesel::result::Error) -> Self {
|
||||||
|
Self::Diesel(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn poll_next_task<A, AE>(
|
||||||
|
upload_folder: impl Into<std::path::PathBuf>,
|
||||||
db_con: &mut diesel_async::pg::AsyncPgConnection,
|
db_con: &mut diesel_async::pg::AsyncPgConnection,
|
||||||
) -> Result<AnalysisInput, ()> {
|
action: A,
|
||||||
|
) -> Result<(), TaskError<AE>>
|
||||||
|
where
|
||||||
|
A: Fn(
|
||||||
|
AnalysisInput,
|
||||||
|
&mut diesel_async::pg::AsyncPgConnection,
|
||||||
|
)
|
||||||
|
-> core::pin::Pin<Box<(dyn core::future::Future<Output = Result<(), AE>> + Send + '_)>>
|
||||||
|
+ Send
|
||||||
|
+ Clone
|
||||||
|
+ Sync,
|
||||||
|
{
|
||||||
let query = crate::schema::analysis_queue::dsl::analysis_queue
|
let query = crate::schema::analysis_queue::dsl::analysis_queue
|
||||||
.order(crate::schema::analysis_queue::dsl::created_at.asc())
|
.order(crate::schema::analysis_queue::dsl::created_at.asc())
|
||||||
.limit(1)
|
.limit(1)
|
||||||
@@ -47,50 +70,53 @@ pub async fn poll_next_task(
|
|||||||
.for_update()
|
.for_update()
|
||||||
.skip_locked();
|
.skip_locked();
|
||||||
|
|
||||||
|
let upload_folder: std::path::PathBuf = upload_folder.into();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let upload_folder = upload_folder.clone();
|
||||||
|
let action = action.clone();
|
||||||
|
|
||||||
let result = db_con
|
let result = db_con
|
||||||
.build_transaction()
|
.build_transaction()
|
||||||
.run::<_, diesel::result::Error, _>(|conn| {
|
.run::<_, TaskError<AE>, _>(|conn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut results: Vec<crate::models::AnalysisTask> = query.load(conn).await?;
|
let mut results: Vec<crate::models::AnalysisTask> = query.load(conn).await?;
|
||||||
let final_result = match results.pop() {
|
let task = match results.pop() {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let input = AnalysisInput {
|
||||||
|
path: upload_folder
|
||||||
|
.join(&task.steam_id)
|
||||||
|
.join(format!("{}.dem", task.demo_id)),
|
||||||
|
steamid: task.steam_id.clone(),
|
||||||
|
demoid: task.demo_id.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let tmp = action(input, &mut *conn);
|
||||||
|
tmp.await.map_err(|e| TaskError::RunningAction(e))?;
|
||||||
|
|
||||||
let delete_query =
|
let delete_query =
|
||||||
diesel::dsl::delete(crate::schema::analysis_queue::dsl::analysis_queue)
|
diesel::dsl::delete(crate::schema::analysis_queue::dsl::analysis_queue)
|
||||||
.filter(
|
.filter(crate::schema::analysis_queue::dsl::demo_id.eq(task.demo_id))
|
||||||
crate::schema::analysis_queue::dsl::demo_id
|
.filter(crate::schema::analysis_queue::dsl::steam_id.eq(task.steam_id));
|
||||||
.eq(final_result.demo_id.clone()),
|
|
||||||
)
|
|
||||||
.filter(
|
|
||||||
crate::schema::analysis_queue::dsl::steam_id
|
|
||||||
.eq(final_result.steam_id.clone()),
|
|
||||||
);
|
|
||||||
delete_query.execute(conn).await?;
|
delete_query.execute(conn).await?;
|
||||||
|
|
||||||
Ok(Some(final_result))
|
Ok(Some(()))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(r)) => {
|
Ok(Some(())) => {
|
||||||
return Ok(AnalysisInput {
|
return Ok(());
|
||||||
path: upload_folder
|
|
||||||
.join(&r.steam_id)
|
|
||||||
.join(format!("{}.dem", r.demo_id)),
|
|
||||||
steamid: r.steam_id,
|
|
||||||
demoid: r.demo_id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Getting Task from Postgres: {:?}", e);
|
return Err(e);
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -456,7 +456,11 @@ async fn perround(
|
|||||||
.map(|dteam| common::demo_analysis::PerRoundTeam {
|
.map(|dteam| common::demo_analysis::PerRoundTeam {
|
||||||
name: dteam.start_name,
|
name: dteam.start_name,
|
||||||
number: dteam.team as u32,
|
number: dteam.team as u32,
|
||||||
players: players.iter().filter(|p| p.team == dteam.team).map(|p| p.name.clone()).collect(),
|
players: players
|
||||||
|
.iter()
|
||||||
|
.filter(|p| p.team == dteam.team)
|
||||||
|
.map(|p| p.name.clone())
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ pub async fn run_api(
|
|||||||
let serve_dir = option_env!("FRONTEND_DIST_DIR").unwrap_or("../frontend/dist/");
|
let serve_dir = option_env!("FRONTEND_DIST_DIR").unwrap_or("../frontend/dist/");
|
||||||
tracing::debug!("Serving static files from {:?}", serve_dir);
|
tracing::debug!("Serving static files from {:?}", serve_dir);
|
||||||
|
|
||||||
let steam_callback_base_url = std::env::var("BASE_URL").unwrap_or("http://localhost:3000".to_owned());
|
let steam_callback_base_url =
|
||||||
|
std::env::var("BASE_URL").unwrap_or("http://localhost:3000".to_owned());
|
||||||
tracing::debug!("Base-URL: {:?}", steam_callback_base_url);
|
tracing::debug!("Base-URL: {:?}", steam_callback_base_url);
|
||||||
|
|
||||||
let router = axum::Router::new()
|
let router = axum::Router::new()
|
||||||
@@ -93,68 +94,63 @@ pub async fn run_api(
|
|||||||
#[tracing::instrument(skip(upload_folder))]
|
#[tracing::instrument(skip(upload_folder))]
|
||||||
pub async fn run_analysis(upload_folder: impl Into<std::path::PathBuf>) {
|
pub async fn run_analysis(upload_folder: impl Into<std::path::PathBuf>) {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
let upload_folder: std::path::PathBuf = upload_folder.into();
|
let upload_folder: std::path::PathBuf = upload_folder.into();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut db_con = db_connection().await;
|
let mut db_con = db_connection().await;
|
||||||
let input = match crate::analysis::poll_next_task(&upload_folder, &mut db_con).await {
|
|
||||||
Ok(i) => i,
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Polling for next Task: {:?}", e);
|
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let demo_id = input.demoid.clone();
|
let res = crate::analysis::poll_next_task(
|
||||||
|
&upload_folder,
|
||||||
let mut store_result_fns = Vec::new();
|
&mut db_con,
|
||||||
for analysis in analysis::ANALYSIS_METHODS.iter().map(|a| a.clone()) {
|
move |input: analysis::AnalysisInput, db_con: &mut diesel_async::AsyncPgConnection| {
|
||||||
let input = input.clone();
|
|
||||||
let store_result =
|
|
||||||
match tokio::task::spawn_blocking(move || analysis.analyse(input)).await {
|
|
||||||
Ok(Ok(r)) => r,
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
tracing::error!("Analysis failed: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Joining Task: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
store_result_fns.push(store_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut db_con = crate::db_connection().await;
|
|
||||||
|
|
||||||
let update_process_info =
|
|
||||||
diesel::dsl::update(crate::schema::processing_status::dsl::processing_status)
|
|
||||||
.set(crate::schema::processing_status::dsl::info.eq(1))
|
|
||||||
.filter(crate::schema::processing_status::dsl::demo_id.eq(demo_id));
|
|
||||||
|
|
||||||
let store_res = db_con
|
|
||||||
.transaction::<'_, '_, '_, _, diesel::result::Error, _>(|conn| {
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
for store_fn in store_result_fns {
|
let demo_id = input.demoid.clone();
|
||||||
store_fn(conn).await?;
|
|
||||||
}
|
|
||||||
update_process_info.execute(conn).await?;
|
|
||||||
|
|
||||||
Ok(())
|
let mut store_result_fns = Vec::new();
|
||||||
|
for analysis in analysis::ANALYSIS_METHODS.iter().map(|a| a.clone()) {
|
||||||
|
let input = input.clone();
|
||||||
|
let store_result = match tokio::task::spawn_blocking(move || {
|
||||||
|
analysis.analyse(input)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Ok(r)) => r,
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
tracing::error!("Analysis failed: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Joining Task: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
store_result_fns.push(store_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let update_process_info = diesel::dsl::update(
|
||||||
|
crate::schema::processing_status::dsl::processing_status,
|
||||||
|
)
|
||||||
|
.set(crate::schema::processing_status::dsl::info.eq(1))
|
||||||
|
.filter(crate::schema::processing_status::dsl::demo_id.eq(demo_id));
|
||||||
|
|
||||||
|
for store_fn in store_result_fns {
|
||||||
|
store_fn(db_con).await.map_err(|e| ())?;
|
||||||
|
}
|
||||||
|
update_process_info.execute(db_con).await.map_err(|e| ())?;
|
||||||
|
|
||||||
|
Ok::<(), ()>(())
|
||||||
})
|
})
|
||||||
})
|
},
|
||||||
.await;
|
)
|
||||||
match store_res {
|
.await;
|
||||||
Ok(_) => {
|
|
||||||
tracing::info!("Stored analysis results");
|
if let Err(e) = res {
|
||||||
}
|
tracing::error!("Polling for next Task: {:?}", e);
|
||||||
Err(e) => {
|
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
||||||
tracing::error!("Failed to store results: {:?}", e);
|
continue;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use leptos::*;
|
|||||||
use leptos_router::{Outlet, A};
|
use leptos_router::{Outlet, A};
|
||||||
|
|
||||||
pub mod heatmap;
|
pub mod heatmap;
|
||||||
pub mod scoreboard;
|
|
||||||
pub mod perround;
|
pub mod perround;
|
||||||
|
pub mod scoreboard;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct CurrentDemoName(ReadSignal<String>);
|
struct CurrentDemoName(ReadSignal<String>);
|
||||||
@@ -31,10 +31,10 @@ pub fn demo() -> impl leptos::IntoView {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let rerun_analysis = create_action(move |_: &()| {
|
let rerun_analysis = create_action(move |_: &()| async move {
|
||||||
async move {
|
let _ = reqwasm::http::Request::get(&format!("/api/demos/{}/reanalyse", id()))
|
||||||
let _ = reqwasm::http::Request::get(&format!("/api/demos/{}/reanalyse", id())).send().await;
|
.send()
|
||||||
}
|
.await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let map = move || match demo_info.get() {
|
let map = move || match demo_info.get() {
|
||||||
|
|||||||
@@ -4,19 +4,17 @@ use super::CurrentDemoName;
|
|||||||
|
|
||||||
#[leptos::component]
|
#[leptos::component]
|
||||||
pub fn heatmaps() -> impl leptos::IntoView {
|
pub fn heatmaps() -> impl leptos::IntoView {
|
||||||
let heatmaps_resource =
|
let heatmaps_resource = create_resource(leptos_router::use_params_map(), |params| async move {
|
||||||
create_resource(leptos_router::use_params_map(), |params| async move {
|
let id = params.get("id").unwrap();
|
||||||
let id = params.get("id").unwrap();
|
|
||||||
|
|
||||||
let res =
|
let res = reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/heatmap", id))
|
||||||
reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/heatmap", id))
|
.send()
|
||||||
.send()
|
.await
|
||||||
.await
|
.unwrap();
|
||||||
.unwrap();
|
res.json::<Vec<common::demo_analysis::PlayerHeatmap>>()
|
||||||
res.json::<Vec<common::demo_analysis::PlayerHeatmap>>()
|
.await
|
||||||
.await
|
.unwrap()
|
||||||
.unwrap()
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let style = stylers::style! {
|
let style = stylers::style! {
|
||||||
"Heatmap-Wrapper",
|
"Heatmap-Wrapper",
|
||||||
@@ -111,17 +109,27 @@ fn heatmap_view(heatmaps: Vec<common::demo_analysis::PlayerHeatmap>) -> impl lep
|
|||||||
|
|
||||||
let player = players.get(idx).unwrap();
|
let player = players.get(idx).unwrap();
|
||||||
|
|
||||||
set_value(heatmaps.iter().filter(|h| &h.name == player).cloned().collect());
|
set_value(
|
||||||
|
heatmaps
|
||||||
|
.iter()
|
||||||
|
.filter(|h| &h.name == player)
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
set_idx(idx);
|
set_idx(idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
let players = og_players;
|
let players = og_players;
|
||||||
let select_values = move || {
|
let select_values = move || {
|
||||||
players.iter().enumerate().map(|(idx, name)| {
|
players
|
||||||
view! {
|
.iter()
|
||||||
<option value={idx}>{ format!("{}", name) }</option>
|
.enumerate()
|
||||||
}
|
.map(|(idx, name)| {
|
||||||
}).collect::<Vec<_>>()
|
view! {
|
||||||
|
<option value={idx}>{ format!("{}", name) }</option>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
|
|||||||
@@ -14,19 +14,17 @@ fn to_coloumn(idx: usize) -> usize {
|
|||||||
|
|
||||||
#[leptos::component]
|
#[leptos::component]
|
||||||
pub fn per_round() -> impl leptos::IntoView {
|
pub fn per_round() -> impl leptos::IntoView {
|
||||||
let perround_resource =
|
let perround_resource = create_resource(leptos_router::use_params_map(), |params| async move {
|
||||||
create_resource(leptos_router::use_params_map(), |params| async move {
|
let id = params.get("id").unwrap();
|
||||||
let id = params.get("id").unwrap();
|
|
||||||
|
|
||||||
let res =
|
let res = reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/perround", id))
|
||||||
reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/perround", id))
|
.send()
|
||||||
.send()
|
.await
|
||||||
.await
|
.unwrap();
|
||||||
.unwrap();
|
res.json::<common::demo_analysis::PerRoundResult>()
|
||||||
res.json::<common::demo_analysis::PerRoundResult>()
|
.await
|
||||||
.await
|
.unwrap()
|
||||||
.unwrap()
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let style = stylers::style! {
|
let style = stylers::style! {
|
||||||
"PerRound",
|
"PerRound",
|
||||||
@@ -73,7 +71,10 @@ pub fn per_round() -> impl leptos::IntoView {
|
|||||||
let events_list = move || {
|
let events_list = move || {
|
||||||
let round_index = round();
|
let round_index = round();
|
||||||
let data = perround_resource.get();
|
let data = perround_resource.get();
|
||||||
let current_round = data.as_ref().map(|rs| rs.rounds.get(round_index).cloned()).flatten();
|
let current_round = data
|
||||||
|
.as_ref()
|
||||||
|
.map(|rs| rs.rounds.get(round_index).cloned())
|
||||||
|
.flatten();
|
||||||
let teams = data.as_ref().map(|rs| rs.teams.clone());
|
let teams = data.as_ref().map(|rs| rs.teams.clone());
|
||||||
|
|
||||||
match (current_round, teams) {
|
match (current_round, teams) {
|
||||||
@@ -171,13 +172,22 @@ pub fn per_round() -> impl leptos::IntoView {
|
|||||||
None => return view! {}.into_view(),
|
None => return view! {}.into_view(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let upper = perround_teams.iter().find(|t| t.name == "CT").map(|t| t.number).unwrap_or(0);
|
let upper = perround_teams
|
||||||
let lower = perround_teams.iter().find(|t| t.name == "TERRORIST").map(|t| t.number).unwrap_or(0);
|
.iter()
|
||||||
|
.find(|t| t.name == "CT")
|
||||||
|
.map(|t| t.number)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let lower = perround_teams
|
||||||
|
.iter()
|
||||||
|
.find(|t| t.name == "TERRORIST")
|
||||||
|
.map(|t| t.number)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<span style="grid-column: 1; grid-row: 1">Team { upper }</span>
|
<span style="grid-column: 1; grid-row: 1">Team { upper }</span>
|
||||||
<span style="grid-column: 1; grid-row: 3">Team { lower }</span>
|
<span style="grid-column: 1; grid-row: 3">Team { lower }</span>
|
||||||
}.into_view()
|
}
|
||||||
|
.into_view()
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ pub fn scoreboard() -> impl leptos::IntoView {
|
|||||||
.get()
|
.get()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|v| v.teams.into_iter())
|
.flat_map(|v| v.teams.into_iter())
|
||||||
.map(|(team, players)| view! {
|
.map(|(team, players)| {
|
||||||
<TeamScoreboard value=players team_name=format!("Team {}", team) />
|
view! {
|
||||||
|
<TeamScoreboard value=players team_name=format!("Team {}", team) />
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
@@ -46,7 +48,10 @@ mod orderings {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Ordering {
|
pub struct Ordering {
|
||||||
name: SelectedStat,
|
name: SelectedStat,
|
||||||
pub sort_fn: fn(p1: &common::demo_analysis::ScoreBoardPlayer, p2: &common::demo_analysis::ScoreBoardPlayer) -> core::cmp::Ordering,
|
pub sort_fn: fn(
|
||||||
|
p1: &common::demo_analysis::ScoreBoardPlayer,
|
||||||
|
p2: &common::demo_analysis::ScoreBoardPlayer,
|
||||||
|
) -> core::cmp::Ordering,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ordering {
|
impl Ordering {
|
||||||
@@ -89,7 +94,10 @@ mod orderings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[leptos::component]
|
#[leptos::component]
|
||||||
fn team_scoreboard(value: Vec<common::demo_analysis::ScoreBoardPlayer>, team_name: String) -> impl IntoView {
|
fn team_scoreboard(
|
||||||
|
value: Vec<common::demo_analysis::ScoreBoardPlayer>,
|
||||||
|
team_name: String,
|
||||||
|
) -> impl IntoView {
|
||||||
let (ordering, set_ordering) = create_signal::<orderings::Ordering>(orderings::DAMAGE);
|
let (ordering, set_ordering) = create_signal::<orderings::Ordering>(orderings::DAMAGE);
|
||||||
|
|
||||||
let style = stylers::style! {
|
let style = stylers::style! {
|
||||||
|
|||||||
Reference in New Issue
Block a user