[READ-ONLY] Mirror of https://github.com/danielroe/vue-sanity. Sanity integration for Vue Composition API
composition-api hacktoberfest javascript nuxt sanity typescript vue vuejs
0

Configure Feed

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

vue-sanity / src / query.ts
6.9 kB 260 lines
1import { 2 computed, 3 inject, 4 watch, 5 InjectionKey, 6 Ref, 7 isRef, 8} from '@vue/composition-api' 9import minifier from 'minify-groq' 10import type { SanityClient } from '@sanity/client' 11import type { QueryBuilder } from 'sanity-typed-queries' 12 13import { useCache, CacheOptions, FetchStatus } from './cache' 14 15export interface Client { 16 fetch: (query: string) => Promise<any> 17 [key: string]: any 18} 19 20export const clientSymbol: InjectionKey<Client> = Symbol('Sanity client') 21export const previewClientSymbol: InjectionKey<SanityClient> = Symbol( 22 'Sanity client for previews' 23) 24export const optionsSymbol: InjectionKey<Options> = Symbol( 25 'Default query options' 26) 27 28type Query = string | (() => string | null | undefined | false) 29 30interface Result<T> { 31 /** 32 * An automatically synced and updated result of the Sanity query. 33 */ 34 data: Ref<T> 35 /** 36 * The status of the query. Can be 'server loaded', 'loading', 'client loaded' or 'error'. 37 */ 38 status: Ref<FetchStatus> 39 /** 40 * An error returned in the course of fetching 41 */ 42 error: any 43 /** 44 * Get result directly from fetcher (integrates with cache) 45 */ 46 fetch: () => Promise<T> 47} 48 49export type Options = Omit<CacheOptions<any>, 'initialValue'> & { 50 /** 51 * Whether to listen to real-time updates from Sanity. You can also pass an object of options to pass to `client.listen`. Defaults to false. 52 */ 53 listen?: boolean | Record<string, any> 54} 55 56/** 57 * 58 * @param query A string, or a function that retuns a query string. If the return value changes, a new Sanity query will be run and the return value automatically updated. 59 */ 60export function useSanityFetcher<T extends any>(query: Query): Result<T | null> 61 62/** 63 * 64 * @param query A function that retuns a query string. If the return value changes, a new Sanity query will be run and the return value automatically updated. 65 * @param initialValue The value to return before the Sanity client returns an actual result. Defaults to null. 66 * @param mapper A function that transforms the result from Sanity, before returning it to your component. 67 */ 68export function useSanityFetcher<T extends any, R extends any = T>( 69 query: Query, 70 initialValue: R, 71 mapper?: (result: any) => T, 72 options?: Options 73): Result<T | R> 74 75export function useSanityFetcher( 76 query: Query, 77 initialValue = null, 78 mapper = (result: any) => result, 79 queryOptions?: Options 80) { 81 const client = inject(clientSymbol) 82 const defaultOptions = inject(optionsSymbol, {}) 83 84 const options = { 85 ...defaultOptions, 86 initialValue, 87 ...queryOptions, 88 } 89 if (!client) 90 throw new Error( 91 'You must call useSanityClient before using sanity resources in this project.' 92 ) 93 94 const computedQuery = 95 typeof query === 'string' 96 ? minifier(query).replace(/\n/g, ' ') 97 : computed(() => minifier(query() || '').replace(/\n/g, ' ')) 98 99 const { data, status, setCache, error, fetch } = useCache( 100 computedQuery, 101 query => (query ? client.fetch(query).then(mapper) : Promise.resolve(null)), 102 options 103 ) 104 105 if (options.listen) { 106 const previewClient = inject(previewClientSymbol, client as SanityClient) 107 if ('listen' in previewClient) { 108 const listenOptions = 109 typeof options.listen === 'boolean' ? undefined : options.listen 110 111 const subscribe = (query: string) => 112 previewClient.listen(query, listenOptions).subscribe( 113 event => 114 event.result && 115 setCache({ 116 key: query, 117 value: event.result, 118 }) 119 ) 120 if (isRef(computedQuery)) { 121 watch( 122 computedQuery, 123 query => { 124 const subscription = subscribe(query) 125 126 const unwatch = watch( 127 computedQuery, 128 newQuery => { 129 if (newQuery !== query) { 130 subscription.unsubscribe() 131 unwatch() 132 } 133 }, 134 { immediate: true } 135 ) 136 }, 137 { immediate: true } 138 ) 139 } else { 140 subscribe(computedQuery) 141 } 142 } 143 } 144 145 return { data, status, error, fetch } 146} 147 148export function useSanityQuery< 149 Builder extends Pick< 150 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 151 'use' 152 >, 153 Schema, 154 Mappings extends Record<string, any>, 155 Type, 156 Project extends boolean, 157 Exclude extends string 158>( 159 builder: Builder | (() => Builder) 160): Result< 161 ReturnType<Builder['use']>[1] extends Array<any> 162 ? ReturnType<Builder['use']>[1] 163 : ReturnType<Builder['use']>[1] | null 164> 165 166export function useSanityQuery< 167 Builder extends Pick< 168 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 169 'use' 170 >, 171 Schema, 172 Mappings extends Record<string, any>, 173 Type, 174 Project extends boolean, 175 Exclude extends string 176>( 177 builder: Builder | (() => Builder), 178 initialValue: null 179): Result<ReturnType<Builder['use']>[1] | null> 180 181export function useSanityQuery< 182 Builder extends Pick< 183 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 184 'use' 185 >, 186 Schema, 187 Mappings extends Record<string, any>, 188 Type, 189 Project extends boolean, 190 Exclude extends string, 191 InitialValue 192>( 193 builder: Builder | (() => Builder), 194 initialValue: InitialValue 195): Result<ReturnType<Builder['use']>[1] | InitialValue> 196 197export function useSanityQuery< 198 Builder extends Pick< 199 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 200 'use' 201 >, 202 Schema, 203 Mappings extends Record<string, any>, 204 Type, 205 Project extends boolean, 206 Exclude extends string, 207 Mapper extends (result: ReturnType<Builder['use']>[1]) => any 208>( 209 builder: Builder | (() => Builder), 210 initialValue: null, 211 mapper: Mapper, 212 options?: Options 213): Result<ReturnType<Mapper> | null> 214 215export function useSanityQuery< 216 Builder extends Pick< 217 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 218 'use' 219 >, 220 Schema, 221 Mappings extends Record<string, any>, 222 Type, 223 Project extends boolean, 224 Exclude extends string, 225 InitialValue, 226 Mapper extends (result: ReturnType<Builder['use']>[1]) => any 227>( 228 builder: Builder | (() => Builder), 229 initialValue: InitialValue, 230 mapper: Mapper, 231 options?: Options 232): Result<ReturnType<Mapper> | InitialValue> 233 234export function useSanityQuery< 235 Builder extends Pick< 236 QueryBuilder<Schema, Mappings, Type, Project, Exclude>, 237 'use' 238 >, 239 Schema, 240 Mappings extends Record<string, any>, 241 Type, 242 Project extends boolean, 243 Exclude extends string 244>( 245 builder: Builder | (() => Builder), 246 initialValue = null, 247 mapper = (result: any) => result, 248 options?: Options 249) { 250 const query = 251 'use' in builder ? () => builder.use()[0] : () => builder().use()[0] 252 const type = 'use' in builder ? builder.use()[1] : builder().use()[1] 253 254 return useSanityFetcher<typeof type>( 255 query, 256 initialValue || type, 257 mapper, 258 options 259 ) 260}