-
-
Save Zehir/be3b229e36963b1e4d6869207b231ac0 to your computer and use it in GitHub Desktop.
useFirestore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { MaybeRef } from '@vueuse/core' | |
import type { Ref, UnwrapRef } from 'vue' | |
import type { CollectionReference, DocumentData, DocumentReference, QueryConstraint, SetOptions, UpdateData } from 'firebase/firestore' | |
import { addDoc, collection as fireCollection, doc as fireDoc, query as fireQuery, onSnapshot, setDoc, updateDoc } from 'firebase/firestore' | |
import { tryOnScopeDispose } from '@vueuse/shared' | |
import type { useFirebase } from './useFirebase' | |
import type { MaybeArray } from '~/misc/types' | |
export type Path = MaybeArray<MaybeRef<string | undefined>> | |
export function concatenatePath(...paths: MaybeRef<string | undefined>[]) { | |
return computed(() => { | |
if (useArraySome(paths, path => path === undefined).value) | |
return undefined | |
return useArrayReduce(paths, (fullPath = '', path) => fullPath += `/${resolveUnref(path)}`) | |
.value?.replaceAll(/[\/]{2,}/g, '/') | |
}) | |
} | |
export function asArray<T>(value: MaybeArray<T>) { | |
return Array.isArray(value) ? value : [value] | |
} | |
// export type FirestoreDocument = ReturnType<ReturnType<typeof useFirestore>['getDocument']> | |
export interface FirestoreDocument<T extends DocumentData> { | |
data: Ref<UnwrapRef<T>> | |
id: Readonly<Ref<string>> | |
path: Readonly<Ref<string>> | |
exists: Ref<boolean | undefined> | |
close: () => void | |
} | |
const DISCONNECT_TIMEOUT = 30 * 1000 | |
export function useFirestore(firebase: ReturnType<typeof useFirebase>) { | |
const path = (path: Path) => computed(() => { | |
if (Array.isArray(path)) | |
return concatenatePath(...path).value | |
return unref(path) | |
}) | |
const doc = <T>(collectionPath: Path) => computed(() => { | |
const _path = path(collectionPath) | |
if (!_path.value) | |
return | |
return fireDoc(firebase.firestore, _path.value) as DocumentReference<T> | |
}) | |
const collection = <T>(collectionPath: Path) => computed(() => { | |
const _path = path(collectionPath) | |
if (!_path.value) | |
return | |
return fireCollection(firebase.firestore, _path.value) as CollectionReference<T> | |
}) | |
const query = <T>(collectionPath: Path, ...queryConstraints: MaybeRef<QueryConstraint | undefined>[]) => computed(() => { | |
const _collection = collection<T>(collectionPath) | |
if (_collection.value === undefined) | |
return | |
if (useArraySome(queryConstraints, constraint => constraint === undefined).value) | |
return | |
return fireQuery<T>(_collection.value, ...queryConstraints.map(unref) as QueryConstraint[]) | |
}) | |
const addDocument = async <T>(path: Path, value: T) => { | |
const colReference = collection<T>(path) | |
if (unref(colReference)) { | |
try { | |
return addDoc(colReference.value!, value) | |
} | |
catch (error) { | |
console.warn(error, 'addDocument', colReference.value?.path, value) | |
} | |
} | |
} | |
const setDocument = <T>(path: Path, value: T, options: SetOptions = {}) => { | |
const docReference = doc<T>(path) | |
if (unref(docReference)) | |
setDoc(docReference.value!, value, options) | |
} | |
const updateDocument = <T>(path: Path, field: string | UpdateData<T>, value: unknown, ...moreFieldsAndValues: unknown[]) => { | |
const docReference = doc<T>(path) | |
console.log('updateDocument', unref(docReference)?.path) | |
if (unref(docReference)) { | |
if (typeof field === 'string') | |
return updateDoc(docReference.value!, field, value, ...moreFieldsAndValues) | |
else | |
return updateDoc<T>(docReference.value!, field) | |
} | |
} | |
const getDocument = <T extends DocumentData>(path: Path, placeHolder: T): FirestoreDocument<T> => { | |
const docReference = doc<T>(path) | |
const data = ref<T>(placeHolder) | |
const id = ref('') | |
const exists = ref<boolean | undefined>(undefined) | |
let close = () => { } | |
watchTriggerable(() => docReference.value, (isValid) => { | |
if (!isValid) | |
return | |
close() | |
console.log('getDocument', docReference.value?.path) | |
close = onSnapshot<T>(docReference.value!, (snapshot) => { | |
data.value = snapshot.data() as UnwrapRef<T> | |
id.value = snapshot.id | |
exists.value = snapshot.exists() | |
}, (error) => { | |
console.warn(error, 'getDocument', docReference.value?.path) | |
}) | |
}).trigger() | |
tryOnScopeDispose(() => { | |
// console.log('On Scope Dispose of a document ...', docReference.value?.path) | |
useTimeoutFn(() => { | |
// console.log('On Scope Dispose of a document closed.', docReference.value?.path) | |
close() | |
}, DISCONNECT_TIMEOUT) | |
}) | |
return { | |
data, | |
id: readonly(id), | |
path: readonly(computed(() => docReference.value?.path ?? '')), | |
exists, | |
close, | |
} | |
} | |
const findDocuments = <T extends DocumentData>( | |
_path: Path, | |
...queryConstraints: MaybeRef<QueryConstraint | undefined>[] | |
): { | |
data: Ref<UnwrapRef<FirestoreDocument<T>[]>> | |
close: FirestoreDocument<T>['close'] | |
} => { | |
const basePath = path(_path) | |
const _query = query<T>(_path, ...queryConstraints) | |
const data = ref<FirestoreDocument<T>[]>([]) | |
let close = () => { } | |
watchTriggerable(() => _query.value, (isValid) => { | |
if (!isValid) | |
return | |
close() | |
close = onSnapshot<T>(fireQuery(_query.value!), (snapshot) => { | |
data.value = snapshot.docs.map((value) => { | |
return { | |
data: value.data() as UnwrapRef<T>, | |
id: value.id, | |
path: `${basePath.value}/${value.id}`, | |
exists: value.exists(), | |
close, | |
} | |
}).filter(value => value.data !== undefined) | |
}, (error) => { | |
console.warn(error, 'findDocuments', basePath.value, queryConstraints.map((constrain) => { return unref(constrain) })) | |
}) | |
}).trigger() | |
tryOnScopeDispose(() => { | |
// console.log('On Scope Dispose of a query ...', path(_path).value) | |
useTimeoutFn(() => { | |
// console.log('On Scope Dispose of a query closed.', path(_path).value) | |
close() | |
}, DISCONNECT_TIMEOUT) | |
}) | |
return { data, close } | |
} | |
return { | |
path, | |
doc, | |
collection, | |
query, | |
addDocument, | |
setDocument, | |
updateDocument, | |
getDocument, | |
findDocuments, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment