core events

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
Jamie 2021-12-28 02:20:00 -08:00
parent 122ef2b374
commit fd350a51bd
12 changed files with 138 additions and 153 deletions

View file

@ -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",

View file

@ -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" ]

View file

@ -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
}
})
}

View file

@ -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,

View file

@ -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) =>

View file

@ -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) {

View file

@ -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"

View file

@ -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"]}

View file

@ -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

View file

@ -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
}
})
}

View file

@ -1,5 +1,5 @@
pub mod checksum;
// pub mod icon;
pub mod icon;
pub mod indexer;
pub mod init;
pub mod retrieve;

View file

@ -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();