114 lines
2.6 KiB
TypeScript
114 lines
2.6 KiB
TypeScript
import { ConvexHttpClient } from 'convex/browser';
|
|
import { getContext, setContext } from 'svelte';
|
|
import type { FunctionReference, FunctionArgs, FunctionReturnType } from 'convex/server';
|
|
|
|
const POLLING_CONTEXT_KEY = 'convex-polling';
|
|
const POLL_INTERVAL = 1000;
|
|
|
|
type PollingContext = {
|
|
client: ConvexHttpClient;
|
|
};
|
|
|
|
export function hasWebSocketSupport(): boolean {
|
|
if (typeof window === 'undefined') return true;
|
|
try {
|
|
return 'WebSocket' in window && typeof WebSocket !== 'undefined';
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function setupPollingConvex(url: string): void {
|
|
const client = new ConvexHttpClient(url);
|
|
setContext<PollingContext>(POLLING_CONTEXT_KEY, { client });
|
|
}
|
|
|
|
export function usePollingClient(): ConvexHttpClient {
|
|
const ctx = getContext<PollingContext>(POLLING_CONTEXT_KEY);
|
|
if (!ctx) {
|
|
throw new Error('Convex polling client not set up. Call setupPollingConvex first.');
|
|
}
|
|
return ctx.client;
|
|
}
|
|
|
|
type QueryState<T> = {
|
|
data: T | undefined;
|
|
error: Error | null;
|
|
isLoading: boolean;
|
|
};
|
|
|
|
export function usePollingMutation<Mutation extends FunctionReference<'mutation'>>(
|
|
mutation: Mutation
|
|
): (args: FunctionArgs<Mutation>) => Promise<FunctionReturnType<Mutation>> {
|
|
const client = usePollingClient();
|
|
return (args: FunctionArgs<Mutation>) => client.mutation(mutation, args);
|
|
}
|
|
|
|
export function usePollingQuery<Query extends FunctionReference<'query'>>(
|
|
query: Query,
|
|
argsGetter: () => FunctionArgs<Query> | 'skip'
|
|
): {
|
|
data: FunctionReturnType<Query> | undefined;
|
|
error: Error | null;
|
|
isLoading: boolean;
|
|
} {
|
|
const client = usePollingClient();
|
|
|
|
// eslint-disable-next-line prefer-const
|
|
let state = $state<QueryState<FunctionReturnType<Query>>>({
|
|
data: undefined,
|
|
error: null,
|
|
isLoading: true
|
|
});
|
|
|
|
let intervalId: ReturnType<typeof setInterval> | null = null;
|
|
let lastArgsJson = '';
|
|
|
|
async function poll() {
|
|
const args = argsGetter();
|
|
if (args === 'skip') {
|
|
state.isLoading = false;
|
|
return;
|
|
}
|
|
|
|
const argsJson = JSON.stringify(args);
|
|
if (argsJson !== lastArgsJson) {
|
|
state.isLoading = true;
|
|
lastArgsJson = argsJson;
|
|
}
|
|
|
|
try {
|
|
const result = await client.query(query, args);
|
|
state.data = result;
|
|
state.error = null;
|
|
state.isLoading = false;
|
|
} catch (err) {
|
|
state.error = err instanceof Error ? err : new Error(String(err));
|
|
state.isLoading = false;
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
poll();
|
|
intervalId = setInterval(poll, POLL_INTERVAL);
|
|
|
|
return () => {
|
|
if (intervalId) {
|
|
clearInterval(intervalId);
|
|
}
|
|
};
|
|
});
|
|
|
|
return {
|
|
get data() {
|
|
return state.data;
|
|
},
|
|
get error() {
|
|
return state.error;
|
|
},
|
|
get isLoading() {
|
|
return state.isLoading;
|
|
}
|
|
};
|
|
}
|