mirror of
https://github.com/nekename/OpenDeck.git
synced 2026-10-28 18:02:23 +00:00
Implement instance-level settings
This commit is contained in:
1 parent
adfdd9e9d7
commit
50908effc7
9 files changed
+165
-52
No files matched your search
@@ -1,11 +1,16 @@
|
||||
use serde::Deserialize;
|
||||
use super::PayloadEvent;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RegisterEvent {
|
||||
pub uuid: String
|
||||
}
|
||||
use tauri::{api::shell::open, Manager};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OpenUrlEvent {
|
||||
pub url: String
|
||||
}
|
||||
|
||||
pub async fn open_url(event: PayloadEvent<OpenUrlEvent>) -> Result<(), anyhow::Error> {
|
||||
let app_handle = crate::APP_HANDLE.lock().await;
|
||||
open(&app_handle.as_ref().unwrap().shell_scope(), event.payload.url, None)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,31 +1,54 @@
|
||||
pub mod misc;
|
||||
mod misc;
|
||||
mod settings;
|
||||
|
||||
use crate::shared::ActionContext;
|
||||
|
||||
use serde::Deserialize;
|
||||
use tauri::{api::shell::open, Manager};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "event", content = "payload")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum InboundEvent {
|
||||
OpenUrl(misc::OpenUrlEvent)
|
||||
pub struct RegisterEvent {
|
||||
pub uuid: String
|
||||
}
|
||||
|
||||
async fn open_url(event: misc::OpenUrlEvent) -> Result<(), anyhow::Error> {
|
||||
let app_handle = crate::APP_HANDLE.lock().await;
|
||||
open(&app_handle.as_ref().unwrap().shell_scope(), event.url, None)?;
|
||||
Ok(())
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContextEvent {
|
||||
pub context: ActionContext
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PayloadEvent<T> {
|
||||
pub payload: T
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContextAndPayloadEvent<T> {
|
||||
pub context: ActionContext,
|
||||
pub payload: T
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "event")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum InboundEventType {
|
||||
OpenUrl(PayloadEvent<misc::OpenUrlEvent>),
|
||||
SetSettings(ContextAndPayloadEvent<serde_json::Value>),
|
||||
GetSettings(ContextEvent)
|
||||
}
|
||||
|
||||
pub async fn process_incoming_message(data: tokio_tungstenite::tungstenite::Message) -> Result<(), tokio_tungstenite::tungstenite::Error> {
|
||||
if let tokio_tungstenite::tungstenite::Message::Text(text) = data {
|
||||
let decoded: InboundEvent = match serde_json::from_str(&text) {
|
||||
let decoded: InboundEventType = match serde_json::from_str(&text) {
|
||||
Ok(event) => event,
|
||||
Err(_) => return Ok(())
|
||||
};
|
||||
|
||||
let _ = match decoded {
|
||||
InboundEvent::OpenUrl(event) => open_url(event).await
|
||||
};
|
||||
if let Err(error) = match decoded {
|
||||
InboundEventType::OpenUrl(event) => misc::open_url(event).await,
|
||||
InboundEventType::SetSettings(event) => settings::set_settings(event).await,
|
||||
InboundEventType::GetSettings(event) => settings::get_settings(event).await
|
||||
} {
|
||||
eprintln!("Failed to process incoming event: {}\n\tCaused by: {}", error, error.root_cause())
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
pub async fn set_settings(event: super::ContextAndPayloadEvent<serde_json::Value>) -> Result<(), anyhow::Error> {
|
||||
let (
|
||||
app,
|
||||
mut device_stores,
|
||||
devices,
|
||||
mut profile_stores
|
||||
) = crate::store::profiles::lock_mutexes().await;
|
||||
|
||||
let selected_profile = &device_stores.get_device_store(&event.context.device, app.as_ref().unwrap())?.value.selected_profile;
|
||||
let device = devices.get(&event.context.device).unwrap();
|
||||
let store = profile_stores.get_profile_store(device, selected_profile, app.as_ref().unwrap())?;
|
||||
let profile = &mut store.value;
|
||||
|
||||
let instance = match event.context.controller.as_str() {
|
||||
"Encoder" => profile.sliders[event.context.position as usize].as_mut(),
|
||||
_ => profile.keys[event.context.position as usize].as_mut()
|
||||
};
|
||||
|
||||
if let Some(instance) = instance {
|
||||
instance.settings = event.payload;
|
||||
store.save()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_settings(event: super::ContextEvent) -> Result<(), anyhow::Error> {
|
||||
if let Some(instance) = crate::store::profiles::get_instance(
|
||||
&event.context.device,
|
||||
event.context.position,
|
||||
&event.context.controller
|
||||
).await? {
|
||||
crate::events::outbound::settings::did_receive_settings(event.context, &instance).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -16,7 +16,7 @@ lazy_static! {
|
||||
}
|
||||
|
||||
/// Register a plugin to send and receive events with its WebSocket.
|
||||
pub async fn register_plugin(data: inbound::misc::RegisterEvent, stream: WebSocketStream<TcpStream>) {
|
||||
pub async fn register_plugin(data: inbound::RegisterEvent, stream: WebSocketStream<TcpStream>) {
|
||||
let (read, write) = stream.split();
|
||||
tokio::spawn(write.try_for_each(inbound::process_incoming_message));
|
||||
SOCKETS.lock().await.insert(data.uuid.clone(), read);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::{Coordinates, send_to_plugin, get_instance};
|
||||
use super::{Coordinates, send_to_plugin};
|
||||
|
||||
use crate::shared::ActionContext;
|
||||
use crate::store::profiles::get_instance;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -29,7 +30,7 @@ pub async fn dial_rotate(device: String, index: u8, ticks: i16) -> Result<(), an
|
||||
|
||||
send_to_plugin(&instance.action.plugin, DialRotateEvent {
|
||||
action: instance.action.uuid.clone(),
|
||||
event: "keyDown".to_owned(),
|
||||
event: "dialRotate".to_owned(),
|
||||
context: instance.context.clone(),
|
||||
device: instance.context.device.clone(),
|
||||
payload: DialRotatePayload {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::{Coordinates, send_to_plugin, get_instance};
|
||||
use super::{Coordinates, send_to_plugin};
|
||||
|
||||
use crate::shared::{ActionContext, ActionInstance};
|
||||
use crate::store::profiles::get_instance;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
pub mod keypad;
|
||||
pub mod encoder;
|
||||
pub mod settings;
|
||||
|
||||
use super::SOCKETS;
|
||||
|
||||
use crate::APP_HANDLE;
|
||||
use crate::devices::DEVICES;
|
||||
use crate::shared::ActionInstance;
|
||||
use crate::store::profiles::{DEVICE_STORES, PROFILE_STORES};
|
||||
|
||||
use serde::Serialize;
|
||||
use futures_util::SinkExt;
|
||||
|
||||
@@ -22,26 +18,3 @@ async fn send_to_plugin(plugin: &str, data: impl Serialize) -> Result<(), anyhow
|
||||
SOCKETS.lock().await.get_mut(plugin).unwrap().send(message).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_instance(device: &str, index: u8, controller: &str) -> Result<Option<ActionInstance>, anyhow::Error> {
|
||||
let app = APP_HANDLE.lock().await;
|
||||
let app = app.as_ref().unwrap();
|
||||
|
||||
let mut device_stores = DEVICE_STORES.lock().await;
|
||||
let selected_profile = &device_stores.get_device_store(device, app)?.value.selected_profile;
|
||||
|
||||
let devices = DEVICES.lock().await;
|
||||
let device = devices.get(device).unwrap();
|
||||
|
||||
let mut profile_stores = PROFILE_STORES.lock().await;
|
||||
let profile = &profile_stores.get_profile_store(device, selected_profile, app)?.value;
|
||||
|
||||
let configured = match controller {
|
||||
"Encoder" => profile.sliders[index as usize].as_ref(),
|
||||
_ => profile.keys[index as usize].as_ref()
|
||||
};
|
||||
match configured {
|
||||
Some(configured) => Ok(Some(configured.clone())),
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use super::{Coordinates, send_to_plugin};
|
||||
|
||||
use crate::shared::ActionContext;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DidReceiveSettingsPayload {
|
||||
settings: serde_json::Value,
|
||||
coordinates: Coordinates
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DidReceiveSettings {
|
||||
event: String,
|
||||
action: String,
|
||||
context: ActionContext,
|
||||
device: String,
|
||||
payload: DidReceiveSettingsPayload
|
||||
}
|
||||
|
||||
pub async fn did_receive_settings(context: crate::shared::ActionContext, instance: &crate::shared::ActionInstance) -> Result<(), anyhow::Error> {
|
||||
send_to_plugin(&instance.action.plugin, DidReceiveSettings {
|
||||
event: "didReceiveSettings".to_owned(),
|
||||
action: instance.action.uuid.clone(),
|
||||
context: context.clone(),
|
||||
device: context.device.clone(),
|
||||
payload: DidReceiveSettingsPayload {
|
||||
settings: instance.settings.clone(),
|
||||
coordinates: Coordinates {
|
||||
row: instance.context.position / 3,
|
||||
column: instance.context.position % 3
|
||||
}
|
||||
}
|
||||
}).await
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use serde::{Serialize, Deserialize};
|
||||
|
||||
use anyhow::Context;
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
||||
pub struct ProfileStores {
|
||||
stores: HashMap<String, Store<Profile>>
|
||||
@@ -101,3 +101,40 @@ lazy_static! {
|
||||
/// A singleton object to manage Store instances for device configurations.
|
||||
pub static ref DEVICE_STORES: Mutex<DeviceStores> = Mutex::new(DeviceStores { stores: HashMap::new() });
|
||||
}
|
||||
|
||||
pub async fn lock_mutexes() -> (
|
||||
MutexGuard<'static, Option<tauri::AppHandle>>,
|
||||
MutexGuard<'static, DeviceStores>,
|
||||
MutexGuard<'static, HashMap<String, crate::devices::DeviceInfo>>,
|
||||
MutexGuard<'static, ProfileStores>
|
||||
) {
|
||||
let app = crate::APP_HANDLE.lock().await;
|
||||
let device_stores = DEVICE_STORES.lock().await;
|
||||
let devices = crate::devices::DEVICES.lock().await;
|
||||
let profile_stores = PROFILE_STORES.lock().await;
|
||||
(app, device_stores, devices, profile_stores)
|
||||
}
|
||||
|
||||
pub async fn get_instance(device: &str, position: u8, controller: &str) -> Result<Option<crate::shared::ActionInstance>, anyhow::Error> {
|
||||
let (
|
||||
app,
|
||||
mut device_stores,
|
||||
devices,
|
||||
mut profile_stores
|
||||
) = lock_mutexes().await;
|
||||
|
||||
let selected_profile = &device_stores.get_device_store(device, app.as_ref().unwrap())?.value.selected_profile;
|
||||
let device = devices.get(device).unwrap();
|
||||
let store = profile_stores.get_profile_store(device, selected_profile, app.as_ref().unwrap())?;
|
||||
let profile = &store.value;
|
||||
|
||||
let configured = match controller {
|
||||
"Encoder" => profile.sliders[position as usize].as_ref(),
|
||||
_ => profile.keys[position as usize].as_ref()
|
||||
};
|
||||
|
||||
match configured {
|
||||
Some(configured) => Ok(Some(configured.clone())),
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user