|
1 | | -import { |
2 | | - type CompilableQuery, |
3 | | - ParsedQuery, |
4 | | - type SQLWatchOptions, |
5 | | - parseQuery, |
6 | | - runOnSchemaChange |
7 | | -} from '@powersync/common'; |
8 | | -import { type MaybeRef, type Ref, ref, toValue, watchEffect } from 'vue'; |
9 | | -import { usePowerSync } from './powerSync'; |
10 | | - |
11 | | -interface AdditionalOptions extends Omit<SQLWatchOptions, 'signal'> { |
12 | | - runQueryOnce?: boolean; |
13 | | -} |
| 1 | +import { type CompilableQuery } from '@powersync/common'; |
| 2 | +import { type MaybeRef, type Ref } from 'vue'; |
| 3 | +import { AdditionalOptions, useSingleQuery } from './useSingleQuery'; |
| 4 | +import { useWatchedQuery } from './useWatchedQuery'; |
14 | 5 |
|
15 | 6 | export type WatchedQueryResult<T> = { |
16 | 7 | data: Ref<T[]>; |
@@ -54,115 +45,12 @@ export type WatchedQueryResult<T> = { |
54 | 45 | export const useQuery = <T = any>( |
55 | 46 | query: MaybeRef<string | CompilableQuery<T>>, |
56 | 47 | sqlParameters: MaybeRef<any[]> = [], |
57 | | - options: AdditionalOptions = {} |
| 48 | + options: AdditionalOptions<T> = {} |
58 | 49 | ): WatchedQueryResult<T> => { |
59 | | - const data = ref<T[]>([]) as Ref<T[]>; |
60 | | - const error = ref<Error | undefined>(undefined); |
61 | | - const isLoading = ref(true); |
62 | | - const isFetching = ref(true); |
63 | | - |
64 | | - // Only defined when the query and parameters are successfully parsed and tables are resolved |
65 | | - let fetchData: () => Promise<void> | undefined; |
66 | | - |
67 | | - const powerSync = usePowerSync(); |
68 | | - const logger = powerSync?.value?.logger ?? console; |
69 | | - |
70 | | - const finishLoading = () => { |
71 | | - isLoading.value = false; |
72 | | - isFetching.value = false; |
73 | | - }; |
74 | | - |
75 | | - if (!powerSync) { |
76 | | - finishLoading(); |
77 | | - error.value = new Error('PowerSync not configured.'); |
78 | | - return { data, isLoading, isFetching, error }; |
| 50 | + switch (true) { |
| 51 | + case options.runQueryOnce: |
| 52 | + return useSingleQuery(query, sqlParameters, options); |
| 53 | + default: |
| 54 | + return useWatchedQuery(query, sqlParameters, options); |
79 | 55 | } |
80 | | - |
81 | | - const handleResult = (result: T[]) => { |
82 | | - finishLoading(); |
83 | | - data.value = result; |
84 | | - error.value = undefined; |
85 | | - }; |
86 | | - |
87 | | - const handleError = (e: Error) => { |
88 | | - fetchData = undefined; |
89 | | - finishLoading(); |
90 | | - data.value = []; |
91 | | - |
92 | | - const wrappedError = new Error('PowerSync failed to fetch data: ' + e.message); |
93 | | - wrappedError.cause = e; |
94 | | - error.value = wrappedError; |
95 | | - }; |
96 | | - |
97 | | - const _fetchData = async (executor: () => Promise<T[]>) => { |
98 | | - isFetching.value = true; |
99 | | - try { |
100 | | - const result = await executor(); |
101 | | - handleResult(result); |
102 | | - } catch (e) { |
103 | | - logger.error('Failed to fetch data:', e); |
104 | | - handleError(e); |
105 | | - } |
106 | | - }; |
107 | | - |
108 | | - watchEffect(async (onCleanup) => { |
109 | | - const abortController = new AbortController(); |
110 | | - // Abort any previous watches when the effect triggers again, or when the component is unmounted |
111 | | - onCleanup(() => abortController.abort()); |
112 | | - |
113 | | - let parsedQuery: ParsedQuery; |
114 | | - const queryValue = toValue(query); |
115 | | - try { |
116 | | - parsedQuery = parseQuery(queryValue, toValue(sqlParameters).map(toValue)); |
117 | | - } catch (e) { |
118 | | - logger.error('Failed to parse query:', e); |
119 | | - handleError(e); |
120 | | - return; |
121 | | - } |
122 | | - |
123 | | - const { sqlStatement: sql, parameters } = parsedQuery; |
124 | | - const watchQuery = async (abortSignal: AbortSignal) => { |
125 | | - let resolvedTables = []; |
126 | | - try { |
127 | | - resolvedTables = await powerSync.value.resolveTables(sql, parameters, options); |
128 | | - } catch (e) { |
129 | | - logger.error('Failed to fetch tables:', e); |
130 | | - handleError(e); |
131 | | - return; |
132 | | - } |
133 | | - // Fetch initial data |
134 | | - const executor = |
135 | | - typeof queryValue == 'string' ? () => powerSync.value.getAll<T>(sql, parameters) : () => queryValue.execute(); |
136 | | - fetchData = () => _fetchData(executor); |
137 | | - await fetchData(); |
138 | | - |
139 | | - if (options.runQueryOnce) { |
140 | | - return; |
141 | | - } |
142 | | - |
143 | | - powerSync.value.onChangeWithCallback( |
144 | | - { |
145 | | - onChange: async () => { |
146 | | - await fetchData(); |
147 | | - }, |
148 | | - onError: handleError |
149 | | - }, |
150 | | - { |
151 | | - ...options, |
152 | | - signal: abortSignal, |
153 | | - tables: resolvedTables |
154 | | - } |
155 | | - ); |
156 | | - }; |
157 | | - |
158 | | - runOnSchemaChange(watchQuery, powerSync.value, { signal: abortController.signal }); |
159 | | - }); |
160 | | - |
161 | | - return { |
162 | | - data, |
163 | | - isLoading, |
164 | | - isFetching, |
165 | | - error, |
166 | | - refresh: () => fetchData?.() |
167 | | - }; |
168 | 56 | }; |
0 commit comments