Skip to content

Commit

Permalink
feat: add incoming transfer progress (#47)
Browse files Browse the repository at this point in the history
* fix: don't spam tokio runtime
* fix: readme format

Signed-off-by: Martichou <[email protected]>
  • Loading branch information
Martichou authored Apr 29, 2024
1 parent 7091df0 commit f154e90
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 45 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ sudo dpkg -i r-quick-share_${VERSION}_amd64.deb

```
sudo rpm -i r-quick-share-${VERSION}-1.x86_64.rpm
```

#### AppImage (no root required)
Expand Down
2 changes: 1 addition & 1 deletion core_lib/bindings/TransferMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RemoteDeviceInfo } from "./RemoteDeviceInfo";

export interface TransferMetadata { id: string, destination: string | null, source: RemoteDeviceInfo | null, files: Array<string> | null, pin_code: string | null, text_description: string | null, text_payload: string | null, }
export interface TransferMetadata { id: string, destination: string | null, source: RemoteDeviceInfo | null, files: Array<string> | null, pin_code: string | null, text_description: string | null, text_payload: string | null, total_bytes: bigint, ack_bytes: bigint, }
14 changes: 7 additions & 7 deletions core_lib/bindings/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export * from "./OutboundPayload"
export * from "./ChannelDirection"
export * from "./DeviceType"
export * from "./Visibility"
export * from "./ChannelMessage"
export * from "./SendInfo"
export * from "./TransferType"
export * from "./EndpointInfo"
export * from "./RemoteDeviceInfo"
export * from "./DeviceType"
export * from "./ChannelDirection"
export * from "./OutboundPayload"
export * from "./TransferMetadata"
export * from "./EndpointInfo"
export * from "./ChannelAction"
export * from "./RemoteDeviceInfo"
export * from "./State"
export * from "./Visibility"
export * from "./SendInfo"
29 changes: 22 additions & 7 deletions core_lib/src/hdl/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,11 @@ impl InboundRequest {
current_offset
));
}
if current_offset + chunk.body().len() as i64 > file_internal.total_size {

let chunk_size = chunk.body().len();
if current_offset + chunk_size as i64 > file_internal.total_size {
return Err(anyhow!(
"Transferred file size exceeds previously specified value: {} vs {}", current_offset + chunk.body().len() as i64, file_internal.total_size
"Transferred file size exceeds previously specified value: {} vs {}", current_offset + chunk_size as i64, file_internal.total_size
));
}

Expand All @@ -657,7 +659,16 @@ impl InboundRequest {
.as_ref()
.unwrap()
.write_all_at(chunk.body(), current_offset as u64)?;
file_internal.bytes_transferred += chunk.body().len() as i64;
file_internal.bytes_transferred += chunk_size as i64;

self.update_state(
|e| {
if let Some(tmd) = e.transfer_metadata.as_mut() {
tmd.ack_bytes += chunk_size as i64;
}
},
true,
);
} else if (chunk.flags() & 1) == 1 {
self.state.transferred_files.remove(&payload_id);
if self.state.transferred_files.is_empty() {
Expand Down Expand Up @@ -808,6 +819,7 @@ impl InboundRequest {
if !introduction.file_metadata.is_empty() && introduction.text_metadata.is_empty() {
trace!("process_introduction: handling file_metadata");
let mut files_name = Vec::with_capacity(introduction.file_metadata.len());
let mut total_bytes = 0;

for file in &introduction.file_metadata {
info!("File name: {}", file.name());
Expand Down Expand Up @@ -839,6 +851,7 @@ impl InboundRequest {
total_size: file.size(),
file: None,
};
total_bytes += info.total_size;
self.state.transferred_files.insert(file.payload_id(), info);
files_name.push(file.name().to_owned());
}
Expand All @@ -855,13 +868,14 @@ impl InboundRequest {
files: Some(files_name),
pin_code: self.state.pin_code.clone(),
text_description: None,
total_bytes,
..Default::default()
};

info!("Asking for user consent: {:?}", metadata);
self.update_state(
|e| {
e.transfer_metadata = Some(metadata.clone());
e.transfer_metadata = Some(metadata);
},
true,
);
Expand All @@ -885,7 +899,7 @@ impl InboundRequest {
self.update_state(
|e| {
e.text_payload = Some(TextPayloadInfo::Url(meta.payload_id()));
e.transfer_metadata = Some(metadata.clone());
e.transfer_metadata = Some(metadata);
},
true,
);
Expand All @@ -907,7 +921,7 @@ impl InboundRequest {
self.update_state(
|e| {
e.text_payload = Some(TextPayloadInfo::Text(meta.payload_id()));
e.transfer_metadata = Some(metadata.clone());
e.transfer_metadata = Some(metadata);
},
true,
);
Expand Down Expand Up @@ -940,7 +954,7 @@ impl InboundRequest {
meta.payload_id(),
meta.ssid().to_owned(),
)));
e.transfer_metadata = Some(metadata.clone());
e.transfer_metadata = Some(metadata);
},
true,
);
Expand Down Expand Up @@ -1305,6 +1319,7 @@ impl InboundRequest {
return;
}

trace!("Sending msg into the channel");
let _ = self.sender.send(ChannelMessage {
id: self.state.id.clone(),
direction: ChannelDirection::LibToFront,
Expand Down
2 changes: 2 additions & 0 deletions core_lib/src/hdl/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ pub struct TransferMetadata {
pub pin_code: Option<String>,
pub text_description: Option<String>,
pub text_payload: Option<String>,
pub total_bytes: i64,
pub ack_bytes: i64,
}
7 changes: 7 additions & 0 deletions core_lib/src/manager.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Duration;

use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use tokio::net::{TcpListener, TcpStream};
Expand Down Expand Up @@ -46,6 +48,7 @@ impl TcpServer {

pub async fn run(&mut self, ctk: CancellationToken) -> Result<(), anyhow::Error> {
info!("{INNER_NAME}: service starting");
let sanity_wait_time = Duration::from_micros(10);

loop {
let cctk = ctk.clone();
Expand Down Expand Up @@ -94,6 +97,10 @@ impl TcpServer {
}
},
}
// Add a small sleep timer to allow the Tokio runtime to have
// some spare time to process channel's message. Otherwise it
// get spammed by new requests. Currently set to 10 micro secs.
tokio::time::sleep(sanity_wait_time).await;
}
});
},
Expand Down
31 changes: 31 additions & 0 deletions frontend/src/assets/main.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,35 @@ body,

.checkbox:checked, .checkbox[checked=true], .checkbox[aria-checked=true] {
@apply bg-green-200 border-green-200 text-black;
}

.circular-progress {
--size: 250px;
--half-size: calc(var(--size) / 2);
--stroke-width: 12px;
--radius: calc((var(--size) - var(--stroke-width)) / 2);
--circumference: calc(var(--radius) * pi * 2);
--dash: calc((var(--progress) * var(--circumference)) / 100);
}

.circular-progress circle {
cx: var(--half-size);
cy: var(--half-size);
r: var(--radius);
stroke-width: var(--stroke-width);
fill: none;
stroke-linecap: round;
}

.circular-progress circle.bg {
stroke: #ddd;
}

.circular-progress circle.fg {
transform: rotate(-90deg);
transform-origin: var(--half-size) var(--half-size);
stroke-dasharray: var(--dash) calc(var(--circumference) - var(--dash));
transition: stroke-dasharray 0.3s linear 0s;

@apply stroke-black
}
74 changes: 45 additions & 29 deletions frontend/src/components/HomePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -167,37 +167,45 @@
v-for="item in displayedItems" :key="item.id" class="w-full rounded-3xl flex flex-row gap-6 p-4 mb-4 bg-green-100"
:class="{'cursor-pointer': item.endpoint}" @click="item.endpoint && sendInfo(item.id)">
<div>
<div class="h-16 w-16 rounded-full bg-white">
<div class="relative w-[62px] h-[62px]">
<svg
v-if="item.state === 'Finished'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M268-240 42-466l57-56 170 170 56 56-57 56Zm226 0L268-466l56-57 170 170 368-368 56 57-424 424Zm0-226-57-56 198-198 57 56-198 198Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Laptop'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M0-160v-80h160v-40q-33 0-56.5-23.5T80-360v-400q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v400q0 33-23.5 56.5T800-280v40h160v80H0Zm160-200h640v-400H160v400Zm0 0v-400 400Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Phone'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M280-40q-33 0-56.5-23.5T200-120v-720q0-33 23.5-56.5T280-920h400q33 0 56.5 23.5T760-840v720q0 33-23.5 56.5T680-40H280Zm0-120v40h400v-40H280Zm0-80h400v-480H280v480Zm0-560h400v-40H280v40Zm0 0v-40 40Zm0 640v40-40Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Tablet'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M120-160q-33 0-56.5-23.5T40-240v-480q0-33 23.5-56.5T120-800h720q33 0 56.5 23.5T920-720v480q0 33-23.5 56.5T840-160H120Zm40-560h-40v480h40v-480Zm80 480h480v-480H240v480Zm560-480v480h40v-480h-40Zm0 0h40-40Zm-640 0h-40 40Z" />
</svg>
<svg
v-else xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M280-160H160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640v80H160v480h120v80Zm160-100q25 0 42.5-17.5T500-320q0-25-17.5-42.5T440-380q-25 0-42.5 17.5T380-320q0 25 17.5 42.5T440-260Zm-80 100v-71q-19-17-29.5-40T320-320q0-26 10.5-49t29.5-40v-71h160v71q19 17 29.5 40t10.5 49q0 26-10.5 49T520-231v71H360Zm480 0H640q-17 0-28.5-11.5T600-200v-360q0-17 11.5-28.5T640-600h200q17 0 28.5 11.5T880-560v360q0 17-11.5 28.5T840-160Zm-160-80h120v-280H680v280Zm0 0h120-120Z" />
v-if="item.ack_bytes" width="62" height="62" viewBox="0 0 250 250"
class="circular-progress" :style="getProgress(item)">
<circle class="bg" />
<circle class="fg" />
</svg>
<div class="h-14 w-14 rounded-full bg-white absolute top-0 left-0 bottom-0 right-0 m-auto">
<svg
v-if="item.state === 'Finished'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M268-240 42-466l57-56 170 170 56 56-57 56Zm226 0L268-466l56-57 170 170 368-368 56 57-424 424Zm0-226-57-56 198-198 57 56-198 198Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Laptop'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M0-160v-80h160v-40q-33 0-56.5-23.5T80-360v-400q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v400q0 33-23.5 56.5T800-280v40h160v80H0Zm160-200h640v-400H160v400Zm0 0v-400 400Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Phone'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M280-40q-33 0-56.5-23.5T200-120v-720q0-33 23.5-56.5T280-920h400q33 0 56.5 23.5T760-840v720q0 33-23.5 56.5T680-40H280Zm0-120v40h400v-40H280Zm0-80h400v-480H280v480Zm0-560h400v-40H280v40Zm0 0v-40 40Zm0 640v40-40Z" />
</svg>
<svg
v-else-if="item.deviceType === 'Tablet'" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M120-160q-33 0-56.5-23.5T40-240v-480q0-33 23.5-56.5T120-800h720q33 0 56.5 23.5T920-720v480q0 33-23.5 56.5T840-160H120Zm40-560h-40v480h40v-480Zm80 480h480v-480H240v480Zm560-480v480h40v-480h-40Zm0 0h40-40Zm-640 0h-40 40Z" />
</svg>
<svg
v-else xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960"
width="24" class="w-full h-full p-4">
<!-- eslint-disable-next-line -->
<path d="M280-160H160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640v80H160v480h120v80Zm160-100q25 0 42.5-17.5T500-320q0-25-17.5-42.5T440-380q-25 0-42.5 17.5T380-320q0 25 17.5 42.5T440-260Zm-80 100v-71q-19-17-29.5-40T320-320q0-26 10.5-49t29.5-40v-71h160v71q19 17 29.5 40t10.5 49q0 26-10.5 49T520-231v71H360Zm480 0H640q-17 0-28.5-11.5T600-200v-360q0-17 11.5-28.5T640-600h200q17 0 28.5 11.5T880-560v360q0 17-11.5 28.5T840-160Zm-160-80h120v-280H680v280Zm0 0h120-120Z" />
</svg>
</div>
</div>

<p
Expand Down Expand Up @@ -355,6 +363,8 @@ interface DisplayedItem {
files?: string[],
text_description?: string,
destination?: string,
total_bytes?: number,
ack_bytes?: number,
}
const visibilityToNumber: { [key in Visibility]: number } = {
Expand Down Expand Up @@ -568,6 +578,8 @@ export default {
destination: el.meta?.destination ?? el.meta?.text_payload ?? undefined,
files: el.meta?.files ?? undefined,
text_description: el.meta?.text_description ?? undefined,
ack_bytes: (el.meta?.ack_bytes as number | undefined) ?? undefined,
total_bytes: (el.meta?.total_bytes as number | undefined) ?? undefined,
};
if (idx !== -1) {
Expand Down Expand Up @@ -702,6 +714,10 @@ export default {
this.discoveryRunning = true;
})
},
getProgress: function(item: DisplayedItem): string {
const value = item.ack_bytes! / item.total_bytes! * 100;
return `--progress: ${value}`;
},
openDownloadPicker: function() {
dialog.open({
title: "Select the destination for files",
Expand Down

0 comments on commit f154e90

Please sign in to comment.