144 lines
2.8 KiB
TypeScript
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';
|
|
});
|