Commit e8b55308 authored by Vladislav's avatar Vladislav

fetch estates with update

parent e6aa7f16
......@@ -175,6 +175,10 @@ dependencies {
//RxKotlin
implementation("io.reactivex.rxjava2:rxkotlin:$rxKotlinVersion")
//Arch Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
//Tests
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
......
......@@ -16,6 +16,7 @@ import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.model.fromEntity
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.rxkotlin.Observables
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import javax.inject.Inject
......@@ -37,17 +38,31 @@ class EstateRepository @Inject constructor(
Timber.d("Estate Repository Created $this")
}
override fun fetchFavorites(user:UserEntity): Observable<Iterable<EstateEntity>> =
override fun fetchFavorites(user:UserEntity): Observable<List<EstateEntity>> =
Observables.zip(
api.getFavorites(user.authToken)
.doOnError(Timber::e)
.map { fromRawList(it, ::fromRaw) }
.doOnNext {
it.forEach { estate ->
estate.setFavorite(true)
estate.user = user
}
,db.getUserFavorites(user.uuid)
.defaultIfEmpty(null).toList().toObservable()
.doOnError(Timber::e)
){apiList,dbList ->
if (dbList.isEmpty())
return@zip apiList
dbList
.filterNotNull()
.filter {dbFav -> apiList.map{it.id}.contains(dbFav.id) }
.let{ db.deleteEstate(it) }
apiList.forEach { estate ->
estate.setFavorite(true)
estate.user = user
}
return@zip apiList
}
.flatMap(db::upsertEstates)
.map { it.toList() }
.doOnNext{ db.refreshUser(user) }
override fun fetchDeals(user:UserEntity): Observable<List<EstateEntity>> =
......@@ -80,7 +95,7 @@ class EstateRepository @Inject constructor(
.doOnNext{ db.refreshUser(user) }
}
private val getFavoritesDb: Observable<List<EstateEntity>> =
private val getFavoritesDb: Observable<MutableList<EstateEntity?>>? =
local.recentUser()
.flatMap {
when (it) {
......@@ -95,12 +110,14 @@ class EstateRepository @Inject constructor(
}
override fun getFavorites(): Observable<List<EstateModel>> {
override fun getFavorites(user:UserEntity): Observable<List<EstateModel>> {
return Observable.mergeDelayError(
arrayListOf(
getFavoritesApi, getFavoritesDb
fetchFavorites(user), getFavoritesDb
)
).map { fromEntity(it, ::fromEntity) }
)
.map { it.filterNotNull() }
.map { fromEntity(it, ::fromEntity) }
.doOnError(Timber::e)
}
......
package com.biganto.visual.roompark.data.memcache
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import com.jakewharton.rxrelay2.Relay
import com.jakewharton.rxrelay2.ReplayRelay
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
/**
* Created by Vladislav Bogdashkin on 19.10.2018.
*/
/**
* @param[maxSize] max entries count before automatic save && flush
* @param[flushInterval] time without any actions with cache in SECONDS before automatic save && flush
* */
abstract class EntityCache<K,V>
constructor(
private val maxSize: Int = 300,
private val flushInterval: Long = TimeUnit.MINUTES.toSeconds(1)
) : Cache<K,V> {
private val disposable = CompositeDisposable()
protected abstract fun saveDelegate (values:List<V>)
protected abstract fun readDelegate (key:K) : V?
protected abstract fun deleteDelegate (value:V)
protected val notifier: Relay<V> = ReplayRelay.create<V>().toSerialized()
init {
disposable.addAll(
Observable.interval(flushInterval, flushInterval, TimeUnit.SECONDS)
.doOnTerminate { clear() }
.doOnDispose { clear() }
.subscribe { clear() }
)
}
override fun contains(key: K): Boolean = keyMap.containsKey(key)
protected val locker = Any()
private var lastFlushTime = System.nanoTime()
private val keyMap = ConcurrentHashMap<K, V>()
override val size: Int
get() = keyMap.size
override val toList
get() = keyMap.toList()
fun deleteEntity(key: K){
synchronized(locker) {
deleteItem(key)
}
}
private fun deleteItem(key:K){
synchronized(locker) {
lastFlushTime = System.nanoTime()
var toDelete = keyMap.remove(key)
if (toDelete == null)
toDelete = readDelegate(key)
if (toDelete == null) return // -> нет в хранилище
deleteDelegate(value = toDelete)
}
}
fun deleteEntitys(keys: List<K>){
synchronized(locker) {
keys.forEach { deleteItem(it)}
}
}
override fun set(key: K, value: V?) {
synchronized(locker) {
lastFlushTime = System.nanoTime()
if (keyMap.size > maxSize)
clear()
value?.let {
keyMap[key]=it
notifier.accept(it)
}
}
}
override fun remove(key: K) = keyMap.remove(key)
override fun get(key: K): V? {
synchronized(locker) {
lastFlushTime = System.nanoTime()
return keyMap[key] ?: readDelegate(key)
}
}
override fun saveAll(){
synchronized(locker)
{
if (keyMap.size > 0) {
Timber.d("Going to save items: ${keyMap.values.size}")
saveDelegate(keyMap.values.toList())
}
}
}
override fun clear() {
if (keyMap.size == 0) return
synchronized(locker)
{
saveAll()
keyMap.clear()
}
}
override fun removeAll(): List<V>? {
val values= keyMap.values.toList()
keyMap.clear()
return values
}
private fun recycle() {
if (keyMap.size<=0) return
val shouldRecycle = System.nanoTime() - lastFlushTime >= TimeUnit.MILLISECONDS.toNanos(flushInterval)
if (!shouldRecycle) return
keyMap.clear()
}
}
interface Cache<K,V> {
val size: Int
val toList:List<Pair<K,V>>
operator fun set(key: K, value: V?)
fun contains(key: K): Boolean
operator fun get(key: K): V?
fun remove(key: K): V?
fun removeAll(): List<V>?
fun clear()
fun saveAll()
}
/**
* Suppress warning Leaking This as we sure to make singletone instance of object in single-thread
* more info and
* @see <a href="https://stackoverflow.com/questions/3921616/leaking-this-in-constructor-warning">discussion</a>
*/
@Suppress("LeakingThis")
abstract class LifeCycleCache<K,V>(size:Int, secondsToFlush:Long)
: EntityCache<K, V>(maxSize=size,flushInterval = secondsToFlush)
, LifecycleObserver
{
init {
ProcessLifecycleOwner.get().lifecycle
.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onAppPaused() {
saveAll()
}
}
......@@ -32,7 +32,7 @@ interface IDb {
fun getPhotos(albumId: Int): Observable<GalleryPhotoEntity>
fun getPhoto(photoId: Int): Observable<GalleryPhotoEntity>
fun getAlbum(albumId: Int): Observable<ImageAlbumEntity>
fun getUserFavorites(uuid: Int): Observable<EstateEntity>
fun getUserFavorites(uuid: Int): Observable<EstateEntity?>
fun fetchAllUsers(): Observable<List<UserEntity>>
fun getEstate(estateId: Int): Observable<EstateEntity>
fun upsertEstate(entity: EstateEntity)
......@@ -78,4 +78,6 @@ interface IDb {
fun upsertEstates(entity: List<EstateEntity>): Observable<Iterable<EstateEntity>>?
fun upsertDeals(entity: List<DealEntity>): Observable<Iterable<DealEntity>>
fun deleteSubscriptions(entities: List<Subscription>)
fun deleteEstate(entity: List<EstateEntity>)
fun deleteEstate(entity: EstateEntity)
}
\ No newline at end of file
......@@ -190,7 +190,7 @@ class RequeryRepository @Inject constructor(
.get()
.observableResult()
override fun getUserFavorites(uuid: Int): Observable<EstateEntity> =
override fun getUserFavorites(uuid: Int): Observable<EstateEntity?> =
store.select(EstateEntity::class)
.where(EstateEntity.USER_ID.eq(uuid))
.and(EstateEntity.FAVORITE.eq(true))
......@@ -409,6 +409,10 @@ class RequeryRepository @Inject constructor(
.where(TourFileJunctionEntity.TOUR.eq(tourId))
.get()
override fun deleteEstate(entity:EstateEntity) = deleteBlocking(entity)
override fun deleteEstate(entity:List<EstateEntity>) = deleteBlocking(entity)
override fun deleteFile(entity:FileEntity) = deleteBlocking(entity)
override fun deleteFiles(entity:List<FileEntity>) = deleteBlocking(entity)
......
package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.data.repository.db.requrey.model.DealEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.UserEntity
import com.biganto.visual.roompark.domain.model.DealModel
......@@ -15,11 +14,12 @@ import io.reactivex.Observable
interface DealContract{
fun getFavorites() : Observable<List<EstateModel>>
// fun getFavorites() : Observable<List<EstateModel>>
fun getEstate(estateId: Int): Observable<EstateModel>
fun getDeals(): Observable<List<DealModel>>
fun fetchEstate(building: Int, number: Int): Observable<EstateModel>
fun setDealRead(dealId: String): Completable
fun fetchDeals(user: UserEntity): Observable<List<EstateEntity>>
fun fetchFavorites(user: UserEntity): Observable<Iterable<EstateEntity>>
fun fetchFavorites(user: UserEntity): Observable<List<EstateEntity>>
fun getFavorites(user: UserEntity): Observable<List<EstateModel>>
}
\ No newline at end of file
package com.biganto.visual.roompark.domain.use_case
import com.biganto.visual.roompark.domain.contract.AuthContract
import com.biganto.visual.roompark.domain.contract.DealContract
import com.biganto.visual.roompark.domain.contract.FlatPlanContract
import com.biganto.visual.roompark.domain.model.EstateModel
import io.reactivex.Observable
import javax.inject.Inject
/**
......@@ -10,10 +13,13 @@ import javax.inject.Inject
class EstateUseCase @Inject constructor(
private val contract: DealContract,
private val planContract: FlatPlanContract
private val planContract: FlatPlanContract,
private val authContract: AuthContract
) {
fun fetchFavorites() = contract.getFavorites()
fun fetchFavorites(): Observable<List<EstateModel>> =
authContract.currentUser()
.flatMap (contract::getFavorites)
fun getEstate(estateId: Int) = contract.getEstate(estateId)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment