tauri-mw-store
Version:
Declarative multi-window state management for Tauri apps with React hooks, schema-driven persistence, and automatic synchronization.
1 lines • 6.26 kB
JavaScript
import {listen,emit}from'@tauri-apps/api/event';import {Store}from'@tauri-apps/plugin-store';import {Window}from'@tauri-apps/api/window';import {useState,useEffect}from'react';var g=class{constructor(e="multi-window-store.bin"){this.store=null;this.initPromise=null;this.fileName=e;}async ensureStore(){return this.store?this.store:this.initPromise?this.initPromise:(this.initPromise=Store.load(this.fileName).then(e=>(this.store=e,e)),this.initPromise)}async load(e){console.log("PersistentAdapter load",e);let t=await this.ensureStore(),s={};for(let o of e){let r=await t.get(o);r!=null&&(s[o]=r);}return console.log("PersistentAdapter load",s),s}async save(e,t){let s=await this.ensureStore();await s.set(e,t),await s.save();}async remove(e){let t=await this.ensureStore();await t.delete(e),await t.save();}async clear(){let e=await this.ensureStore();await e.clear(),await e.save();}async getAllKeys(){return await(await this.ensureStore()).keys()}async removeBatch(e){let t=await this.ensureStore();for(let s of e)await t.delete(s);await t.save();}async cleanupOrphanedKeys(e){let s=(await this.getAllKeys()).filter(o=>!e.has(o));return s.length>0&&(console.log(`\u6E05\u7406\u5B64\u7ACB\u7684\u6301\u4E45\u5316\u952E: ${s.join(", ")}`),await this.removeBatch(s)),s}},d=new g;var S=class{constructor(){this.listeners=new Map;}addListener(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.removeListener(e,t)}removeListener(e,t){this.listeners.get(e)?.delete(t);}notify(e,t){this.listeners.get(e)?.forEach(s=>s(t));}},u=new S;var h=class{constructor(e,t){this.state={};this.initialState={};this.immediateKeys=new Set;this.onCloseKeys=new Set;this.windowLabel=Window.getCurrent().label,this.initialState={...e},this.state={...e},this.autoCleanup=t?.autoCleanup??true,t&&(this.immediateKeys=new Set(t.immediateKeys||[]),this.onCloseKeys=new Set(t.onCloseKeys||[]));}async loadPersistedState(e){if(e.size>0){let t=await d.load(e);this.state={...this.state,...t};}}notifyPersistedKeys(e){for(let t of e)u.notify(t,this.state[t]);}async hydrate(){let e=new Set([...this.immediateKeys,...this.onCloseKeys]);if(this.autoCleanup)try{let t=await d.cleanupOrphanedKeys(e);t.length>0&&console.log(`\u5DF2\u6E05\u7406 ${t.length} \u4E2A\u5B64\u7ACB\u7684\u6301\u4E45\u5316\u952E`);}catch(t){console.warn("\u6E05\u7406\u5B64\u7ACB\u952E\u65F6\u51FA\u9519:",t);}e.size>0&&(await this.loadPersistedState(e),this.notifyPersistedKeys(e)),this.onCloseKeys.size>0&&this.setupWindowCloseListener();}get(e){return this.state[e]}getDefaultValue(e){return this.initialState[e]}async requestSync(e){return new Promise(t=>{let s=null,o=setTimeout(()=>{s&&s(),t(this.state[e]);},1e3);listen("store-sync-response",r=>{let{key:a,value:n,targetWindow:l}=r.payload;a===e&&l===this.windowLabel&&(clearTimeout(o),s&&s(),this.state[e]=n,u.notify(e,n),t(n));}).then(r=>{s=r;}),emit("store-sync-request",{key:e,requestWindow:this.windowLabel});})}async set(e,t,s="all"){this.state[e]=t,u.notify(e,t),await this.emitToOtherWindows(e,t,s),this.immediateKeys.has(e)&&await d.save(e,t);}async emitToOtherWindows(e,t,s){await emit("store-update",{key:e,value:t,sourceWindow:this.windowLabel,targetWindows:s});}async listenToIncomingUpdates(){listen("store-update",async e=>{let{key:t,value:s,sourceWindow:o,targetWindows:r}=e.payload;o!==this.windowLabel&&(r!=="all"&&!r.includes(this.windowLabel)||(this.state[t]=s,u.notify(t,s),this.immediateKeys.has(t)&&await d.save(t,s)));}),listen("store-sync-request",async e=>{let{key:t,requestWindow:s}=e.payload;s!==this.windowLabel&&await emit("store-sync-response",{key:t,value:this.state[t],sourceWindow:this.windowLabel,targetWindow:s});});}setupWindowCloseListener(){try{Window.getCurrent().onCloseRequested(async()=>{console.log("\u7A97\u53E3\u5373\u5C06\u5173\u95ED\uFF0C\u6B63\u5728\u4FDD\u5B58\u72B6\u6001..."),await this.saveOnCloseKeys();});}catch(e){console.warn("\u8BBE\u7F6E\u7A97\u53E3\u5173\u95ED\u76D1\u542C\u5931\u8D25:",e);}}async saveOnCloseKeys(){let e=[];for(let t of this.onCloseKeys)e.push(d.save(t,this.state[t]));await Promise.all(e);}async getAllStoredKeys(){return await d.getAllKeys()}},c=null;function y(){if(!c)throw new Error("StoreManager \u5C1A\u672A\u521B\u5EFA\uFF0C\u8BF7\u5148\u8C03\u7528 defineStore \u521D\u59CB\u5316");return c}async function C(i,e){c||(c=new h(i,e),await c.hydrate(),await c.listenToIncomingUpdates());}function f(i){return y().get(i)}function v(i){return y().getDefaultValue(i)}async function b(i){return y().requestSync(i)}async function T(i,e,t="all"){return y().set(i,e,t)}function P(i,e){return u.addListener(i,e)}async function A(){return y().getAllStoredKeys()}function x(i,e={}){let{syncOnMount:t=true}=e,[s,o]=useState(()=>v(i));return useEffect(()=>{let r=Window.getCurrent().label,a=u.addListener(i,n=>{console.log("useStoreState",r,i,n),o(n);});return t&&(r!=="main"?b(i):setTimeout(()=>{try{let n=f(i),l=v(i);n!==l&&(console.log("useStoreState \u4E3B\u7A97\u53E3\u68C0\u6D4B\u5230 hydrate \u540E\u7684\u503C",i,n),o(n));}catch(n){console.log("useStoreState \u4E3B\u7A97\u53E3\u68C0\u67E5\u72B6\u6001\u65F6\u51FA\u9519",n);}},0)),()=>a()},[i,t]),s}var V=i=>i;function M(i){return i.charAt(0).toUpperCase()+i.slice(1)}function $(i){let e={},t=[],s=[];Object.entries(i).forEach(([a,n])=>{typeof n=="object"&&n!==null&&"default"in n?(e[a]=n.default,n.persist&&(typeof n.persist=="object"&&(n.persist.saveStrategy||"onClose")==="immediate"?t.push(a):s.push(a))):e[a]=n;});async function o(){await C(e,{immediateKeys:t,onCloseKeys:s});}return Object.keys(i).reduce((a,n)=>{let l=M(n);return a[`get${l}`]=()=>f(n),a[`set${l}`]=(p,L)=>T(n,p,L??"all"),a[`use${l}`]=p=>x(n,p),a[`on${l}Change`]=p=>P(n,p),a},{initAppStore:o})}function U(i){return i}async function j(i){let e=Window.getCurrent().label,t=i[e];if(!t)return;let s=[],o;if(t.onInit&&(o=await t.onInit()),t.listeners)for(let[r,a]of Object.entries(t.listeners)){let n=await listen(r,()=>{o!=null?a(o):a();});s.push(()=>n());}if(t.emitOnInit?.length)for(let r of t.emitOnInit)typeof r=="string"?await emit(r):await emit(r.event,r.payload);return {dispose:()=>{s.forEach(r=>r());}}}export{$ as createMWStore,j as defineWindowEvents,A as getAllStoredKeys,V as storeConfig,U as window};