mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Events keep saying "this happened"; commands say "do this" — single owner, dispatch resolves when the work finishes with the owner's result, reject codes unavailable/timeout (bus) and auth/quota/input (owners, via appCommandError). Homepage sections register ipinfo:refresh / connectivity:run / webrtc:run / dnsleak:run / speedtest:toggle; keyboard shortcuts, the refresh orchestrator, and Persona Check's dependency repair dispatch instead of reaching through template refs, and Home.vue sheds the test-section refs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
2.0 KiB
JavaScript
57 lines
2.0 KiB
JavaScript
// Minimal app-wide event bus for domain events — components emit what
|
|
// happened ("speed test finished", "whois lookup ran") and decoupled modules
|
|
// react to it. Framework-agnostic on purpose: no Vue imports, usable from any
|
|
// util / composable / component. Consumed by the achievement engine
|
|
// (composables/use-achievement-engine.js) and the report collector
|
|
// (composables/use-report-collector.js).
|
|
|
|
const listeners = new Map(); // event name → Set<handler>
|
|
|
|
// Subscribe to an event. Returns an unsubscribe function.
|
|
export const onAppEvent = (event, handler) => {
|
|
if (!listeners.has(event)) listeners.set(event, new Set());
|
|
listeners.get(event).add(handler);
|
|
return () => {
|
|
const set = listeners.get(event);
|
|
if (!set) return;
|
|
set.delete(handler);
|
|
if (set.size === 0) listeners.delete(event);
|
|
};
|
|
};
|
|
|
|
// One-shot subscription: resolves with the next payload of the event. Safe
|
|
// to call before triggering the producer — subscribe first, then invoke.
|
|
// With timeoutMs, rejects (code 'timeout') and unsubscribes if the event
|
|
// never fires.
|
|
export const waitForAppEvent = (event, { timeoutMs } = {}) => new Promise((resolve, reject) => {
|
|
let timer;
|
|
const off = onAppEvent(event, (payload) => {
|
|
clearTimeout(timer);
|
|
off();
|
|
resolve(payload);
|
|
});
|
|
if (timeoutMs) {
|
|
timer = setTimeout(() => {
|
|
off();
|
|
reject(Object.assign(
|
|
new Error(`[app-events] "${event}" did not fire within ${timeoutMs}ms`),
|
|
{ code: 'timeout' },
|
|
));
|
|
}, timeoutMs);
|
|
}
|
|
});
|
|
|
|
// Emit an event to all subscribers. Emitting is fire-and-forget: a throwing
|
|
// handler must not break the emitter or the remaining handlers.
|
|
export const emitAppEvent = (event, payload = {}) => {
|
|
const set = listeners.get(event);
|
|
if (!set) return;
|
|
for (const handler of [...set]) {
|
|
try {
|
|
handler(payload);
|
|
} catch (error) {
|
|
console.error(`[app-events] handler for "${event}" failed:`, error);
|
|
}
|
|
}
|
|
};
|