Skip to content

Commit

Permalink
wip: web version progress
Browse files Browse the repository at this point in the history
  • Loading branch information
gmallios committed Jul 10, 2024
1 parent 79fcb84 commit a82e8e1
Show file tree
Hide file tree
Showing 49 changed files with 814 additions and 6,554 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
rustflags = ["--cfg=web_sys_unstable_apis"]
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,7 @@ test_video.mp4

# old cli directories
/tooling/cli.js
/tooling/cli.rs
/tooling/cli.rs

# dist
manager-ui/dist/
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
resolver = "2"
members = [
"manager-app",
"manager-fut",
"manager-wasm",
"soundcore-lib",
"test_data"
"test_data",
]

[workspace.package]
authors = ["Grigoris Mallios <[email protected]>"]
license = "GPL-3.0-or-later"
edition = "2021"

Expand All @@ -20,9 +22,18 @@ env_logger = { version = "0.11" }
thiserror = { version = "1.0.40" }
tokio = { version = "1" }
serde = { version = "1" }
serde_json = { version = "1.0" }
async-trait = { version = "0.1" }
futures = { version = "0.3" }
wasm-bindgen-futures = { version = "0.4" }
js-sys = { version = "0.3" }
web-sys = { version = "0.3" }
console_error_panic_hook = { version = "0.1.7" }
serde-wasm-bindgen = { version = "0.6" }
uuid = { version = "1.6.1" }
soundcore-lib = { path = "./soundcore-lib", default-features = false }
manager-fut = { path = "./manager-fut" }

[profile.release]
lto = true
opt-level = "s"
5 changes: 4 additions & 1 deletion manager-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ tauri = { version = "1.2.5", features = [
"system-tray",
] }
soundcore-lib = { workspace = true, features = ["btleplug-backend"] }
manager-fut = { workspace = true }

[dev-dependencies]
soundcore-lib = { workspace = true, features = ["mock"], default-features = false }
soundcore-lib = { workspace = true, features = [
"mock",
], default-features = false }

[features]
# by default Tauri runs in production mode
Expand Down
14 changes: 7 additions & 7 deletions manager-app/src/async_bridge/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use std::sync::Arc;

use log::{debug, info, trace};

use manager_fut::TokioFuture;
use tokio::sync::{mpsc, Mutex};

use soundcore_lib::{
ble::{BLEConnection, BLEConnectionManager},
ble::BLEConnectionManager,
device::SoundcoreBLEDevice,
device_manager::{create_device_manager, DeviceManager},
error::SoundcoreLibError, models::EQConfiguration,
models::EQConfiguration,
};

