|
@@ -1,10 +1,27 @@
|
|
|
|
|
+use crate::app_state::AppState;
|
|
|
|
|
+use crate::poller;
|
|
|
|
|
+use crate::settings::SettingsStore;
|
|
|
|
|
+use crate::state::{PetState, PetStatePayload};
|
|
|
|
|
+use crate::token::TokenStore;
|
|
|
|
|
+use tauri::{AppHandle, Emitter, Manager, State};
|
|
|
|
|
+
|
|
|
#[tauri::command]
|
|
#[tauri::command]
|
|
|
-pub async fn on_interact(_app: tauri::AppHandle, _action: String, _data: Option<String>) -> Result<(), String> {
|
|
|
|
|
- Ok(())
|
|
|
|
|
|
|
+pub async fn on_interact(app: AppHandle, action: String, _data: Option<String>) -> Result<(), String> {
|
|
|
|
|
+ match action.as_str() {
|
|
|
|
|
+ "click" => complete_water(&app).await,
|
|
|
|
|
+ "pet" => {
|
|
|
|
|
+ let _ = app.emit("pet://bubble", serde_json::json!({"text": "嘻嘻~", "durationMs": 2000}));
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+ _ => Ok(()),
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
#[tauri::command]
|
|
|
-pub async fn drag_start(_app: tauri::AppHandle) -> Result<(), String> {
|
|
|
|
|
|
|
+pub async fn drag_start(app: AppHandle) -> Result<(), String> {
|
|
|
|
|
+ if let Some(win) = app.get_webview_window("main") {
|
|
|
|
|
+ win.start_dragging().map_err(|e| e.to_string())?;
|
|
|
|
|
+ }
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -14,15 +31,74 @@ pub async fn drag_end() -> Result<(), String> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
#[tauri::command]
|
|
|
-pub fn get_settings(_app: tauri::AppHandle) -> Result<serde_json::Value, String> {
|
|
|
|
|
- Ok(serde_json::json!({}))
|
|
|
|
|
|
|
+pub fn get_settings(
|
|
|
|
|
+ _state: State<AppState>,
|
|
|
|
|
+ settings: State<SettingsStore>,
|
|
|
|
|
+) -> Result<serde_json::Value, String> {
|
|
|
|
|
+ let cfg = settings.load();
|
|
|
|
|
+ let has_token = TokenStore::new().load().map(|t| t.is_some()).unwrap_or(false);
|
|
|
|
|
+ Ok(serde_json::json!({
|
|
|
|
|
+ "baseUrl": cfg.base_url,
|
|
|
|
|
+ "hasToken": has_token
|
|
|
|
|
+ }))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
#[tauri::command]
|
|
|
pub fn save_settings(
|
|
pub fn save_settings(
|
|
|
- _app: tauri::AppHandle,
|
|
|
|
|
- _base_url: String,
|
|
|
|
|
- _token: Option<String>,
|
|
|
|
|
|
|
+ app: AppHandle,
|
|
|
|
|
+ state: State<AppState>,
|
|
|
|
|
+ settings: State<SettingsStore>,
|
|
|
|
|
+ base_url: String,
|
|
|
|
|
+ token: Option<String>,
|
|
|
) -> Result<(), String> {
|
|
) -> Result<(), String> {
|
|
|
|
|
+ let mut cfg = settings.load();
|
|
|
|
|
+ cfg.base_url = base_url.trim().to_string();
|
|
|
|
|
+ settings.save(&cfg)?;
|
|
|
|
|
+
|
|
|
|
|
+ if let Some(t) = token {
|
|
|
|
|
+ if !t.trim().is_empty() {
|
|
|
|
|
+ TokenStore::new().save(t.trim())?;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新运行时客户端并重启轮询(对齐现 ShowSettings 的 Stop/Start)
|
|
|
|
|
+ state.api.set_base_url(cfg.base_url.clone());
|
|
|
|
|
+ if let Some(t) = TokenStore::new().load()? {
|
|
|
|
|
+ state.api.update_token(t);
|
|
|
|
|
+ }
|
|
|
|
|
+ restart_polling(&app, &state);
|
|
|
|
|
+
|
|
|
|
|
+ if let Some(win) = app.get_webview_window("settings") {
|
|
|
|
|
+ let _ = win.hide();
|
|
|
|
|
+ }
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+fn restart_polling(app: &AppHandle, state: &State<AppState>) {
|
|
|
|
|
+ if let Some(h) = state.poll_handle.lock().unwrap().take() {
|
|
|
|
|
+ h.abort();
|
|
|
|
|
+ }
|
|
|
|
|
+ let handle = poller::spawn(app.clone(), state.api.clone());
|
|
|
|
|
+ *state.poll_handle.lock().unwrap() = Some(handle);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async fn complete_water(app: &AppHandle) -> Result<(), String> {
|
|
|
|
|
+ let state = app.state::<AppState>();
|
|
|
|
|
+ let api = state.api.clone();
|
|
|
|
|
+ match api.complete_water().await {
|
|
|
|
|
+ Ok(_) => {
|
|
|
|
|
+ let payload = PetStatePayload {
|
|
|
|
|
+ state: PetState::Happy,
|
|
|
|
|
+ reason: Some("drink_complete".into()),
|
|
|
|
|
+ data: None,
|
|
|
|
|
+ };
|
|
|
|
|
+ let _ = app.emit("pet://state", payload);
|
|
|
|
|
+ let _ = app.emit("pet://bubble", serde_json::json!({"text": "喝水打卡成功!太棒了~", "durationMs": 4000}));
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+ Err(_) => {
|
|
|
|
|
+ let _ = app.emit("pet://bubble", serde_json::json!({"text": "打卡失败,请稍后再试", "durationMs": 4000}));
|
|
|
|
|
+ Err("checkin failed".into())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|