[READ-ONLY] Mirror of https://github.com/danielroe/zero-vue. Vue bindings for Zero
sync vue zero
0

Configure Feed

Select the types of activity you want to include in your feed.

zero-vue / src / query.ts
4.2 kB 146 lines
1// based on https://github.com/rocicorp/mono/tree/main/packages/zero-solid 2 3import type { 4 CustomMutatorDefs, 5 DefaultContext, 6 DefaultSchema, 7 Falsy, 8 HumanReadable, 9 PullRow, 10 QueryOrQueryRequest, 11 ReadonlyJSONValue, 12 Schema, 13 TTL, 14 Zero, 15} from '@rocicorp/zero' 16import type { ComputedRef, MaybeRefOrGetter } from 'vue' 17import type { QueryError, QueryStatus, VueView } from './view' 18 19import { addContextToQuery, asQueryInternals, DEFAULT_TTL_MS } from '@rocicorp/zero/bindings' 20import { 21 computed, 22 getCurrentInstance, 23 onUnmounted, 24 shallowRef, 25 toValue, 26 watch, 27} from 'vue' 28import { vueViewFactory } from './view' 29 30export interface UseQueryOptions { 31 ttl?: TTL | undefined 32} 33 34export interface QueryResult<TReturn> { 35 data: ComputedRef<HumanReadable<TReturn>> 36 status: ComputedRef<QueryStatus> 37 error: ComputedRef<QueryError & { retry: () => void } | undefined> 38} 39 40export interface MaybeQueryResult<TReturn> { 41 data: ComputedRef<HumanReadable<TReturn> | undefined> 42 status: ComputedRef<QueryStatus | 'disabled'> 43 error: ComputedRef<QueryError & { retry: () => void } | undefined> 44} 45 46// Overload 1: Query 47export function useQuery< 48 TTable extends keyof TSchema['tables'] & string, 49 TInput extends ReadonlyJSONValue | undefined, 50 TOutput extends ReadonlyJSONValue | undefined, 51 TSchema extends Schema = DefaultSchema, 52 TReturn = PullRow<TTable, TSchema>, 53 TContext = DefaultContext, 54 MD extends CustomMutatorDefs | undefined = undefined, 55>( 56 z: MaybeRefOrGetter<Zero<TSchema, MD, TContext>>, 57 query: MaybeRefOrGetter<QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>>, 58 options?: MaybeRefOrGetter<UseQueryOptions>, 59): QueryResult<TReturn> 60 61// Overload 2: Maybe query 62export function useQuery< 63 TTable extends keyof TSchema['tables'] & string, 64 TInput extends ReadonlyJSONValue | undefined, 65 TOutput extends ReadonlyJSONValue | undefined, 66 TSchema extends Schema = DefaultSchema, 67 TReturn = PullRow<TTable, TSchema>, 68 TContext = DefaultContext, 69 MD extends CustomMutatorDefs | undefined = undefined, 70>( 71 z: MaybeRefOrGetter<Zero<TSchema, MD, TContext>>, 72 query: MaybeRefOrGetter<QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext> | Falsy>, 73 options?: MaybeRefOrGetter<UseQueryOptions>, 74): MaybeQueryResult<TReturn> 75 76// Implementation 77export function useQuery< 78 TTable extends keyof TSchema['tables'] & string, 79 TInput extends ReadonlyJSONValue | undefined, 80 TOutput extends ReadonlyJSONValue | undefined, 81 TSchema extends Schema = DefaultSchema, 82 TReturn = PullRow<TTable, TSchema>, 83 TContext = DefaultContext, 84 MD extends CustomMutatorDefs | undefined = undefined, 85>( 86 z: MaybeRefOrGetter<Zero<TSchema, MD, TContext>>, 87 query: MaybeRefOrGetter<QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext> | Falsy>, 88 options?: MaybeRefOrGetter<UseQueryOptions>, 89): QueryResult<TReturn> | MaybeQueryResult<TReturn> { 90 const ttl = computed(() => toValue(options)?.ttl ?? DEFAULT_TTL_MS) 91 const view = shallowRef<VueView | null>(null) 92 const refetchKey = shallowRef(0) 93 94 const q = shallowRef() 95 watch( 96 [ 97 () => toValue(query), 98 () => toValue(z), 99 ], 100 ([query, z]) => { 101 q.value = query ? addContextToQuery(toValue(query), toValue(z).context) : undefined 102 }, 103 { immediate: true }, 104 ) 105 106 const qi = computed(() => q.value ? asQueryInternals(q.value) : undefined) 107 const hash = computed(() => qi.value?.hash()) 108 109 watch( 110 [ 111 () => toValue(z), 112 hash, 113 refetchKey, 114 ], 115 ([z]) => { 116 view.value?.destroy() 117 if (!hash.value) { 118 view.value = null 119 } 120 else { 121 view.value = z.materialize(toValue(q), vueViewFactory, { ttl: toValue(ttl) }) 122 } 123 }, 124 { immediate: true }, 125 ) 126 127 watch(ttl, (ttl) => { 128 toValue(view)?.updateTTL(ttl) 129 }) 130 131 if (getCurrentInstance()) { 132 onUnmounted(() => view.value?.destroy()) 133 } 134 135 return { 136 data: computed(() => view.value?.data as HumanReadable<TReturn>), 137 status: computed(() => view.value?.status ?? 'disabled'), 138 error: computed(() => view.value?.error 139 ? { 140 retry: () => { refetchKey.value++ }, 141 ...view.value.error, 142 } 143 : undefined, 144 ), 145 } 146}