Created
December 27, 2018 10:48
-
-
Save RubyLichtenstein/fa89f60b2c67b90c8af14e5dbc312f42 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
package com.tandoo.android.libs.repo | |
import android.content.Context | |
import com.google.firebase.firestore.CollectionReference | |
import com.google.firebase.firestore.DocumentChange | |
import com.tandoo.android.featuers.user.Cacheable | |
import com.tandoo.android.firebase.firestoreInstance | |
import com.tandoo.android.libs.mapParallel | |
import kotlinx.coroutines.channels.* | |
import kotlinx.coroutines.tasks.await | |
import kotlinx.serialization.KSerializer | |
import timber.log.Timber | |
interface IRepo<T : Cacheable> { | |
suspend fun getDoc(doc: String): T? | |
suspend fun getDocCache(doc: String): T? | |
suspend fun getDocCacheOrReal(doc: String): T? | |
suspend fun getCollectionCached(): List<T> | |
suspend fun getCollection(): List<T> | |
suspend fun delete(doc: String) | |
suspend fun setDoc(doc: String, any: T) | |
suspend fun getSubCollection(ids: List<String>): List<T> | |
suspend fun getSubCollectionCached(ids: List<String>): List<T> | |
fun observeDoc(doc: String): ReceiveChannel<T> | |
} | |
open class FirestoreCoroutines<T : Cacheable>( | |
val ref: String, | |
val collection: CollectionReference = firestoreInstance().collection(ref), | |
val context: Context, | |
val serializer: KSerializer<T>, | |
val clazz: Class<T> | |
) : IRepo<T> { | |
protected val dataCache = DataCache(context, serializer, clazz) | |
override suspend fun getDoc(doc: String): T? { | |
Timber.i("getDoc: collection: $collection, doc: $doc") | |
return collection.document(doc).get().await().toObject(clazz)?.also { | |
val cacheKey = createCacheKey(ref, doc) | |
dataCache.set(cacheKey, it) | |
} | |
} | |
override suspend fun getDocCacheOrReal(doc: String): T? { | |
return getDocCache(doc) ?: getDoc(doc) | |
} | |
override suspend fun getDocCache(doc: String): T? { | |
Timber.i("getDocCache: collection: $collection, doc: $doc") | |
val cacheKey = createCacheKey(ref, doc) | |
return dataCache.get(cacheKey) | |
} | |
private fun createCacheKey(ref: String, doc: String) = | |
"${ref.toLowerCase()}${doc.toLowerCase()}" | |
private suspend fun cacheList(collection: List<T>) { | |
dataCache.setList(ref, collection) | |
collection.forEach { | |
val cacheKey = createCacheKey(ref, it.id) | |
dataCache.set(cacheKey, it) | |
} | |
} | |
override suspend fun getSubCollection(ids: List<String>): List<T> { | |
return ids.mapParallel { getDoc(it) }.filterNotNull() | |
} | |
override suspend fun getSubCollectionCached(ids: List<String>): List<T> { | |
return ids.mapParallel { getDocCache(it) }.filterNotNull() | |
} | |
override suspend fun getCollectionCached(): List<T> { | |
Timber.i("getCollectionCached: collection: ${collection.path}") | |
return dataCache.getList(ref).orEmpty() | |
} | |
override suspend fun getCollection(): List<T> { | |
Timber.i("getCollection: collection: ${collection.path}") | |
return collection.get().await().toObjects(clazz) | |
.also { cacheList(it) } | |
} | |
override suspend fun setDoc(doc: String, any: T) { | |
Timber.i("setDoc: collection: $collection, doc: $doc") | |
collection.document(doc).set(any).await() | |
} | |
override suspend fun delete(doc: String) { | |
Timber.i("delete: collection: $collection, doc: $doc") | |
collection.document(doc).delete().await() | |
} | |
suspend fun update(doc: String, updateFieldsMap: Map<String, Any>) { | |
Timber.i("update: collection: $collection, doc: $doc, updateFieldsMap: $updateFieldsMap") | |
collection.document(doc).update(updateFieldsMap).await() | |
} | |
override fun observeDoc(doc: String): ReceiveChannel<T> { | |
Timber.i("observeDoc: collection: $collection, doc: $doc") | |
val channel = Channel<T>() | |
collection.document(doc).addSnapshotListener { querySnapshot, exception -> | |
exception?.let { | |
channel.close(it) | |
return@addSnapshotListener | |
} | |
if (querySnapshot == null) { | |
channel.close() | |
return@addSnapshotListener | |
} | |
querySnapshot.toObject(clazz)?.let { | |
channel.sendBlocking(it) | |
} | |
} | |
return channel | |
} | |
data class ChildEvent<T>(val child: T, val type: DocumentChange.Type) | |
fun observeAddedChildEvent(): ReceiveChannel<T> = | |
observeChildEvent() | |
.filter { it.type == DocumentChange.Type.ADDED } | |
.map { it.child } | |
fun observeChildEvent(): ReceiveChannel<ChildEvent<T>> { | |
val channel = Channel<ChildEvent<T>>() | |
collection.addSnapshotListener { querySnapshot, exception -> | |
exception?.let { | |
channel.close(it) | |
return@addSnapshotListener | |
} | |
if (querySnapshot == null) { | |
channel.close() | |
return@addSnapshotListener | |
} | |
querySnapshot.documentChanges | |
.mapNotNull { documentChange -> | |
ChildEvent(documentChange.document.toObject(clazz), documentChange.type) | |
} | |
.forEach { | |
channel.sendBlocking(it) | |
} | |
} | |
return channel | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment