fwk-q-firebase
Version:
Firebase access library
453 lines (431 loc) • 14.2 kB
JavaScript
import { initializeApp } from 'firebase/app'
import { getFunctions, httpsCallable } from 'firebase/functions'
import { getAuth, signInWithEmailAndPassword, createUserWithEmailAndPassword, updateProfile, GoogleAuthProvider, signInWithPopup, sendPasswordResetEmail } from 'firebase/auth'
import { getFirestore, onSnapshot, collection, getDocs, doc, addDoc, deleteDoc, updateDoc, getDoc, setDoc, query, where, orderBy, writeBatch, runTransaction } from 'firebase/firestore'
import { getStorage, ref, uploadBytes, uploadBytesResumable, getDownloadURL, deleteObject } from 'firebase/storage'
import { getDatabase, onValue, ref as refRt } from 'firebase/database'
import { Capacitor } from '@capacitor/core'
import { Network } from '@capacitor/network'
import { ui } from 'fwk-ui'
let db
let dbRt
let sto
let auth
let rtListeners = {}
let rtRefs = {}
let functions
const initFirebase = (cfg) => {
const app = initializeApp(cfg)
functions = getFunctions(app)
db = getFirestore()
dbRt = getDatabase(app)
sto = getStorage()
auth = getAuth()
}
const cFunction = async (fnName, payload) => {
console.log('Cloud Function payload:', fnName, payload)
const fn = httpsCallable(functions, fnName)
const response = await fn(payload)
console.log('Cloud Function response:', response)
return response?.data
}
/////////////////////////////////////////////////////////////////////////////////////
// REALTIME DB
/////////////////////////////////////////////////////////////////////////////////////
const realtimeOn = (route, proxy) => {
console.log('route:', route)
const lstRef = refRt(dbRt, route)
rtRefs[route] = lstRef
rtListeners[route] = onValue(lstRef, (snapshot) => {
const data = snapshot.val()
proxy.value = data
})
}
const realtimeOff = (route) => {
//rtRefs[route].off('value', rtListeners[route]) // ("value", (snap) => {}))
rtRefs[route] = null
delete rtRefs[route]
}
//=========================================================
/////////////////////////////////////////////////////////////////////////////////////
// SECURITY AUTH
/////////////////////////////////////////////////////////////////////////////////////
const loginFirebase = async (email, password) => {
try {
const result = signInWithEmailAndPassword(auth, email, password)
return result
}
catch (err) {
console.log('signIn error:', err.message)
ui.actions.notify(err.message, 'error')
}
}
const loginGoogle = async () => {
auth.useDeviceLanguage()
const provider = new GoogleAuthProvider()
provider.addScope('https://www.googleapis.com/auth/userinfo.email')
provider.addScope('https://www.googleapis.com/auth/userinfo.profile')
const redirectUrl = 'YOUR_REDIRECT_URL_HERE'
auth.languageCode = 'es'
try {
const result = await signInWithPopup(auth, provider)
return result
} catch (error) {
ui.actions.notify('Error Auth:', error.message)
}
}
const logout = async () => {
try {
return auth.signOut()
}
catch (err) {
console.log('logout error:', err.message)
ui.actions.notify(err.message, 'error')
}
}
const register = async (name, email, password) => {
try {
const { user } = await createUserWithEmailAndPassword(auth, email, password)
await updateProfile(user, { displayName: name })
return user
}
catch (err) {
console.log('registration error:', err.message)
ui.actions.notify(err.message, 'error')
}
}
const forgotPassword = (email) => {
console.log('store sendResetPassword:', email)
if (!email) {
ui.actions.notify('Debe ingresar un mail válido!', 'warning')
} else {
sendPasswordResetEmail(auth, email).then(() => {
ui.actions.notify('Se ha enviado el mail de blanqueo de contraseña correctamente!', 'success')
})
}
}
const addQuote = (quote) => {
if (!auth.currentUser) {
return alert('Not Authorized')
}
return db.doc(`users/${auth.currentUser.id}`).set({ quote })
}
const isInitialized = async () => {
return new Promise((resolve) => {
auth.onAuthStateChanged(() => {
resolve(true)
})
})
}
const getCurrentUser = async () => {
return auth.currentUser
}
const getCurrentUserQuote = async () => {
const quote = await db
.doc(`users/${auth.currentUser.uid}`)
.get()
return quote.get('quote')
}
//=========================================================
/////////////////////////////////////////////////////////////////////////////////////
// CRUD
/////////////////////////////////////////////////////////////////////////////////////
const getCollection = async (col) => {
const colRef = collection(db, col)
const colSnapshot = await getDocs(colRef)
const result = colSnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }))
return result
}
const getCollectionQuery = async (col, multiWhere, ops) => {
let q = query(collection(db, col))
multiWhere.forEach(criteria => {
q = query(q, where(criteria.field, criteria.op, criteria.val))
})
if (ops?.sortField) {
q = query(q, orderBy(ops.sortField, ops.sortDir))
}
const querySnapshot = await getDocs(q)
const result = querySnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }))
return result
}
const getCollectionRefQuery = (col, multiWhere, ops) => {
let q = query(collection(db, col))
multiWhere.forEach(criteria => {
q = query(q, where(criteria.field, criteria.op, criteria.val))
})
if (ops?.sortField) {
q = query(q, orderBy(ops.sortField, ops.sortDir))
}
return q
}
const getCollectionFlex = async (col, ops) => {
let q
let operator = '=='
if (ops.op) operator = ops.op
if (ops.field && ops.sortField) {
q = query(collection(db, col), where(ops.field, operator, ops.val), orderBy(ops.field, 'asc'), orderBy(ops.sortField, ops.sortDir))
}
if (ops.field && !ops.sortField) {
q = query(collection(db, col), where(ops.field, operator, ops.val))
}
if (!ops.field && ops.sortField) {
q = query(collection(db, col), orderBy(ops.sortField, ops.sortDir))
}
if (!ops.field && !ops.sortField) {
q = query(collection(db, colName))
}
const querySnapshot = await getDocs(q)
const result = querySnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }))
return result
}
const getCollectionRef = (col) => {
const colRef = collection(db, col)
return colRef
}
const getDocument = async (col, d) => {
const docRef = doc(db, col, d)
const sn = await getDoc(docRef)
const result = sn.data()
return result
}
const getDocumentRef = (col, d) => {
let ref
if (d) {
ref = doc(db, col, d)
}
else // new doc
{
ref = doc(collection(db, col))
}
return ref
}
const addDocument = async (col, d) => {
const colRef = collection(db, col)
const docRef = await addDoc(colRef, d)
return docRef
}
const setDocument = async (col, d, id, options = { merge: true }) => {
let docRef
removeUndefinedFields(d)
if (id) {
const docRef = doc(db, col, d.id || id)
await setDoc(docRef, d, options)
} else {
const ref = collection(db, col)
await addDoc(ref, d)
console.log('Document has been added successfully')
}
return true
}
const emptyCollection = async (col) => {
ui.actions.showLoading()
const colRef = collection(db, col)
const colSnapshot = await getDocs(colRef)
const documents = colSnapshot.docs
ui.actions.hideLoading()
ui.actions.showLoading({
type: 'progressCounter',
color: 'red',
total: documents.length,
value: 0
})
let i = 0
for (const d of documents) {
await deleteDocument(col, d.id)
// await sleep(5)
ui.actions.setLoaderOps({
type: 'progressCounter',
color: 'red',
total: documents.length,
value: i++
})
}
ui.actions.hideLoading()
}
const deleteCollection = async (col, batchSize) => {
const colRef = collection(db, col)
const q = query(collection(db, col), orderBy('__name__'))
console.log('query:', q)
// const query = colRef.orderBy('__name__').limit(batchSize) // collectionRef.orderBy('__name__').limit(batchSize)
return new Promise((resolve, reject) => {
deleteQueryBatch(q, resolve).catch(reject)
})
}
const deleteDocument = async (col, id) => {
const docRef = doc(db, col, id)
await deleteDoc(docRef)
}
const batchInsert = async (col, data) => {
const batch = writeBatch(db)
console.log('data len: ', data.length)
const filteredData = data.filter(x => !evalUndefinedFields(x)) // !!x['Fecha inicio'])
console.log('data filtered len: ', filteredData.length)
ui.actions.showLoading({
type: 'progressCounter',
color: 'blue',
total: filteredData.length,
value: 0
})
let i = 1
for (const d of filteredData) {
const docRef = doc(db, col, new Date().getTime())
ui.actions.setLoaderOps({
type: 'progressCounter',
color: 'blue',
total: filteredData.length,
value: i++
})
batch.set(docRef, d) // batch.set(docRef, d) // batch.create(docRef, d)
}
await batch.commit()
ui.actions.hideLoading()
}
const transaction = async (cb) => {
await runTransaction(db, cb)
}
//=========================================================
/////////////////////////////////////////////////////////////////////////////////////
// STORAGE
/////////////////////////////////////////////////////////////////////////////////////
const uploadFile = async (file, fileName) => {
return new Promise(async (resolve, reject) => {
try {
let fn = file.name
if (fileName) fn = fileName
const storageRef = ref(sto, fn)
const uploadTask = uploadBytesResumable(storageRef, file)
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
console.log('Upload is ' + progress + '% done')
switch (snapshot.state) {
case 'paused':
console.log('Upload is paused')
break
case 'running':
console.log('Upload is running')
break
}
},
(error) => {
console.log('upload error:', error)
switch (error.code) {
case 'storage/unauthorized':
ui.actions.notify('No autorizado', 'error') // User doesn't have permission to access the object
break
case 'storage/canceled':
ui.actions.notify('Subida cancelada', 'error')
break
case 'storage/unknown':
ui.actions.notify('Error desconocido', 'error')
break
}
reject(error)
},
async () => {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref)
console.log('File available at', downloadURL)
resolve(downloadURL)
}
)
} catch (error) {
console.log('Error al subir archivo:', error)
reject(error)
}
})
}
const deleteFile = async (bucket) => {
await deleteObject(ref(sto, bucket))
}
const getStoDownloadURL = async (bucket) => {
return await getDownloadURL(ref(sto, bucket))
}
//=========================================================
const fb = {
initFirebase,
db,
sto,
auth,
realtimeOn,
realtimeOff,
loginFirebase,
loginGoogle,
logout,
register,
forgotPassword,
addQuote,
onSnapshot,
deleteObject,
isInitialized,
getCurrentUser,
getCurrentUserQuote,
getCollection,
getCollectionRef,
getCollectionFlex,
getCollectionQuery,
getCollectionRefQuery,
getDocument,
getDocumentRef,
setDocument,
addDocument,
emptyCollection,
deleteDocument,
deleteCollection,
batchInsert,
transaction,
uploadFile,
deleteFile,
getStoDownloadURL,
cFunction
}
export default fb
/////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////
async function isOnline () {
const netRes = await Network.getStatus() //'wifi' | 'cellular' | 'none' | 'unknown'
const status = netRes.connectionType
const isOnline = (status === 'wifi' || status === 'cellular' || !status)
return isOnline
}
async function deleteQueryBatch (query, resolve) {
const snapshot = await query.get()
const batchSize = snapshot.size
if (batchSize === 0) { // When there are no documents left, we are done
resolve()
return
}
const batch = db.batch()
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref)
})
await batch.commit()
// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
deleteQueryBatch(query, resolve)
})
}
function removeUndefinedFields (data) {
for (const key in data) {
if (data[key] === undefined) {
delete data[key]
}
}
return data
}
function evalUndefinedFields (doc) {
let flag = false
for (const key in doc) {
if (doc[key] === undefined)
flag = true
}
return flag
}
async function sleep (tout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, tout)
})
}