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
JavaScript
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;
}
}