Files
spiceflow/client/src/lib/stores/push.ts
2026-01-20 14:04:19 -05:00

144 lines
2.8 KiB
TypeScript

import { writable, derived } from 'svelte/store';
import {
isPushSupported,
getPermissionStatus,
isSubscribed,
subscribe as pushSubscribe,
unsubscribe as pushUnsubscribe
} from '$lib/push';
export type PushState = 'unsupported' | 'default' | 'denied' | 'subscribed' | 'unsubscribed';
interface PushStore {
supported: boolean;
permission: NotificationPermission;
subscribed: boolean;
loading: boolean;
error: string | null;
}
function createPushStore() {
const store = writable<PushStore>({
supported: false,
permission: 'default',
subscribed: false,
loading: false,
error: null
});
const { subscribe: storeSubscribe, set, update } = store;
return {
subscribe: storeSubscribe,
/**
* Initialize the push store state
*/
async init() {
const supported = isPushSupported();
if (!supported) {
set({
supported: false,
permission: 'denied',
subscribed: false,
loading: false,
error: null
});
return;
}
const permission = getPermissionStatus();
const subscribed = await isSubscribed();
set({
supported,
permission,
subscribed,
loading: false,
error: null
});
},
/**
* Subscribe to push notifications
*/
async enablePush() {
update((s) => ({ ...s, loading: true, error: null }));
try {
await pushSubscribe();
update((s) => ({
...s,
subscribed: true,
permission: 'granted',
loading: false
}));
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to subscribe';
update((s) => ({
...s,
loading: false,
error: message,
permission: getPermissionStatus()
}));
throw err;
}
},
/**
* Unsubscribe from push notifications
*/
async disablePush() {
update((s) => ({ ...s, loading: true, error: null }));
try {
await pushUnsubscribe();
update((s) => ({
...s,
subscribed: false,
loading: false
}));
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to unsubscribe';
update((s) => ({
...s,
loading: false,
error: message
}));
throw err;
}
},
/**
* Toggle subscription state
*/
async toggle() {
const state = await new Promise<PushStore>((resolve) => {
const unsub = storeSubscribe((s) => {
resolve(s);
unsub();
});
});
if (state.subscribed) {
await this.disablePush();
} else {
await this.enablePush();
}
}
};
}
export const pushStore = createPushStore();
/**
* Derived store for the overall push state
*/
export const pushState = derived(pushStore, ($push): PushState => {
if (!$push.supported) return 'unsupported';
if ($push.permission === 'denied') return 'denied';
if ($push.subscribed) return 'subscribed';
if ($push.permission === 'granted') return 'unsubscribed';
return 'default';
});