UNPKG

svelte-firebase-state

Version:

Simplify Firebase integration in Svelte and SvelteKit with reactive state management for Firestore and Realtime Database.

130 lines (129 loc) 4.49 kB
import { collection, getDocs, query as firestoreQuery, onSnapshot, deleteDoc, doc, setDoc } from "firebase/firestore"; import { FirestoreState } from "./FirestoreState.svelte.js"; import { CollectionAggregateState } from "./CollectionAggregateState.svelte.js"; var COLLECTION_STATE_ERRORS; (function (COLLECTION_STATE_ERRORS) { COLLECTION_STATE_ERRORS["NO_COLLECTION_REF"] = "Collection reference is not set"; COLLECTION_STATE_ERRORS["NO_QUERY_REF"] = "Query reference is not set"; COLLECTION_STATE_ERRORS["NO_PATH_STRING"] = "Path string is not set"; })(COLLECTION_STATE_ERRORS || (COLLECTION_STATE_ERRORS = {})); export class CollectionState extends FirestoreState { queryFunction; aggregateState; queryRef; collectionRef; constructor({ auth, firestore, query: queryFunction, path: pathFunctionOrString, listen = false, fromFirestore, toFirestore, aggregate, converter }) { super({ auth, firestore, listen, fromFirestore, toFirestore, pathFunctionOrString, converter }); this.queryFunction = queryFunction; if (aggregate) { this.aggregateState = new CollectionAggregateState({ auth, firestore, path: pathFunctionOrString, aggregate, listen }); } } get_collection_from_path(path) { const pathArray = path.split("/"); return collection(this.firestore, ...pathArray).withConverter(this.converter); } async init() { const user = await this.getUserPromise; const pathStr = this.get_path_string(user); if (!pathStr) { throw new Error(COLLECTION_STATE_ERRORS.NO_PATH_STRING); } this.collectionRef = this.get_collection_from_path(pathStr); } // Set query reference with query function async setup_query_ref() { if (!this.collectionRef) { throw new Error(COLLECTION_STATE_ERRORS.NO_COLLECTION_REF); } const user = await this.getUserPromise; const queryParams = this.queryFunction ? this.queryFunction(user) : []; return firestoreQuery(this.collectionRef, ...queryParams); } map_data(doc) { return doc.data(); } async fetch_data() { this.loading = true; this.queryRef = await this.setup_query_ref(); if (!this.queryRef) { throw new Error(COLLECTION_STATE_ERRORS.NO_QUERY_REF); } const querySnapshot = await getDocs(this.queryRef); this.data = querySnapshot.docs.map(this.map_data); this.loading = false; } async listen_data(callback) { if (this.unsub) { return; } this.queryRef = await this.setup_query_ref(); if (!this.queryRef) { throw new Error(COLLECTION_STATE_ERRORS.NO_QUERY_REF); } this.unsub = onSnapshot(this.queryRef, (querySnapshot) => { if (querySnapshot.empty) { callback?.([]); this.data = []; return; } const newData = querySnapshot.docs.map(this.map_data); this.data = newData; callback?.(this.data); }); return; } async add(data) { if (!this.collectionRef) { console.error("Collection reference is not set"); return; } const docRef = doc(this.collectionRef); // TODO: Here we have the id from docRef, we could do optimistic update // this.data = [...currentData, data]; await setDoc(docRef, data); return docRef.id; } get aggregateData() { return this.aggregateState?.data; } async delete(id) { if (!this.collectionRef) { return; } const docRef = this.get_doc_ref(id); return deleteDoc(docRef); } get_doc_ref(id) { if (!this.collectionRef) { throw new Error(COLLECTION_STATE_ERRORS.NO_COLLECTION_REF); } return doc(this.collectionRef, id); } refetch_aggregate_data() { this.aggregateState?.refetch(); } async get_query_ref() { await this.initPromise; this.queryRef = await this.setup_query_ref(); return this.queryRef; } async get_collection_ref() { await this.initPromise; return this.collectionRef; } }