Skip to content

Commit

Permalink
Update websockets, theme
Browse files Browse the repository at this point in the history
  • Loading branch information
kahnclusions committed Aug 10, 2024
1 parent 313f37e commit 1ca5b37
Show file tree
Hide file tree
Showing 15 changed files with 477 additions and 223 deletions.
348 changes: 279 additions & 69 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ rust_decimal = "1.35.0"
use_websocket = { path = "../use_websocket", optional = true }
codee = { version = "0.1.2", features = ["msgpack_serde"] }
web-sys = "0.3.69"
leptos_icons = { git = "https://github.com/kahnclusions/leptos-icons.git"}
icondata = "0.4.0"
icondata_core = "0.1.0"
humantime = "2.1.0"


[features]
Expand Down
49 changes: 0 additions & 49 deletions app/src/components/example.rs

This file was deleted.

1 change: 1 addition & 0 deletions app/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// pub mod example;
// pub mod icon;
pub mod status_bar;
pub mod torrents;
57 changes: 45 additions & 12 deletions app/src/components/status_bar.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,59 @@
use fnord_ui::components::{Text, View};
use fnord_ui::components::View;
use human_bytes::human_bytes;
use icondata as i;
use leptos::prelude::*;
use leptos::text_prop::TextProp;
use leptos_icons::Icon;
use use_websocket::core::ConnectionReadyState;

use crate::signals::syncstate::ServerState;
use qbittorrent_rs_proto::transfer::ServerStateFull;

#[component]
pub fn StatusBar(server_state: ServerState) -> impl IntoView {
pub fn StatusBar(
server_state: ServerState,
ready_state: Signal<ConnectionReadyState>,
) -> impl IntoView {
let dl_speed = move || human_bytes(server_state.dl_info_speed.get());
let up_speed = move || human_bytes(server_state.up_info_speed.get());
let dl_data = move || human_bytes(server_state.dl_info_data.get());
let up_data = move || human_bytes(server_state.up_info_data.get());

let status = move || match ready_state.get() {
ConnectionReadyState::Open => "Open",
ConnectionReadyState::Closed => "Closed",
ConnectionReadyState::Closing => "Closing",
ConnectionReadyState::Connecting => "Opening",
};

let status_icon = move || match ready_state.get() {
ConnectionReadyState::Open => {
view! { <Icon icon=i::TbNetwork class=TextProp::from("w-4 w-4 text-lime-600") /> }
}
ConnectionReadyState::Connecting => {
view! { <Icon icon=i::TbNetwork class=TextProp::from("w-4 w-4 text-grey-300") /> }
}
ConnectionReadyState::Closed => {
view! { <Icon icon=i::TbNetworkOff class=TextProp::from("w-4 w-4 text-red-600") /> }
}
ConnectionReadyState::Closing => {
view! { <Icon icon=i::TbNetworkOff class=TextProp::from("w-4 w-4 text-grey-300") /> }
}
};

view! {
<View class="flex-row bg-background-highlight justify-between fixed bottom-0 left-0 right-0 h-10 text-sm">
<View class="gap-0">
<div>{move || dl_speed()}"/s"</div>
<div>{move || up_speed()}"/s"</div>
<View class="flex-row bg-background-highlight justify-between items-stretch fixed bottom-0 left-0 right-0 h-8 text-sm border-t border-t-gray-300 dark:border-t-gray-700 gap-0">
<View class="flex-row gap-1 p-1 px-2 border-r border-t-gray-300 dark:border-r-gray-700 grow w-full items-center">
<Icon icon=i::FaDownloadSolid class=TextProp::from("w-4 h-4") />
<span>{move || dl_speed()} "/s"</span>
</View>
<View class="flex-row gap-1 p-1 px-2 border-r border-t-gray-300 dark:border-r-gray-700 grow w-full items-center">
<Icon icon=i::FaUploadSolid class=TextProp::from("w-4 h-4") />
<span>{move || up_speed()} "/s"</span>
</View>
<View class="flex-row gap-1 items-center p-1 px-2 border-r border-t-gray-300 dark:border-r-gray-700 justify-start shrink-0">
<Icon icon=i::BiNetworkChartRegular class=TextProp::from("w-4 w-4") />
<span>{move || server_state.dht_nodes.get()}</span>
</View>
<View class="gap-0">
<div>"Downloaded: "{move || dl_data()}</div>
<div>"Uploaded: "{move || up_data()}</div>
<View class="flex-row gap-1 items-center p-1 px-2 justify-start">
{move || status_icon()} {move || status()}
</View>
</View>
}
Expand Down
76 changes: 52 additions & 24 deletions app/src/components/torrents.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::time::Duration;

use human_bytes::human_bytes;
use humantime::format_duration;
use rust_decimal::prelude::*;
use tailwind_fuse::tw_merge;

Expand All @@ -11,26 +14,32 @@ static CELL_CLASS: &'static str = "shadow-border p-2 whitespace-nowrap text-left
#[component]
pub fn TorrentList(torrents: Signal<Vec<Torrent>>) -> impl IntoView {
view! {
<div class="h-full w-full overflow-auto ">
<table class=" border-grey border-spacing-[2px] border-collapse border-px">
<div class="h-full w-full overflow-auto overscroll-none">
<table class=" border-grey border-spacing-[2px] border-collapse border-px w-full">
<thead class="">
<tr>
<th class=tw_merge!("sticky left-0 bg-background z-10 overflow-hidden text-ellipsis whitespace-nowrap max-w-[40vw] shadow-border p-1 text-left font-normal", CELL_CLASS)>"Name"</th>
<th class=tw_merge!(
"sticky left-0 bg-gray-50 dark:bg-gray-950 z-10 overflow-hidden text-ellipsis whitespace-nowrap max-w-[40vw] shadow-border p-1 text-left font-normal w-[40vw]",
CELL_CLASS
)>"Name"</th>
<th class=CELL_CLASS>"Progress"</th>
<th class=CELL_CLASS>"DL/s"</th>
<th class=CELL_CLASS>"UP/s"</th>
<th class=CELL_CLASS>"SD"</th>
<th class=CELL_CLASS>"LE"</th>
<th class=tw_merge!(CELL_CLASS, "w-[90px]")>"DL/s"</th>
<th class=tw_merge!(CELL_CLASS, "w-[90px]")>"UP/s"</th>
<th class=CELL_CLASS>"Seeds"</th>
<th class=CELL_CLASS>"Leechs"</th>
<th class=CELL_CLASS>"Eta"</th>
<th class=CELL_CLASS>"Avail."</th>
</tr>
</thead>
<tbody>
<For
each=torrents
key=|torrent| torrent.name.clone()
children=move |torrent| {
view! { <TorrentSummary torrent=torrent /> }
view! { <TorrentSummary torrent=torrent/> }
}
/>

</tbody>
</table>
</div>
Expand All @@ -40,27 +49,35 @@ pub fn TorrentList(torrents: Signal<Vec<Torrent>>) -> impl IntoView {
#[component]
pub fn TorrentSummary(torrent: Torrent) -> impl IntoView {
let name = move || torrent.name.get();
let torrent_progress = torrent.progress.clone();
let progress = move || {
Decimal::from_str(format!("{:.2}", torrent_progress.get() * 100.0).as_str())
.unwrap()
.normalize()
.to_string()
};

// let downloaded = move || human_bytes(torrent.downloaded.get());
// let uploaded = move || human_bytes(torrent.downloaded.get());
let dlspeed = move || human_bytes(torrent.dlspeed.get());
let upspeed = move || human_bytes(torrent.upspeed.get());

let availability = move || torrent.availability.get().min(1.0);
let eta = move || format_duration(Duration::from_secs_f64(torrent.eta.get())).to_string();

view! {
<tr class="gap-0">
<th class=tw_merge!("sticky left-0 bg-background z-10 overflow-hidden text-ellipsis whitespace-nowrap max-w-[40vw] shadow-border p-1 text-left font-normal", CELL_CLASS)>{move || name()}</th>
<td class=CELL_CLASS><Progress progress={torrent.progress} downloaded={torrent.downloaded} size={torrent.size} total_size={torrent.total_size} /></td>
<td class=CELL_CLASS>{move || dlspeed()}</td>
<td class=CELL_CLASS>{move || upspeed()}</td>
<th class=tw_merge!(
"sticky left-0 bg-gray-50 dark:bg-gray-950 z-10 overflow-hidden text-ellipsis whitespace-nowrap max-w-[40vw] shadow-border p-1 text-left font-normal",
CELL_CLASS
)>{move || name()}</th>
<td class=CELL_CLASS>
<Progress
progress=torrent.progress
downloaded=torrent.downloaded
size=torrent.size
total_size=torrent.total_size
/>
</td>
<td class=tw_merge!(CELL_CLASS, "w-[90px]")>{move || dlspeed()}</td>
<td class=tw_merge!(CELL_CLASS, "w-[90px]")>{move || upspeed()}</td>
<td class=CELL_CLASS>{move || torrent.num_seeds.get()}</td>
<td class=CELL_CLASS>{move || torrent.num_leechs.get()}</td>
<td class=CELL_CLASS>{move || eta()}</td>
<td class=CELL_CLASS>{move || availability()}</td>
</tr>
}
}
Expand All @@ -74,15 +91,26 @@ fn Progress(
) -> impl IntoView {
let total = total_size.clone();
let percent_selected = move || size.get() / total.get();
let inner_bar_w = move || (percent_selected().min(1.0) * 150.0).ceil();
let inner_bar_w = move || (percent_selected().min(1.0) * 110.0).ceil();
let inner_bar_w2 = inner_bar_w.clone();
let percent_complete = move || (progress.get().min(1.0) * inner_bar_w()) - 8.0;

view! {
<div class="flex flex-col w-[150px] gap-[2px]">
<div class="rounded bg-background_dark h-2">
<div class="rounded bg-background_highlight h-2" style:width=move || { format!("{}px", inner_bar_w2()) }>
<div class="border-t-[2px] border-t-green1 relative top-[3px] left-[4px]" style:width=move || { format!("{}px", (percent_complete().ceil().max(0.0))) }>""</div>
<div class="flex flex-col w-[110px] gap-[2px]">
<div class="rounded dark:bg-gray-900 h-2">
<div
class="rounded dark:bg-gray-800 h-2"
style:width=move || { format!("{}px", inner_bar_w2()) }
>
<div
class="border-t-[2px] border-t-cyan-600 relative top-[3px] left-[4px]"
style:width=move || {
format!("{}px", (percent_complete().ceil().max(0.0)))
}
>

""
</div>
</div>
</div>
<div class="flex flex-row justify-between text-2xs">
Expand Down
Loading

0 comments on commit 1ca5b37

Please sign in to comment.