Skip to content

Commit 810d592

Browse files
committed
feat: hello node download
1 parent 333c155 commit 810d592

File tree

13 files changed

+170
-57
lines changed

13 files changed

+170
-57
lines changed

.env.example

Whitespace-only changes.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"devDependencies": {
2020
"@eslint/js": "^9.7.0",
2121
"@tauri-apps/cli": "^1",
22+
"@types/node": "^20.14.11",
2223
"@types/react": "^18.2.15",
2324
"@types/react-dom": "^18.2.7",
2425
"@vitejs/plugin-react": "^4.2.1",

pnpm-lock.yaml

Lines changed: 21 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
37.8 MB
Binary file not shown.

src-tauri/src/main.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33

44
mod system_utils;
55

6+
use std::thread;
7+
68
use lazy_static::lazy_static;
9+
use system_utils::executor::run_command_with_logging;
710
use tauri::Window;
811
use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu};
912
use tauri_plugin_autostart::MacosLauncher;
10-
use system_utils::executor::run_command_with_logging;
1113

1214
// create a mutex to store the main window
1315
lazy_static! {
@@ -23,17 +25,32 @@ fn logger(message: String, js_function: String) {
2325
}
2426
}
2527

26-
#[allow(non_snake_case)]
2728
#[tauri::command]
28-
fn executeCommand(command: String, args: Vec<String>) {
29-
// convert from Vec<String> to Vec<&str> (required by run_command_with_logging)
30-
let parsed_args: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
31-
match run_command_with_logging(command.as_str(), parsed_args.as_slice(), logger) {
32-
Ok(_) => println!("Command executed successfully"),
33-
Err(e) => println!("Failed to execute command: {}", e),
34-
}
35-
}
29+
fn execute_command(command: String, args: Vec<String>, emit_event: bool) {
30+
// Clone the command and args to pass them to the new thread
31+
let command_clone = command.clone();
32+
let args_clone = args.clone();
3633

34+
// Create a new thread to run the command in the background
35+
let handle = thread::spawn(move || {
36+
// convert from Vec<String> to Vec<&str> (required by run_command_with_logging)
37+
let parsed_args: Vec<&str> = args_clone.iter().map(|s| s.as_str()).collect();
38+
match run_command_with_logging(command_clone.as_str(), parsed_args.as_slice(), logger) {
39+
Ok(_) => {
40+
if emit_event {
41+
// emit event with the command executed
42+
let _ = MAIN_WINDOW
43+
.lock()
44+
.unwrap()
45+
.as_ref()
46+
.unwrap()
47+
.emit("command_executed", command);
48+
}
49+
}
50+
Err(e) => println!("Failed to execute command: {}", e),
51+
}
52+
});
53+
}
3754

