Skip to content

Instantly share code, notes, and snippets.

@samwightt
Last active September 23, 2024 15:03
Show Gist options
  • Select an option

  • Save samwightt/d66c8c6affee47115b6b458fe9cb36d1 to your computer and use it in GitHub Desktop.

Select an option

Save samwightt/d66c8c6affee47115b6b458fe9cb36d1 to your computer and use it in GitHub Desktop.
import {
Owner,
type Evolu,
type EvoluSchema,
type QueryResult,
type SyncState,
} from "@evolu/common";
export * from "@evolu/common";
export * from "@evolu/common-web";
import { type SelectQueryBuilder } from "kysely";
import { type Row } from "@evolu/common";
import {
ref,
type MaybeRef,
toValue,
watch,
computed,
onUnmounted,
type DeepReadonly,
type Ref,
} from "vue";
// I add an explicit 'q` function for subqueries. This allows you to define query functions with correct type inference that you can pass to
// `createQuery` later.
type QueryCallback<T extends EvoluSchema> = Parameters<
Evolu<T>["createQuery"]
>[0];
export type EvoluQueryCreator<T extends EvoluSchema> = Parameters<
QueryCallback<T>
>[0];
export const createVueApi = <T extends EvoluSchema>(evolu: Evolu<T>) => {
function makeQuery<A, I extends keyof A, R extends Row>(
callback: (db: EvoluQueryCreator<T>) => SelectQueryBuilder<A, I, R>
): (db: EvoluQueryCreator<T>) => SelectQueryBuilder<A, I, R> {
return callback;
}
function getEvolu(): Evolu<T> {
return inject("evolu", evolu);
}
function provideEvolu(e: Evolu<T>) {
provide("evolu", e);
}
function loadQuery<A, I extends keyof A, R extends Row>(
callback: MaybeRef<
(db: EvoluQueryCreator<T>) => SelectQueryBuilder<A, I, R>
>
): Ref<DeepReadonly<QueryResult<R>>> {
const evolu = getEvolu();
const data = ref<QueryResult<R>>({
row: null,
rows: [],
});
// Vue's built in get value function has a problem when passed functions for some reason.
const getValue = computed(() => {
if (typeof callback === "function") {
return callback;
}
return callback.value;
});
const compiledQuery = computed(() => evolu.createQuery(getValue.value));
watch(
compiledQuery,
async (query, _prev, onCleanup) => {
const result = await evolu.loadQuery<R>(query);
data.value = result;
console.log("Set up watch!");
const unsubscribe = evolu.subscribeQuery(query)(() => {
console.log("New query value!");
data.value = evolu.getQuery<R>(query);
});
// Have to use the passed onCleanup function because we need this to work async.
onCleanup(() => {
unsubscribe();
});
},
{
// Does not run initially unless we pass this (why??) lol
immediate: true,
}
);
return data as Ref<DeepReadonly<QueryResult<R>>>;
}
function loadOwner(): Ref<DeepReadonly<Owner | null>> {
const evolu = getEvolu();
const owner = evolu.getOwner();
const data = ref<Owner | null>(owner);
// Probably would be safer to put this in an onMount function but seems to work fine.
// Evolu doesn't work with SSR anyways.
const unsubscribe = evolu.subscribeOwner(() => {
data.value = evolu.getOwner();
});
onUnmounted(() => {
unsubscribe();
});
return data;
}
function useSyncState(): Ref<DeepReadonly<SyncState>> {
const evolu = getEvolu();
const state = evolu.getSyncState();
const data = ref<SyncState>(state);
const unsubscribe = evolu.subscribeSyncState(() => {
data.value = evolu.getSyncState();
});
onUnmounted(() => {
unsubscribe();
});
return data;
}
return {
q: makeQuery,
getQuery: loadQuery,
getEvolu,
provideEvolu,
getOwner: loadOwner,
useSyncState,
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment