mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 12:13:27 +00:00
core events
Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
parent
122ef2b374
commit
fd350a51bd
7
apps/desktop/src-tauri/Cargo.lock
generated
7
apps/desktop/src-tauri/Cargo.lock
generated
|
@ -3399,6 +3399,7 @@ dependencies = [
|
|||
"sqlx",
|
||||
"strum 0.21.0",
|
||||
"swift-rs",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"walkdir",
|
||||
]
|
||||
|
@ -3777,6 +3778,7 @@ dependencies = [
|
|||
"swift-rs",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4417,11 +4419,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.13.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee"
|
||||
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"memchr",
|
||||
"num_cpus",
|
||||
|
|
|
@ -35,6 +35,7 @@ sha256 = "1.0.2"
|
|||
once_cell = "1.8.0"
|
||||
int-enum = "0.4.0"
|
||||
async-std = "1.10.0"
|
||||
tokio = {version = "1.15.0", features= ["sync"] }
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
use anyhow::Result;
|
||||
use futures::{
|
||||
stream::{self, StreamExt},
|
||||
Stream,
|
||||
use futures::{stream::StreamExt, Stream};
|
||||
use sdcorelib::{
|
||||
core_send_stream,
|
||||
db::connection::db_instance,
|
||||
file::{icon, indexer, retrieve, retrieve::Directory},
|
||||
get_core_config, native, CoreConfig,
|
||||
};
|
||||
use sdcorelib::db::connection::db_instance;
|
||||
use sdcorelib::file::{indexer, retrieve, retrieve::Directory};
|
||||
use sdcorelib::native;
|
||||
use sdcorelib::AppConfig;
|
||||
use serde::Serialize;
|
||||
use std::fs;
|
||||
use swift_rs::types::SRObjectArray;
|
||||
|
||||
pub fn reply<T: Serialize>(window: &tauri::Window, data: T) {
|
||||
let _message = window.emit("message", data).map_err(|e| println!("{}", e));
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub async fn scan_dir(window: tauri::Window, path: String) -> Result<(), String> {
|
||||
pub async fn scan_dir(path: String) -> Result<(), String> {
|
||||
db_instance().await?;
|
||||
// reply(&window, GlobalEventKind::JEFF, "jeff");
|
||||
|
||||
let files = indexer::scan(&path).await.map_err(|e| e.to_string())?;
|
||||
|
||||
|
@ -31,10 +23,12 @@ pub async fn scan_dir(window: tauri::Window, path: String) -> Result<(), String>
|
|||
pub async fn get_files(path: String) -> Result<Directory, String> {
|
||||
Ok(retrieve::get_dir_with_contents(&path).await?)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub async fn get_config() -> Result<&'static AppConfig, String> {
|
||||
Ok(&sdcorelib::APP_CONFIG.get().unwrap())
|
||||
pub async fn get_config() -> Result<&'static CoreConfig, String> {
|
||||
Ok(get_core_config())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_mounts() -> Result<SRObjectArray<native::methods::Mount>, String> {
|
||||
Ok(native::methods::get_mounts())
|
||||
|
@ -49,74 +43,9 @@ pub async fn test_scan() -> Result<(), String> {
|
|||
)
|
||||
}
|
||||
|
||||
// #[tauri::command(async)]
|
||||
// pub async fn get_file_thumb(path: &str) -> Result<String, String> {
|
||||
// // let thumbnail_b64 = get_file_thumbnail_base64(path).to_string();
|
||||
|
||||
// Ok(thumbnail_b64)
|
||||
// }
|
||||
|
||||
// #[tauri::command(async)]
|
||||
// pub async fn get_thumbs_for_directory(window: tauri::Window, path: &str) -> Result<(), String> {
|
||||
// let config = CONFIG.get().unwrap();
|
||||
|
||||
// let thumbnails = retrieve::get_thumbs_for_directory(path, &config).await?;
|
||||
|
||||
// // ....
|
||||
// }
|
||||
|
||||
async fn stream_reply<T: Stream<Item = sdcorelib::ClientEvent>>(window: &tauri::Window, stream: T) {
|
||||
stream
|
||||
.for_each(|event| async { reply(window, event) })
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub async fn get_thumbs_for_directory(window: tauri::Window, path: &str) -> Result<(), String> {
|
||||
let thumbs = get_thumbs_for_directory_impl(path).await;
|
||||
|
||||
stream_reply(&window, thumbs).await;
|
||||
pub async fn get_thumbs_for_directory(path: &str) -> Result<(), String> {
|
||||
core_send_stream(icon::get_thumbs_for_directory(path).await).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_thumbs_for_directory_impl(
|
||||
path: &str,
|
||||
) -> impl Stream<Item = sdcorelib::ClientEvent> {
|
||||
let dir = retrieve::get_dir_with_contents(&path).await.unwrap();
|
||||
|
||||
stream::iter(dir.contents.into_iter()).filter_map(|file| async {
|
||||
let config = &sdcorelib::APP_CONFIG.get().unwrap();
|
||||
let icon_name = format!(
|
||||
"{}.png",
|
||||
if file.is_dir {
|
||||
"folder".to_owned()
|
||||
} else {
|
||||
file.extension
|
||||
}
|
||||
);
|
||||
let icon_path = config.file_type_thumb_dir.join(icon_name);
|
||||
// extract metadata from file
|
||||
let existing = fs::metadata(&icon_path).is_ok();
|
||||
// write thumbnail only if
|
||||
if !existing {
|
||||
// call swift to get thumbnail data
|
||||
let thumbnail_b64 =
|
||||
sdcorelib::native::methods::get_file_thumbnail_base64(&file.uri).to_string();
|
||||
fs::write(
|
||||
&icon_path,
|
||||
base64::decode(thumbnail_b64).unwrap_or_default(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !existing {
|
||||
Some(sdcorelib::ClientEvent::NewFileTypeThumb {
|
||||
icon_created: true,
|
||||
file_id: file.id,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,13 +2,24 @@ mod commands;
|
|||
mod menu;
|
||||
use sdcorelib;
|
||||
use tauri::api::path;
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
let data_dir = path::data_dir().unwrap_or(std::path::PathBuf::from("./"));
|
||||
sdcorelib::configure(data_dir);
|
||||
|
||||
tauri::Builder::default()
|
||||
.setup(|_app| Ok(()))
|
||||
.setup(|app| {
|
||||
let data_dir = path::data_dir().unwrap_or(std::path::PathBuf::from("./"));
|
||||
let mut core_receiver = sdcorelib::configure(data_dir);
|
||||
|
||||
let app = app.handle();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(event) = core_receiver.recv().await {
|
||||
app.emit_all("core_event", &event).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.on_menu_event(|event| menu::handle_menu_event(event))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::scan_dir,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { SettingsScreen } from './screens/Settings';
|
|||
import { ExplorerScreen } from './screens/Explorer';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { DebugGlobalStore } from './Debug';
|
||||
import { useGlobalEvents } from './hooks/useGlobalEvents';
|
||||
import { useCoreEvents } from './hooks/useCoreEvents';
|
||||
import { AppState, useAppState } from './store/global';
|
||||
import { Modal } from './components/layout/Modal';
|
||||
import { useKey, useKeyBindings } from 'rooks';
|
||||
|
@ -41,7 +41,7 @@ function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
|||
}
|
||||
|
||||
export default function App() {
|
||||
useGlobalEvents();
|
||||
useCoreEvents();
|
||||
useEffect(() => {
|
||||
invoke<AppState>('get_config').then((state) => useAppState.getState().update(state));
|
||||
invoke<Location[]>('get_mounts').then((locations) =>
|
||||
|
|
|
@ -7,9 +7,9 @@ export interface RustEvent {
|
|||
data: any;
|
||||
}
|
||||
|
||||
export function useGlobalEvents() {
|
||||
export function useCoreEvents() {
|
||||
useEffect(() => {
|
||||
listen('message', (e: Event<RustEvent>) => {
|
||||
listen('core_event', (e: Event<RustEvent>) => {
|
||||
console.log({ e });
|
||||
|
||||
switch (e.payload?.kind) {
|
10
packages/core/Cargo.lock
generated
10
packages/core/Cargo.lock
generated
|
@ -1668,6 +1668,7 @@ dependencies = [
|
|||
"sqlx",
|
||||
"strum",
|
||||
"swift-rs",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"walkdir",
|
||||
]
|
||||
|
@ -2180,6 +2181,15 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
|
|
|
@ -41,3 +41,4 @@ sea-orm = { version = "^0.4.2", features = [ "sqlx-sqlite", "runtime-async-std-r
|
|||
walkdir = "^2.3.2"
|
||||
bytesize = "1.1.0"
|
||||
|
||||
tokio = {version = "1.15.0", features=["sync"]}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::APP_CONFIG;
|
||||
use crate::get_core_config;
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rusqlite::Connection;
|
||||
|
@ -8,7 +8,7 @@ use sea_orm::{Database, DatabaseConnection, DbErr};
|
|||
// use std::str::FromStr;
|
||||
|
||||
pub async fn get_connection() -> Result<DatabaseConnection, DbErr> {
|
||||
let config = &APP_CONFIG.get().unwrap();
|
||||
let config = get_core_config();
|
||||
|
||||
let db_url = format!("{}{}", "sqlite://", config.primary_db.to_str().unwrap());
|
||||
|
||||
|
@ -29,7 +29,7 @@ pub async fn db_instance() -> Result<&'static DatabaseConnection, String> {
|
|||
}
|
||||
|
||||
pub async fn create_primary_db() -> Result<(), sqlx::Error> {
|
||||
let config = &APP_CONFIG.get().unwrap();
|
||||
let config = get_core_config();
|
||||
|
||||
let db_url = config.primary_db.to_str().unwrap();
|
||||
// establish connection, this is only used to create the db if missing
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
use crate::{file::retrieve, get_core_config, native, ClientEvent};
|
||||
|
||||
use futures::{
|
||||
stream::{self, StreamExt},
|
||||
Stream,
|
||||
};
|
||||
use std::fs;
|
||||
|
||||
pub async fn get_thumbs_for_directory(window: tauri::Window, path: &str) -> Result<(), String> {
|
||||
let config = config::get_config();
|
||||
let dir = filesystem::retrieve::get_dir_with_contents(&path).await?;
|
||||
// iterate over directory contents
|
||||
for file in dir.contents.into_iter() {
|
||||
let now = Instant::now();
|
||||
let icon_name = format!(
|
||||
"{}.png",
|
||||
if file.is_dir {
|
||||
"folder".to_owned()
|
||||
} else {
|
||||
file.extension
|
||||
}
|
||||
);
|
||||
let icon_path = config.file_type_thumb_dir.join(icon_name);
|
||||
// extract metadata from file
|
||||
let existing = fs::metadata(&icon_path).is_ok();
|
||||
// write thumbnail only if
|
||||
if !existing {
|
||||
// call swift to get thumbnail data
|
||||
let thumbnail_b64 = get_file_thumbnail_base64(&file.uri).to_string();
|
||||
fs::write(
|
||||
&icon_path,
|
||||
base64::decode(thumbnail_b64).unwrap_or_default(),
|
||||
)
|
||||
.map_err(|_| "thumb_cache_failure")?;
|
||||
}
|
||||
println!("cached thumb {:?} in {:?}", file.id, now.elapsed());
|
||||
pub async fn get_thumbs_for_directory(path: &str) -> impl Stream<Item = ClientEvent> {
|
||||
let dir = retrieve::get_dir_with_contents(&path).await.unwrap();
|
||||
|
||||
if !existing {
|
||||
reply(
|
||||
&window,
|
||||
GlobalEventKind::FileTypeThumb,
|
||||
GenFileTypeIconsResponse {
|
||||
icon_created: true,
|
||||
file_id: file.id,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
stream::iter(dir.contents.into_iter()).filter_map(|file| async {
|
||||
let config = get_core_config();
|
||||
let icon_name = format!(
|
||||
"{}.png",
|
||||
if file.is_dir {
|
||||
"folder".to_owned()
|
||||
} else {
|
||||
file.extension
|
||||
}
|
||||
);
|
||||
let icon_path = config.file_type_thumb_dir.join(icon_name);
|
||||
// extract metadata from file
|
||||
let existing = fs::metadata(&icon_path).is_ok();
|
||||
// write thumbnail only if
|
||||
if !existing {
|
||||
// call swift to get thumbnail data
|
||||
let thumbnail_b64 = native::methods::get_file_thumbnail_base64(&file.uri).to_string();
|
||||
fs::write(
|
||||
&icon_path,
|
||||
base64::decode(thumbnail_b64).unwrap_or_default(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
if !existing {
|
||||
Some(ClientEvent::NewFileTypeThumb {
|
||||
icon_created: true,
|
||||
file_id: file.id,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod checksum;
|
||||
// pub mod icon;
|
||||
pub mod icon;
|
||||
pub mod indexer;
|
||||
pub mod init;
|
||||
pub mod retrieve;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use futures::{stream::StreamExt, Stream};
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use tokio::sync::mpsc;
|
||||
use ts_rs::TS;
|
||||
|
||||
pub mod crypto;
|
||||
pub mod db;
|
||||
|
@ -9,42 +12,69 @@ pub mod native;
|
|||
pub mod util;
|
||||
use futures::executor::block_on;
|
||||
|
||||
trait Emitter {
|
||||
fn emit(&self, event: ClientEvent, data: &str) -> Result<(), String>;
|
||||
}
|
||||
|
||||
// static configuration
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct AppConfig {
|
||||
pub struct CoreConfig {
|
||||
pub data_dir: std::path::PathBuf,
|
||||
pub primary_db: std::path::PathBuf,
|
||||
pub file_type_thumb_dir: std::path::PathBuf,
|
||||
// pub emitter: Box<dyn Emitter>,
|
||||
}
|
||||
|
||||
pub struct Core {
|
||||
pub config: CoreConfig,
|
||||
pub event_channel_sender: mpsc::Sender<ClientEvent>,
|
||||
}
|
||||
|
||||
// represents an event this library can emit
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, TS)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
#[ts(export)]
|
||||
pub enum ClientEvent {
|
||||
NewFileTypeThumb { file_id: u32, icon_created: bool },
|
||||
}
|
||||
|
||||
pub static APP_CONFIG: OnceCell<AppConfig> = OnceCell::new();
|
||||
pub static CORE: OnceCell<Core> = OnceCell::new();
|
||||
|
||||
pub fn configure(mut data_dir: std::path::PathBuf) {
|
||||
pub fn get_core_config() -> &'static CoreConfig {
|
||||
&CORE.get().unwrap().config
|
||||
}
|
||||
|
||||
pub async fn core_send(event: ClientEvent) {
|
||||
CORE.get()
|
||||
.unwrap()
|
||||
.event_channel_sender
|
||||
.send(event)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn core_send_stream<T: Stream<Item = ClientEvent>>(stream: T) {
|
||||
stream
|
||||
.for_each(|event| async {
|
||||
core_send(event).await;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn configure(mut data_dir: std::path::PathBuf) -> mpsc::Receiver<ClientEvent> {
|
||||
data_dir = data_dir.join("spacedrive");
|
||||
|
||||
let config = AppConfig {
|
||||
let (event_sender, event_receiver) = mpsc::channel(100);
|
||||
|
||||
let config = CoreConfig {
|
||||
data_dir: data_dir.clone(),
|
||||
primary_db: data_dir.clone().join("primary.db3"),
|
||||
file_type_thumb_dir: data_dir.clone().join("file_icons"),
|
||||
};
|
||||
|
||||
APP_CONFIG.set(config);
|
||||
CORE.set(Core {
|
||||
config: config,
|
||||
event_channel_sender: event_sender,
|
||||
});
|
||||
|
||||
// create the data directories if not present
|
||||
fs::create_dir_all(&APP_CONFIG.get().unwrap().data_dir).unwrap();
|
||||
fs::create_dir_all(&APP_CONFIG.get().unwrap().file_type_thumb_dir).unwrap();
|
||||
fs::create_dir_all(&get_core_config().data_dir).unwrap();
|
||||
fs::create_dir_all(&get_core_config().file_type_thumb_dir).unwrap();
|
||||
|
||||
// create primary data base if not exists
|
||||
block_on(db::connection::create_primary_db()).unwrap();
|
||||
|
@ -52,6 +82,8 @@ pub fn configure(mut data_dir: std::path::PathBuf) {
|
|||
block_on(file::init::init_library()).unwrap();
|
||||
|
||||
println!("Spacedrive daemon online");
|
||||
|
||||
event_receiver
|
||||
}
|
||||
|
||||
// pub static MAIN_WINDOW: OnceCell<> = OnceCell::new();
|
||||
|
|
Loading…
Reference in a new issue