Skip to content

Commit

Permalink
feat(weg): add live reload to weg items
Browse files Browse the repository at this point in the history
  • Loading branch information
eythaann committed Aug 7, 2024
1 parent 3b854be commit dcdc619
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 11 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- improvements on system color detection and expose more system colors based in accent gamma.
- improve theme creation experience by adding live reload feature.
- improve toolbar layouts (placeholders) creation experience by adding live reload feature.
- improve weg items editor experience by adding live reload feature.

### refactor
- deprecate `onClick` and add new `onClickV2` on toolbar modules.
Expand Down
18 changes: 17 additions & 1 deletion src/apps/seelenweg/modules/shared/store/infra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { SwItemType, SwSavedItem } from '../../../../shared/schemas/SeelenWegIte
import { Theme } from '../../../../shared/schemas/Theme';
import { updateHitbox } from '../../../events';
import i18n from '../../../i18n';
import { loadPinnedItems } from './storeApi';
import { IsSavingPinnedItems, loadPinnedItems } from './storeApi';
import { configureStore } from '@reduxjs/toolkit';
import { listen as listenGlobal } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import { debounce } from 'lodash';

import { SwPinnedAppUtils } from '../../item/app/PinnedApp';
import { SwTemporalAppUtils } from '../../item/app/TemporalApp';
Expand Down Expand Up @@ -111,6 +112,21 @@ export async function registerStoreEvents() {
const userSettings = await new UserSettingsLoader().load();
loadThemeCSS(userSettings);
});

await listenGlobal<unknown>(
'weg-items',
debounce(async () => {
if (IsSavingPinnedItems.current) {
IsSavingPinnedItems.current = false;
return;
}

const apps = await loadPinnedItems();
store.dispatch(RootActions.setItemsOnLeft(await cleanSavedItems(apps.left)));
store.dispatch(RootActions.setItemsOnCenter(await cleanSavedItems(apps.center)));
store.dispatch(RootActions.setItemsOnRight(await cleanSavedItems(apps.right)));
}, 100),
);
}