use super::{
Expand All @@ -17,11 +18,11 @@ use super::{
};

struct CommandLoopState<B: BLEConnectionManager> {
manager: DeviceManager<B>,
manager: DeviceManager<B, TokioFuture>,
}

impl<B: BLEConnectionManager> CommandLoopState<B> {
fn new(manager: DeviceManager<B>) -> Self {
fn new(manager: DeviceManager<B, TokioFuture>) -> Self {
Self { manager }
}
}
Expand Down Expand Up @@ -176,20 +177,19 @@ async fn handle_command<B: BLEConnectionManager>(
}

async fn handle_set_eq<B: BLEConnectionManager>(
device: Arc<SoundcoreBLEDevice<<B as BLEConnectionManager>::Connection>>,
device: Arc<SoundcoreBLEDevice<<B as BLEConnectionManager>::Connection, TokioFuture>>,
wrapped_payload: AddrWrappedPayload<SetEqualizerPayload>,
) -> BridgeResponse {
let eq_configuration = match wrapped_payload.payload {
SetEqualizerPayload::SetCustomEqualizer(eq) => EQConfiguration::mono_custom(eq),
SetEqualizerPayload::SetEqualizerPreset(profile) => {
EQConfiguration::stereo_with_profile(profile)
},
}
};

match device.set_eq(eq_configuration).await {
Ok(_) => BridgeResponse::EqualizerUpdated(wrapped_payload.addr),
Err(e) => BridgeResponse::GenericError(e.to_string()),

}
}

Expand Down
17 changes: 17 additions & 0 deletions manager-fut/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "manager-fut"
version = "0.1.0"
license.workspace = true
edition.workspace = true

[dependencies]
futures = { workspace = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true, features = ["sync", "time", "rt", "rt-multi-thread"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { workspace = true, features = ["sync", "rt", "macros"] }
js-sys = { workspace = true }
web-sys = { workspace = true, features = ["Window"] }
wasm-bindgen-futures = { workspace = true }
22 changes: 22 additions & 0 deletions manager-fut/src/future.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::time::Duration;

use futures::Future;

pub trait ManagerFuture {
type JoinHandle: ManagerJoinHandle;

fn spawn<F, O>(f: F) -> Self::JoinHandle
where
F: Future<Output = O> + Send + 'static,
O: Send + 'static;

fn spawn_local(fut: impl Future + 'static) -> Self::JoinHandle;
async fn sleep(dur: Duration);
async fn timeout<F, T>(dur: Duration, fut: F) -> Result<T, ()>
where
F: Future<Output = T>;
}

pub trait ManagerJoinHandle {
fn abort(&self);
}
15 changes: 15 additions & 0 deletions manager-fut/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mod future;

pub use future::*;

#[cfg(target_arch = "wasm32")]
mod wasm;

#[cfg(target_arch = "wasm32")]
pub use wasm::*;

#[cfg(not(target_arch = "wasm32"))]
mod tokio;

#[cfg(not(target_arch = "wasm32"))]
pub use tokio::*;
53 changes: 53 additions & 0 deletions manager-fut/src/tokio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::future::Future;
use std::time::Duration;

use tokio::task::JoinHandle;

use crate::{ManagerFuture, ManagerJoinHandle};

pub struct TokioFuture;

impl ManagerFuture for TokioFuture {
type JoinHandle = TokioJoinHandle;

fn spawn<F, O>(f: F) -> Self::JoinHandle
where
F: Future<Output = O> + Send + 'static,
O: Send + 'static,
{
TokioJoinHandle {
handle: tokio::task::spawn(async move {
f.await;
}),
}
}

fn spawn_local(f: impl Future + 'static) -> Self::JoinHandle {
TokioJoinHandle {
handle: tokio::task::spawn_local(async move {
f.await;
}),
}
}

async fn sleep(dur: Duration) {
tokio::time::sleep(dur).await;
}

async fn timeout<F, T>(dur: Duration, fut: F) -> Result<T, ()>
where
F: Future<Output = T>,
{
tokio::time::timeout(dur, fut)

Check failure on line 41 in manager-fut/src/tokio.rs

View workflow job for this annotation

GitHub Actions / Run cargo check

mismatched types

Check failure on line 41 in manager-fut/src/tokio.rs

View workflow job for this annotation

GitHub Actions / Run tests with stable Rust

mismatched types
}
}

pub struct TokioJoinHandle {
handle: JoinHandle<()>,
}

impl ManagerJoinHandle for TokioJoinHandle {
fn abort(&self) {
self.handle.abort();
}
}
85 changes: 85 additions & 0 deletions manager-fut/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::{future::Future, rc::Rc, time::Duration};
use futures::pin_mut;
use js_sys::Promise;
use tokio::{select, sync::Notify};
use wasm_bindgen_futures::JsFuture;
use web_sys::window;

use crate::{ManagerFuture, ManagerJoinHandle};

pub struct WasmFuture;

impl ManagerFuture for WasmFuture {
type JoinHandle = WasmJoinHandle;

fn spawn<F, O>(f: F) -> Self::JoinHandle
where
F: Future<Output = O> + Send + 'static,
O: Send + 'static,
{
Self::spawn_local(f)
}

fn spawn_local(fut: impl Future + 'static) -> Self::JoinHandle {
let handle = WasmJoinHandle {
quit: Rc::new(Default::default()),
};

let quit = handle.quit.clone();

wasm_bindgen_futures::spawn_local(async move {
select! {
_ = fut => (),
_ = quit.notified() => (),
}
});
handle
}

async fn sleep(dur: Duration) {
JsFuture::from(Promise::new(&mut move |resolve, _reject| {
window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(
&resolve,
dur.as_millis() as i32,
)
.unwrap();
}))
.await
.unwrap();
}

async fn timeout<F, T>(dur: Duration, fut: F) -> Result<T, ()>
where
F: Future<Output = T>,
{
let sleep_future = JsFuture::from(Promise::new(&mut move |resolve, _reject| {
window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(
&resolve,
dur.as_millis() as i32,
)
.unwrap();
}));

pin_mut!(fut);
pin_mut!(sleep_future);

select! {
result = fut => Ok(result),
_ = sleep_future => Err(()),
}
}
}

pub struct WasmJoinHandle {
quit: Rc<Notify>,
}

impl ManagerJoinHandle for WasmJoinHandle {
fn abort(&self) {
self.quit.notify_one();
}
}
4 changes: 0 additions & 4 deletions manager-ui/dist/.gitignore

This file was deleted.

4 changes: 4 additions & 0 deletions manager-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
"@types/node": "^18.7.10",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.0.6",
"@types/web-bluetooth": "^0.0.20",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@vitejs/plugin-react": "^4.0.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
Expand All @@ -54,6 +56,8 @@
"prettier": "^3.2.5",
"typescript": "^5.0.4",
"vite": "^5.1.5",
"vite-plugin-top-level-await": "^1.4.1",
"vite-plugin-wasm": "^3.3.0",
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^1.4.0"
}
Expand Down
10 changes: 5 additions & 5 deletions manager-ui/src/App.tsx → manager-ui/src/TauriApp.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import React, { useEffect } from 'react';
import './App.css';
import { useSoundcoreStore } from './stores/useSoundcoreStore';
import { useTauriManagerStore } from '@stores/tauri/useTauriManagerStore';
import { BluetoothSearchScreen } from './screens/bluetoothSearch';
import { useAsyncBridgeEvent, useAsyncBridgeRequest } from './hooks/useAsyncBridge';
import { useShallow } from 'zustand/react/shallow';
import { DeviceStateScreen } from '@screens/deviceState';

export const App: React.FC = () => {
export const TauriApp: React.FC = () => {
const [isFirstRender, setFirstRender] = React.useState(true);
const [handleAsyncBridgeEvent, connectedAddresses] = useSoundcoreStore(
const [handleAsyncBridgeEvent, connectedAddresses] = useTauriManagerStore(
useShallow((state) => [state.handleAsyncBridgeEvent, state.connectedAddresses])
);

const state = useSoundcoreStore((state) => state);
const state = useTauriManagerStore((state) => state);
console.log('state', state);

// Add the event listener to the bridge, which listener is
Expand All @@ -36,4 +36,4 @@ export const App: React.FC = () => {
);
};

export default App;
export default TauriApp;
Loading

0 comments on commit a82e8e1

Please sign in to comment.