UNPKG

unfire

Version:

⚡️ Utilities for Firebase 🔥 built with Composition API, Typescript and ❤️

337 lines (334 loc) 10.9 kB
// src/vue/vue-composables.ts import { ref, onUnmounted, reactive, computed, isRef, watch } from "vue"; import { debouncedWatch, useDebounceFn } from "@vueuse/core"; // src/firebase/firestore.ts import { getFirestore, doc, collection, setDoc, getDoc as _getDoc, getDocs as _getDocs, deleteDoc as _deleteDoc, onSnapshot as _onSnapshot, query, where, limit, orderBy } from "@unfire-init"; import { ImportFirestoreSchemaByPath } from "@unfire-firestore-schema"; import { Timestamp } from "@unfire-init"; var defaultWhereQueryBuilder = (...query2) => query2; var queryBuilder = ({ whereQueries, orderByQuery, limitBy }) => { return [ ...whereQueries ? whereQueries({ where: defaultWhereQueryBuilder }).map((query2) => where(...query2)) : [], ...orderByQuery ? [orderBy(orderByQuery.fieldPath, orderByQuery.directionStr)] : [], ...limitBy ? [limit(limitBy)] : [] ]; }; var getDocData = (docRef) => { const data = docRef.data(); if (data) { Object.defineProperty(data, "id", { value: docRef.id.toString(), writable: false }); } return data; }; var createCollection = (collectionPath, firestoreInstance) => { console.log("in createCollection"); return collection(firestoreInstance != null ? firestoreInstance : getFirestore(), collectionPath); }; var getCollectionDocRef = (collectionPath, docId, firestoreInstance) => doc(createCollection(collectionPath, firestoreInstance), docId); var upsertDoc = async (options) => { var _a; const { data, collectionPath, docId, docRef, setOption, validation = true, customSchema, onSuccess, onError } = options; const ref2 = docRef != null ? docRef : docId ? getCollectionDocRef(collectionPath, docId) : doc(createCollection(collectionPath)); try { if (validation) { const schema = customSchema != null ? customSchema : await ImportFirestoreSchemaByPath(collectionPath); const schemaParseResult = schema == null ? void 0 : schema.safeParse(data); (schemaParseResult == null ? void 0 : schemaParseResult.success) ? await setDoc(ref2, schemaParseResult.data, setOption != null ? setOption : { merge: true }).then(() => { onSuccess == null ? void 0 : onSuccess(schemaParseResult.data); }) : onError == null ? void 0 : onError((_a = schemaParseResult == null ? void 0 : schemaParseResult.error) != null ? _a : new Error("No Schema Found")); } else { await setDoc(ref2, data, setOption != null ? setOption : { merge: true }).then(() => { onSuccess == null ? void 0 : onSuccess(data); }); } } catch (error) { onError == null ? void 0 : onError(error); } return ref2; }; var queryCollection = (options) => { const { collectionPath, onSnapshot, onError, queryOption, snapshotListenOptions = { includeMetadataChanges: false } } = options; try { const q = query(createCollection(collectionPath), ...queryOption ? queryBuilder(queryOption) : []); return { unsubscribe: _onSnapshot(q, snapshotListenOptions, { next: (snapshot) => { onSnapshot({ snapshot, docsData: snapshot.docs.map(getDocData) }); }, error: onError }) }; } catch (error) { onError == null ? void 0 : onError(error); return { error }; } }; var queryDoc = (options) => { const { collectionPath, docId, docRef, onSnapshot, onError, snapshotListenOptions = { includeMetadataChanges: false } } = options; try { if (docRef) { return { unsubscribe: _onSnapshot(docRef, snapshotListenOptions, { next: (snapshot) => { onSnapshot({ snapshot, docData: getDocData(snapshot) }); }, error: onError }) }; } else if (collectionPath && docId) { return { unsubscribe: _onSnapshot(getCollectionDocRef(collectionPath, docId), snapshotListenOptions, { next: (snapshot) => { onSnapshot({ snapshot, docData: getDocData(snapshot) }); }, error: onError }) }; } else { const error = new Error('must provide "collectionPath" and "docId" or "docRef"'); onError == null ? void 0 : onError(error); return { error }; } } catch (error) { onError == null ? void 0 : onError(error); return { error }; } }; var deleteDoc = (options) => { const { collectionPath, docId, docRef, onError } = options; try { if (docRef) { return _deleteDoc(docRef); } else if (collectionPath && docId) { return _deleteDoc(getCollectionDocRef(collectionPath, docId)); } else { throw new Error('must provide "collectionPath" and "docId" or "docRef"'); } } catch (error) { onError == null ? void 0 : onError(error); } }; var whereFilterOps = ["==", "!=", ">", ">=", "<", "<=", "array-contains", "array-contains-any", "in", "not-in"]; // src/vue/vue-composables.ts var createPathRef = (path) => ref(path); var toReactiveQueryOption = (queryOption) => reactive(queryOption); var useCollection = (options) => { var _a; const { collectionPath: _collectionPath, resultsRef, queryOption, onError, snapshotListenOptions, optionsWatchDebounce = 20 } = options; console.log({ resultsRef }); const results = resultsRef != null ? resultsRef : ref([]); const unsubscribe = ref(); const collectionPath = ref(_collectionPath); const _queryOption = ref(); const initialWhereQueries = (_a = queryOption == null ? void 0 : queryOption.whereQueries) == null ? void 0 : _a.call(queryOption, { where: defaultWhereQueryBuilder }).map(([fieldPath, opStr, value]) => ({ fieldPath, opStr, value })); const whereQueries = ref(initialWhereQueries != null ? initialWhereQueries : []); const orderByQuery = ref(queryOption == null ? void 0 : queryOption.orderByQuery); const limitBy = ref(queryOption == null ? void 0 : queryOption.limitBy); const stopQueryOptionWatch = watch([whereQueries, orderByQuery, limitBy], ([queries, orderBy2, limit2]) => { _queryOption.value = { whereQueries: ({ where: where2 }) => queries.map(({ fieldPath, opStr, value }) => where2(fieldPath, opStr, value)), limitBy: limit2, orderByQuery: orderBy2 }; }, { immediate: true, deep: true }); const debounce = computed(() => unsubscribe.value ? ref(optionsWatchDebounce).value : 0); const watchStopHandle = debouncedWatch([collectionPath, _queryOption], ([collectionPath2, queryOption2]) => { var _a2; (_a2 = unsubscribe.value) == null ? void 0 : _a2.call(unsubscribe); unsubscribe.value = queryCollection({ collectionPath: collectionPath2, queryOption: queryOption2, onSnapshot: ({ docsData }) => { results.value = docsData; }, onError, snapshotListenOptions }).unsubscribe; }, { immediate: true, debounce, deep: true }); const stop = () => { var _a2; (_a2 = unsubscribe.value) == null ? void 0 : _a2.call(unsubscribe); watchStopHandle(); stopQueryOptionWatch(); }; onUnmounted(() => { stop(); }); return { results, stop, whereQueries, orderByQuery, limitBy }; }; var useDoc = (options) => { const { collectionPath: _collectionPath, docId: _docId, onError, snapshotListenOptions, optionsWatchDebounce = 20 } = options; const result = ref(); const unsubscribe = ref(); const collectionPath = ref(_collectionPath); const docId = ref(_docId); const debounce = computed(() => unsubscribe.value ? ref(optionsWatchDebounce).value : 0); const watchStopHandle = debouncedWatch([collectionPath, docId], ([collectionPath2, docId2]) => { var _a; (_a = unsubscribe.value) == null ? void 0 : _a.call(unsubscribe); unsubscribe.value = queryDoc({ collectionPath: collectionPath2, docId: docId2, onSnapshot: ({ docData }) => { result.value = docData; }, onError, snapshotListenOptions }).unsubscribe; }, { immediate: true, debounce }); const stop = () => { var _a; watchStopHandle(); (_a = unsubscribe.value) == null ? void 0 : _a.call(unsubscribe); }; onUnmounted(() => { stop(); }); return { result, stop }; }; var useDocBiDirection = (options) => { const { collectionPath: _collectionPath, docId: _docId, onError, onUpdateSuccess, snapshotListenOptions, refWatchUpdateDebounce = 200, optionsWatchDebounce = 20, updateValidation, updateCustomSchema } = options; const result = ref(); const validationError = ref(); const unsubscribe = ref(); const collectionPath = ref(_collectionPath); const docId = ref(_docId); const resetValidationError = () => { validationError.value = void 0; }; const updateDoc = (data) => { return upsertDoc({ collectionPath: collectionPath.value, docId: docId.value, data, validation: isRef(updateValidation) ? updateValidation.value : updateValidation, customSchema: updateCustomSchema, onSuccess: (data2) => { resetValidationError(); onUpdateSuccess == null ? void 0 : onUpdateSuccess(data2); }, onError: (e) => { onError == null ? void 0 : onError(e); if ("issues" in e) validationError.value = e; } }); }; const debounce = computed(() => unsubscribe.value ? ref(optionsWatchDebounce).value : 0); let shouldUpdateDoc = false; let localUpdated = false; const resultWatchStopHandle = debouncedWatch(result, async (newResult) => { if (newResult && shouldUpdateDoc) { localUpdated = true; await updateDoc(newResult); localUpdated = false; } }, { deep: true, debounce: refWatchUpdateDebounce }); const shouldUpdateDelay = computed(() => ref(refWatchUpdateDebounce).value + 100); const setShouldUpdateDoc = useDebounceFn(() => { shouldUpdateDoc = true; }, shouldUpdateDelay); const optionsWatchStopHandle = debouncedWatch([collectionPath, docId], ([collectionPath2, docId2]) => { var _a; (_a = unsubscribe.value) == null ? void 0 : _a.call(unsubscribe); unsubscribe.value = queryDoc({ collectionPath: collectionPath2, docId: docId2, onSnapshot: ({ docData }) => { if (!localUpdated) { shouldUpdateDoc = false; result.value = docData; setShouldUpdateDoc(); } }, onError, snapshotListenOptions }).unsubscribe; }, { immediate: true, debounce }); const stop = () => { var _a; resultWatchStopHandle(); optionsWatchStopHandle(); (_a = unsubscribe.value) == null ? void 0 : _a.call(unsubscribe); }; onUnmounted(() => { stop(); }); return { result, validationError, updateDoc, resetValidationError, stop }; }; export { createPathRef, deleteDoc, toReactiveQueryOption, upsertDoc, useCollection, useDoc, useDocBiDirection, whereFilterOps };