function loadSettingsCSS(settings: Seelenweg) {
Expand Down
17 changes: 9 additions & 8 deletions src/apps/seelenweg/modules/shared/store/storeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import {
SwSaveFileSchema,
} from '../../../../shared/schemas/SeelenWegItems';
import { path } from '@tauri-apps/api';
import { exists, readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
import { invoke } from '@tauri-apps/api/core';
import { writeTextFile } from '@tauri-apps/plugin-fs';
import yaml from 'js-yaml';
import { debounce } from 'lodash';

import { store } from './infra';

import { RootState, SwItem } from './domain';

export const IsSavingPinnedItems = {
current: false,
};

export const savePinnedItems = debounce(
async (state: RootState = store.getState()): Promise<void> => {
const cb = (acc: SwSavedItem[], item: SwItem) => {
Expand Down Expand Up @@ -40,17 +45,13 @@ export const savePinnedItems = debounce(
};

const yaml_route = await path.join(await path.appDataDir(), 'seelenweg_items.yaml');
IsSavingPinnedItems.current = true;
await writeTextFile(yaml_route, yaml.dump(data));
},
1000,
);

export const loadPinnedItems = async (): Promise<SwSaveFile> => {
let yaml_route = await path.join(await path.appDataDir(), 'seelenweg_items.yaml');

if (!(await exists(yaml_route))) {
return SwSaveFileSchema.parse({});
}

return SwSaveFileSchema.parse(yaml.load(await readTextFile(yaml_route)));
let items = await invoke<json>('state_get_weg_items');
return SwSaveFileSchema.parse(items || {});
};
2 changes: 1 addition & 1 deletion src/apps/shared/schemas/SeelenWegItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const SwSavedItemSchema = z.union([
StartMenuItemSchema,
]);

export type SwSaveFile = z.infer<typeof SwSaveFileSchema>;
export interface SwSaveFile extends z.infer<typeof SwSaveFileSchema> {}
export const SwSaveFileSchema = z.object({
left: z.array(SwSavedItemSchema).default([
{
Expand Down
1 change: 1 addition & 0 deletions src/background/exposed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ pub fn register_invoke_handler(app_builder: Builder<Wry>) -> Builder<Wry> {
get_auto_start_status,
state_get_themes,
state_get_placeholders,
state_get_weg_items,
// Media
media_prev,
media_toggle_play_pause,
Expand Down
91 changes: 91 additions & 0 deletions src/background/state/application/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use lazy_static::lazy_static;
use notify::{RecursiveMode, Watcher};
use parking_lot::Mutex;
use std::sync::Arc;
use tauri::{AppHandle, Emitter, Manager};

use crate::{error_handler::Result, log_error, seelen::get_app_handle};

use super::domain::WegItems;

lazy_static! {
pub static ref FULL_STATE: Arc<Mutex<FullState>> = Arc::new(Mutex::new(
FullState::new().expect("Failed to create placeholders manager")
));
}

pub struct FullState {
handle: AppHandle<tauri::Wry>,
weg_items: WegItems,
}

impl FullState {
fn new() -> Result<Self> {
let mut manager = Self {
handle: get_app_handle(),
weg_items: serde_yaml::Value::Null,
};
manager.load_all()?;
manager.start_listeners()?;
Ok(manager)
}

pub fn weg_items(&self) -> &WegItems {
&self.weg_items
}

fn start_listeners(&mut self) -> Result<()> {
let (tx, rx) = crossbeam_channel::unbounded();

let mut watcher = notify::recommended_watcher(tx)?;

let data_dir = self.handle.path().app_data_dir()?;
let weg_items_path = data_dir.join("seelenweg_items.yaml");

watcher.watch(&data_dir, RecursiveMode::Recursive)?;

std::thread::spawn(move || {
let _watcher = watcher;
for event in rx {
match event {
Ok(event) => {
if event.paths.contains(&weg_items_path) {
log::info!("Weg Items changed: {:?}", weg_items_path);
let mut manager = FULL_STATE.lock();
log_error!(manager.load_weg_items());
log_error!(manager.emit_weg_items());
}
}
Err(e) => log::error!("Placeholder watcher error: {:?}", e),
}
}
});

log::info!("Seelen UI Data Watcher started!");
Ok(())
}

fn load_weg_items(&mut self) -> Result<()> {
let dir = self.handle.path().app_data_dir()?;
let path = dir.join("seelenweg_items.yaml");

self.weg_items = if !path.exists() {
serde_yaml::Value::Null
} else {
serde_yaml::from_str(&std::fs::read_to_string(&path)?)?
};

Ok(())
}

fn load_all(&mut self) -> Result<()> {
self.load_weg_items()?;
Ok(())
}

fn emit_weg_items(&self) -> Result<()> {
let handle = get_app_handle();
handle.emit("weg-items", self.weg_items())?;
Ok(())
}
}
4 changes: 4 additions & 0 deletions src/background/state/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ pub struct Placeholder {
pub center: Vec<serde_yaml::Value>,
pub right: Vec<serde_yaml::Value>,
}

// ============== WEG ==============

pub type WegItems = serde_yaml::Value;
8 changes: 7 additions & 1 deletion src/background/state/infrastructure.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use itertools::Itertools;

use super::{
domain::{Placeholder, Theme},
application::FULL_STATE,
domain::{Placeholder, Theme, WegItems},
placeholders::PLACEHOLDERS_MANAGER,
themes::THEME_MANAGER,
};
Expand All @@ -25,3 +26,8 @@ pub fn state_get_placeholders() -> Vec<Placeholder> {
.cloned()
.collect_vec()
}

#[tauri::command]
pub fn state_get_weg_items() -> WegItems {
FULL_STATE.lock().weg_items().clone()
}
1 change: 1 addition & 0 deletions src/background/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod application;
mod domain;
pub mod infrastructure;
mod placeholders;
Expand Down

0 comments on commit dcdc619

Please sign in to comment.