Skip to content

Commit

Permalink
Converted in a backwards-compatible manner the Firestore Deck model t…
Browse files Browse the repository at this point in the history
…o use a shortened card metadata model for storing it's card lists.
  • Loading branch information
r0adkll committed Oct 25, 2018
1 parent d47b76e commit 4466f21
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.google.firebase.firestore.EventListener
import com.google.firebase.firestore.FirebaseFirestore
import com.r0adkll.deckbuilder.arch.data.AppPreferences
import com.r0adkll.deckbuilder.arch.data.features.decks.mapper.EntityMapper
import com.r0adkll.deckbuilder.arch.data.features.decks.mapper.EntityMapper.metadata
import com.r0adkll.deckbuilder.arch.data.features.decks.model.DeckEntity
import com.r0adkll.deckbuilder.arch.domain.features.cards.model.PokemonCard
import com.r0adkll.deckbuilder.arch.domain.features.cards.repository.CardRepository
Expand All @@ -29,56 +30,62 @@ class FirestoreDeckCache @Inject constructor(
) : DeckCache {

override fun getDeck(id: String): Observable<Deck> {
return cardRepository.getExpansions()
.flatMap { expansions ->
getUserDeckCollection()?.let { collection ->
val task = collection.document(id).get()
RxFirebase.from(task)
.map { it.toObject(DeckEntity::class.java) }
.map { EntityMapper.to(expansions, it, id) }
.subscribeOn(schedulers.firebase)
.doOnNext { Timber.d("Firebase::getDeck($id) - Thread(${Thread.currentThread()?.name})") }
} ?: Observable.error(FirebaseAuthException("-1", "no current user logged in"))
}
return getUserDeckCollection()?.let { collection ->
val task = collection.document(id).get()
RxFirebase.from(task)
.map { it.toObject(DeckEntity::class.java) }
.flatMap { deck ->
val cardIds = deck.metadata().map { it.id }.toHashSet()
cardRepository.find(cardIds.toList())
.map { EntityMapper.to(deck, it) }
}
.subscribeOn(schedulers.firebase)
.doOnNext { Timber.d("Firebase::getDeck($id) - Thread(${Thread.currentThread()?.name})") }
} ?: Observable.error(FirebaseAuthException("-1", "no current user logged in"))
}


override fun getDecks(): Observable<List<Deck>> {
return cardRepository.getExpansions()
.flatMap { expansions ->
Observable.create<List<Deck>> { emitter ->
getUserDeckCollection()?.let { collection ->
val registration = collection.addSnapshotListener(schedulers.firebaseExecutor, EventListener { snapshot, exception ->
if (exception != null) {
emitter.onError(exception)
return@EventListener
}

val decks = ArrayList<Deck>()
snapshot?.forEach { document ->
val deck = document.toObject(DeckEntity::class.java)
decks.add(EntityMapper.to(expansions, deck, document.id))
}


Timber.d("Firebase::getDecks()::onNext() - Thread(${Thread.currentThread()?.name})")
emitter.onNext(decks)
})

emitter.setCancellable {
registration.remove()
}
} ?: emitter.onError(FirebaseAuthException("-1", "No current user logged in"))
return Observable.create<List<DeckEntity>> { emitter ->
getUserDeckCollection()?.let { collection ->
val registration = collection.addSnapshotListener(schedulers.firebaseExecutor, EventListener { snapshot, exception ->
if (exception != null) {
emitter.onError(exception)
return@EventListener
}

val decks = ArrayList<DeckEntity>()
snapshot?.forEach { document ->
val deckEntity = document.toObject(DeckEntity::class.java)
deckEntity.id = document.id
decks += deckEntity
}

emitter.onNext(decks)
})

emitter.setCancellable {
registration.remove()
}
} ?: emitter.onError(FirebaseAuthException("-1", "No current user logged in"))
}
.flatMap { decks ->
val cardIds = decks.flatMap {
it.metadata().map { it.id }
}.toHashSet() // Convert to set so we don't request duplicate id's

cardRepository.find(cardIds.toList())
.map { cards ->
decks.map { EntityMapper.to(it, cards) }
}
.subscribeOn(schedulers.firebase)
.doOnNext { Timber.d("Firebase::getDecks() - Thread(${Thread.currentThread()?.name})") }
}
.subscribeOn(schedulers.firebase)
}


override fun putDeck(id: String?, cards: List<PokemonCard>, name: String, description: String?, image: DeckImage?): Observable<Deck> {
return getUserDeckCollection()?.let { collection ->
val newDeck = Deck(id ?: "", name, description ?: "", image, cards, System.currentTimeMillis())
val newDeck = Deck(id ?: "", name, description ?: "", image, cards, false, System.currentTimeMillis())
val model = EntityMapper.to(newDeck)
if (id == null) {
val task = collection.add(model)
Expand Down Expand Up @@ -115,7 +122,7 @@ class FirestoreDeckCache @Inject constructor(

val cleanName = deck.name.replace(regex, "").trim()
val newName = "$cleanName ($count)"
duplicateDeck(Deck("", newName, deck.description, deck.image, deck.cards, deck.timestamp))
duplicateDeck(Deck("", newName, deck.description, deck.image, deck.cards, false, deck.timestamp))
}
}
.map { Unit }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ package com.r0adkll.deckbuilder.arch.data.features.decks.mapper


import android.net.Uri
import android.util.ArrayMap
import com.r0adkll.deckbuilder.arch.data.features.decks.model.*
import com.r0adkll.deckbuilder.arch.domain.features.cards.model.*
import com.r0adkll.deckbuilder.arch.domain.features.decks.model.Deck
import com.r0adkll.deckbuilder.arch.ui.features.deckbuilder.deckimage.adapter.DeckImage
import com.r0adkll.deckbuilder.util.compactEffects
import com.r0adkll.deckbuilder.util.compactTypes
import com.r0adkll.deckbuilder.util.deserializeEffects
import com.r0adkll.deckbuilder.util.deserializeTypes
import com.r0adkll.deckbuilder.util.*
import io.pokemontcg.model.SubType
import io.pokemontcg.model.SuperType
import io.pokemontcg.model.Type
Expand All @@ -18,28 +16,60 @@ import io.pokemontcg.model.Type
object EntityMapper {

fun to(deck: Deck): DeckEntity {
return DeckEntity(
return DeckEntity("",
deck.name,
deck.description,
deck.image?.uri?.toString(),
deck.cards.map { to(it) },
/* Deprecated */ emptyList(),
deck.cards.stack().map { to(it) },
System.currentTimeMillis()
)
}


fun to(expansions: List<Expansion>, entity: DeckEntity, id: String): Deck {
fun to(entity: DeckEntity, cards: List<PokemonCard>, isMissingCards: Boolean): Deck {
return Deck(
id,
entity.id,
entity.name,
entity.description,
entity.image?.let { DeckImage.from(Uri.parse(it)) },
entity.cards.map { to(it, expansions) },
cards,
isMissingCards,
entity.timestamp
)
}


fun to(entity: DeckEntity, cards: List<PokemonCard>): Deck {
var isMissingCards = false
val stackedCards = ArrayList<StackedPokemonCard>()
val metadata = entity.metadata()

metadata.forEach { meta ->
// find card
val card = cards.find { it.id == meta.id }
if (card != null) {
stackedCards.add(StackedPokemonCard(card, meta.count))
} else {
isMissingCards = true
}
}

return EntityMapper.to(entity, stackedCards.unstack(), isMissingCards)
}


fun to(stackedCard: StackedPokemonCard): CardMetadataEntity {
return CardMetadataEntity(
stackedCard.card.id,
stackedCard.card.supertype.displayName,
stackedCard.card.imageUrl,
stackedCard.card.imageUrlHiRes,
stackedCard.count
)
}


fun to(card: PokemonCard): PokemonCardEntity {
return PokemonCardEntity(
card.id,
Expand Down Expand Up @@ -96,69 +126,25 @@ object EntityMapper {
}


fun to(expansion: Expansion): ExpansionEntity {
return ExpansionEntity(
expansion.code,
expansion.ptcgoCode,
expansion.name,
expansion.series,
expansion.totalCards,
expansion.standardLegal,
expansion.expandedLegal,
expansion.releaseDate,
expansion.symbolUrl,
expansion.logoUrl
)
fun to(entity: PokemonCardEntity, count: Int): CardMetadataEntity {
return CardMetadataEntity(entity.id, entity.supertype, entity.imageUrl, entity.imageUrlHiRes, count)
}


fun to(entity: ExpansionEntity): Expansion {
return Expansion(
entity.code,
entity.ptcgoCode,
entity.name,
entity.series,
entity.totalCards,
entity.standardLegal,
entity.expandedLegal,
entity.releaseDate,
entity.symbolUrl,
entity.logoUrl
)
}


fun to(attack: Attack): AttackEntity {
return AttackEntity(
// attack.cost.map { it.displayName },
attack.name,
attack.text ?: "",
attack.damage,
attack.convertedEnergyCost
)
}


fun to(entity: AttackEntity): Attack {
return Attack(
emptyList(),
entity.name,
entity.text,
entity.damage,
entity.convertedEnergyCost
)
}


fun to(effect: Effect): EffectEntity {
return EffectEntity(
effect.type.displayName,
effect.value
)
fun DeckEntity.metadata(): List<CardMetadataEntity> {
return this.cardMetadata
?: this.cards.stackCards().map {
EntityMapper.to(it.first, it.second)
}
}


fun to(entity: EffectEntity): Effect {
return Effect(Type.find(entity.type), entity.value)
fun List<PokemonCardEntity>.stackCards(): List<Pair<PokemonCardEntity, Int>> {
val map = ArrayMap<PokemonCardEntity, Int>(this.size)
this.forEach { card ->
val count = map[card] ?: 0
map[card] = count + 1
}
return map.map { Pair(it.key, it.value) }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package com.r0adkll.deckbuilder.arch.data.features.decks.model
* @param imageUrl the HiRes card image url
* @param count the number of instances of this card in the deck
*/
class CardEntity(
class CardMetadataEntity(
val id: String = "",
val superType: String = "",
val imageUrl: String = "",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.r0adkll.deckbuilder.arch.data.features.decks.model

import com.google.firebase.firestore.Exclude


class DeckEntity(
@Exclude var id: String = "",
val name: String = "",
val description: String = "",
val image: String? = null,
@Deprecated("Use 'cardStacks'") val cards: List<PokemonCardEntity> = emptyList(),
val cardStacks: List<CardEntity>? = null,

@Deprecated("No longer used", replaceWith = ReplaceWith("cardMetadata"))
val cards: List<PokemonCardEntity> = emptyList(),

val cardMetadata: List<CardMetadataEntity>? = null,
val timestamp: Long = 0L
)

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.r0adkll.deckbuilder.arch.data.features.decks.model

@Deprecated(
message = "Moving away from storing full card object in Firestore",
replaceWith = ReplaceWith("CardEntity", "com.deckbuilder.arch.data.features.decks.model.CardEntity")
replaceWith = ReplaceWith("CardMetadataEntity", "com.deckbuilder.arch.data.features.decks.model.CardMetadataEntity")
)
class PokemonCardEntity(
val id: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ class DefaultDeckRepository @Inject constructor(
override fun getDeck(id: String): Observable<Deck> {
return cache.getDeck(id)
.subscribeOn(schedulers.firebase)
.doOnNext { Timber.d("Repository::getDeck($id) - Thread(${Thread.currentThread()?.name})") }
}


override fun getDecks(): Observable<List<Deck>> {
return cache.getDecks()
.subscribeOn(schedulers.firebase)
.doOnNext { Timber.d("Repository::getDecks() - Thread(${Thread.currentThread()?.name})") }
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class Deck(
val description: String,
val image: DeckImage?,
val cards: List<PokemonCard>,
val isMissingCards: Boolean,
val timestamp: Long
) : PaperParcelable {

Expand All @@ -24,7 +25,7 @@ data class Deck(


override fun toString(): String {
return "Deck(id='$id', name='$name', description='$description', cards=${cards.size}, timestamp=$timestamp)"
return "Deck(id='$id', name='$name', description='$description', cards=${cards.size}, isMissingCards=$isMissingCards, timestamp=$timestamp)"
}

companion object {
Expand Down
Loading

0 comments on commit 4466f21

Please sign in to comment.