3855
fn main() {
3956
// creating a menu for the system tray, with a quit option (add more options later if needed)
@@ -67,7 +84,11 @@ fn main() {
6784
_ => {}
6885
})
6986
// setting the system tray
70-
.system_tray(SystemTray::new().with_tooltip(tray_title).with_menu(system_tray_menu))
87+
.system_tray(
88+
SystemTray::new()
89+
.with_tooltip(tray_title)
90+
.with_menu(system_tray_menu),
91+
)
7192
// opening the main window when the system tray is clicked
7293
.on_system_tray_event(|app, event| match event {
7394
SystemTrayEvent::LeftClick {
@@ -94,7 +115,7 @@ fn main() {
94115
_ => {}
95116
})
96117
// tauri commands (to use in the frontend)
97-
.invoke_handler(tauri::generate_handler![executeCommand])
118+
.invoke_handler(tauri::generate_handler![execute_command])
98119
.run(tauri::generate_context!())
99120
.expect("error while running tauri application");
100121
}

src-tauri/src/system_utils/executor.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,33 @@ pub fn run_command_with_logging(
3636
let stderr = cmd.stderr.take().expect("Failed to open stderr");
3737

3838
let (tx, rx) = mpsc::channel();
39-
39+
4040
let tx_clone = tx.clone();
41-
thread::spawn(move || {
41+
let stdout_handle = thread::spawn(move || {
4242
let reader = BufReader::new(stdout);
4343
for line in reader.lines() {
4444
if let Ok(line) = line {
4545
tx_clone.send(line).unwrap();
4646
}
4747
}
4848
});
49-
50-
thread::spawn(move || {
49+
50+
let stderr_handle = thread::spawn(move || {
5151
let reader = BufReader::new(stderr);
5252
for line in reader.lines() {
5353
if let Ok(line) = line {
5454
tx.send(line).unwrap();
5555
}
5656
}
5757
});
58-
58+
5959
for line in rx {
6060
logger(line, "FunctionOutputLogger".to_string());
6161
}
6262

63+
stdout_handle.join().expect("Failed to join stdout thread");
64+
stderr_handle.join().expect("Failed to join stderr thread");
65+
6366
cmd.wait()?;
6467
Ok(())
6568
}

src/App.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
overflow-y: auto;
3333
resize: none;
3434
height: 200px;
35+
text-shadow: 1px 0px 0px #0000007e,
36+
0px 1px 0px #0000007e,
37+
-1px 0px 0px #0000007e,
38+
0px -1px 0px #0000007e;
39+
font-family: monospace;
3540

3641
&::-webkit-scrollbar {
3742
background: transparent;

src/App.tsx

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,71 @@ import { enable, isEnabled, disable } from "tauri-plugin-autostart-api";
33
import "./App.css";
44
import "./assets/styles/wave.css";
55
import "./assets/styles/card.css";
6-
import { ExecuteCommand } from "./utils/CommandUtils";
6+
import { ExecuteCommand, GetHelloNodeUrl, greet } from "./utils/CommandUtils";
7+
import { useLogs } from "./assets/globalHooks/useLogs";
8+
import { listen } from "@tauri-apps/api/event";
79

810
function App() {
911
const [enabledAutostart, setEnabledAutostart] = useState(false);
10-
const [logs, setLogs] = useState("");
11-
const executableurl =
12-
"https://github.com/Hello-Storage/hello-ipfs-user-node/releases/download/0.0.1/ipfs-user-node-windows-0.0.1.exe";
12+
const { logs, addLog } = useLogs();
13+
const [executableurl, setExecutableurl] = useState<string | undefined>();
14+
const [executing, setExecuting] = useState<string | undefined>();
15+
const [executed, setExecuted] = useState<string | undefined>();
1316

14-
async function greet() {
15-
await ExecuteCommand("echo", ["Hello there from hello.app!"]);
16-
}
17+
useEffect(() => {
18+
greet();
19+
//get the url of the hello node
20+
setExecutableurl(GetHelloNodeUrl());
21+
// detect if autostart is enabled
22+
isEnabled().then((e) => {
23+
if (e) {
24+
setEnabledAutostart(true);
25+
}
26+
});
27+
28+
//listen the event "command_executed"
29+
listen("command_executed", (event) => {
30+
setExecuted(event.payload as string);
31+
});
32+
}, []);
1733

1834
useEffect(() => {
1935
window.FunctionOutputLogger = function (msg: string) {
20-
// update logs
21-
setLogs(logs + msg + "\n");
22-
// scroll to bottom
23-
const textarea = document.getElementById("logs");
24-
if (textarea) {
25-
textarea.scrollTop = textarea.scrollHeight + 100;
26-
}
36+
addLog(msg);
2737
};
2838
}, [logs]);
2939

3040
useEffect(() => {
31-
greet();
32-
41+
if (!executableurl) return;
3342
// check if hello node is installed (from localstorage)
3443
let helloNodeInstalled = localStorage.getItem("hello-node-installed");
3544
if (!helloNodeInstalled) {
45+
setExecuting("curl");
3646
// install hello node
37-
ExecuteCommand("curl", ["-O", "-L", executableurl]).then(() => {
47+
ExecuteCommand(
48+
"curl",
49+
["-O", "-L", executableurl],
50+
true
51+
).then(() => {
3852
// update localstorage
39-
localStorage.setItem("hello-node-installed", "true");
53+
// localStorage.setItem("hello-node-installed", "true");
54+
addLog("Hello Node installed");
4055
});
41-
setLogs(logs + "Hello Node installed\n");
56+
addLog("Installing Hello Node");
4257
} else {
43-
setLogs(logs + "Hello Node already installed\n");
58+
addLog("Hello Node already installed");
4459
}
60+
}, [executableurl]);
4561

46-
// detect if autostart is enabled
47-
isEnabled().then((e) => {
48-
if (e) {
49-
setEnabledAutostart(true);
50-
}
51-
});
52-
//
53-
}, []);
62+
// detect if command (executing) is finished
63+
useEffect(() => {
64+
if (!executed) return;
65+
if(!executing) return;
66+
if(!executed.includes(executing)) return;
67+
setExecuting(undefined);
68+
setExecuted(undefined);
69+
addLog("Command waited: " + executed);
70+
}, [executed]);
5471

5572
async function switchAutostart() {
5673
if (await isEnabled()) {
@@ -74,7 +91,7 @@ function App() {
7491
</section>
7592

7693
<div className="bgblue">
77-
<div className="card">
94+
<div className="log-card">
7895
<textarea
7996
name="logs"
8097
id="logs"

src/assets/globalHooks/useLogs.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useState } from "react";
2+
3+
export function useLogs() {
4+
const [logs, setLogs] = useState("");
5+
6+
const addLog = (newLine: string) => {
7+
// update logs
8+
setLogs((logs) => logs + newLine + "\n");
9+
// scroll to bottom
10+
const textarea = document.getElementById("logs");
11+
if (textarea) {
12+
textarea.scrollTop = textarea.scrollHeight + 100;
13+
}
14+
}
15+
16+
return { logs, addLog };
17+
18+
}

src/assets/styles/card.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
box-shadow: 0px 1rem 1.5rem -0.9rem #000000e1;
66
}
77

8-
.card {
8+
.log-card {
99
font-size: 1rem;
1010
color: #bec4cf;
1111
background: linear-gradient(135deg, #3586ffc0 0%, #1e284b79 50%, #3586ffc0 100%);
12-
padding: 1.5rem;
12+
padding: 1rem;
1313
border-radius: 1.2rem;
1414
}

0 commit comments

Comments
 (0)