Skip to content

feat: Add paging for fetching similar events #2078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ val viewModelModule = module {
viewModel { EventsViewModel(get(), get(), get(), get(), get(), get(), get()) }
viewModel { ProfileViewModel(get(), get()) }
viewModel { SignUpViewModel(get(), get(), get()) }
viewModel { EventDetailsViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
viewModel {
EventDetailsViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
viewModel { SessionViewModel(get(), get(), get()) }
viewModel { SearchViewModel(get(), get()) }
viewModel { SearchResultsViewModel(get(), get(), get(), get(), get()) }
Expand All @@ -228,7 +229,7 @@ val viewModelModule = module {
viewModel { EventFAQViewModel(get(), get()) }
viewModel { FavoriteEventsViewModel(get(), get()) }
viewModel { SettingsViewModel(get()) }
viewModel { OrderCompletedViewModel(get(), get()) }
viewModel { OrderCompletedViewModel(get(), get(), get()) }
viewModel { OrdersUnderUserViewModel(get(), get(), get(), get()) }
viewModel { OrderDetailsViewModel(get(), get(), get(), get()) }
viewModel { EditProfileViewModel(get(), get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,10 @@ class EventDetailsFragment : Fragment() {
rootView.shimmerSimilarEvents.isVisible = it
if (it) {
rootView.shimmerSimilarEvents.startShimmer()
rootView.similarEventsContainer.isVisible = true
} else {
rootView.shimmerSimilarEvents.stopShimmer()
rootView.similarEventsContainer.isVisible = similarEventsAdapter.currentList?.isEmpty() ?: true
}
})

Expand All @@ -382,9 +384,10 @@ class EventDetailsFragment : Fragment() {
rootView.similarEventsRecycler.layoutManager = similarLinearLayoutManager
rootView.similarEventsRecycler.adapter = similarEventsAdapter

eventViewModel.similarEvents.observe(viewLifecycleOwner, Observer { similarEvents ->
similarEventsAdapter.submitList(similarEvents.toList())
rootView.similarEventsContainer.isVisible = similarEvents.isNotEmpty()
eventViewModel.similarEvents
.nonNull()
.observe(viewLifecycleOwner, Observer { similarEvents ->
similarEventsAdapter.submitList(similarEvents)
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.fossasia.openevent.general.event

import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.paging.PagedList
import androidx.paging.RxPagedListBuilder
import io.reactivex.android.schedulers.AndroidSchedulers

import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.schedulers.Schedulers
import org.fossasia.openevent.general.BuildConfig.MAPBOX_KEY
import org.fossasia.openevent.general.R
import org.fossasia.openevent.general.auth.AuthHolder
Expand All @@ -15,6 +19,7 @@ import org.fossasia.openevent.general.auth.UserId
import org.fossasia.openevent.general.common.SingleLiveEvent
import org.fossasia.openevent.general.connectivity.MutableConnectionLiveData
import org.fossasia.openevent.general.data.Resource
import org.fossasia.openevent.general.event.paging.SimilarEventsDataSourceFactory
import org.fossasia.openevent.general.feedback.Feedback
import org.fossasia.openevent.general.feedback.FeedbackService
import org.fossasia.openevent.general.order.Order
Expand Down Expand Up @@ -44,7 +49,8 @@ class EventDetailsViewModel(
private val feedbackService: FeedbackService,
private val resource: Resource,
private val orderService: OrderService,
private val mutableConnectionLiveData: MutableConnectionLiveData
private val mutableConnectionLiveData: MutableConnectionLiveData,
private val config: PagedList.Config
) : ViewModel() {

private val compositeDisposable = CompositeDisposable()
Expand Down Expand Up @@ -72,10 +78,10 @@ class EventDetailsViewModel(
val eventSponsors: LiveData<List<Sponsor>> = mutableEventSponsors
private val mutableSocialLinks = MutableLiveData<List<SocialLink>>()
val socialLinks: LiveData<List<SocialLink>> = mutableSocialLinks
private val mutableSimilarEvents = MutableLiveData<List<Event>>()
val similarEvents: LiveData<List<Event>> = mutableSimilarEvents
private val mutableSimilarEventsProgress = MutableLiveData<Boolean>()
val similarEventsProgress: LiveData<Boolean> = mutableSimilarEventsProgress
private val mutableSimilarEvents = MutableLiveData<PagedList<Event>>()
val similarEvents: LiveData<PagedList<Event>> = mutableSimilarEvents
private val mutableSimilarEventsProgress = MediatorLiveData<Boolean>()
val similarEventsProgress: MediatorLiveData<Boolean> = mutableSimilarEventsProgress
private val mutableOrders = MutableLiveData<List<Order>>()
val orders: LiveData<List<Order>> = mutableOrders
private val mutablePriceRange = MutableLiveData<String>()
Expand Down Expand Up @@ -155,27 +161,35 @@ class EventDetailsViewModel(
fun fetchSimilarEvents(eventId: Long, topicId: Long, location: String?) {
if (eventId == -1L) return

var similarEventsFlowable = eventService.getEventsByLocation(location)
if (topicId != -1L) {
similarEventsFlowable = similarEventsFlowable.zipWith(eventService.getSimilarEvents(topicId),
BiFunction { firstList: List<Event>, secondList: List<Event> ->
val similarList = mutableSetOf<Event>()
similarList.addAll(firstList + secondList)
similarList.toList()
})
}
compositeDisposable += similarEventsFlowable
.withDefaultSchedulers()
val sourceFactory = SimilarEventsDataSourceFactory(
compositeDisposable,
topicId,
location,
eventId,
mutableSimilarEventsProgress,
eventService
)

val similarEventPagedList = RxPagedListBuilder(sourceFactory, config)
.setFetchScheduler(Schedulers.io())
.buildObservable()
.cache()

compositeDisposable += similarEventPagedList
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.distinctUntilChanged()
.doOnSubscribe {
mutableSimilarEventsProgress.value = true
}
.subscribe({ events ->
mutableSimilarEventsProgress.value = false
val list = events.filter { it.id != eventId }
mutableSimilarEvents.value = list
}.subscribe({ events ->
val currentPagedSimilarEvents = mutableSimilarEvents.value
if (currentPagedSimilarEvents == null) {
mutableSimilarEvents.value = events
} else {
currentPagedSimilarEvents.addAll(events)
mutableSimilarEvents.value = currentPagedSimilarEvents
}
}, {
mutableSimilarEventsProgress.value = false
Timber.e(it, "Error fetching similar events")
mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message,
resource.getString(R.string.similar_events))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ class EventService(
}
}

fun getEventsByLocationPaged(locationName: String?, page: Int): Flowable<List<Event>> {
fun getEventsByLocationPaged(locationName: String?, page: Int, pageSize: Int = 5): Flowable<List<Event>> {
val query = "[{\"name\":\"location-name\",\"op\":\"ilike\",\"val\":\"%$locationName%\"}," +
"{\"name\":\"ends-at\",\"op\":\"ge\",\"val\":\"%${EventUtils.getTimeInISO8601(Date())}%\"}]"
return eventApi.searchEventsPaged("name", query, page).flatMapPublisher { apiList ->
return eventApi.searchEventsPaged("name", query, page, pageSize).flatMapPublisher { apiList ->
updateFavorites(apiList)
}
}
Expand Down Expand Up @@ -132,6 +132,13 @@ class EventService(
}
}

fun getSimilarEventsPaged(id: Long, page: Int, pageSize: Int = 5): Flowable<List<Event>> {
return eventTopicApi.getEventsUnderTopicIdPaged(id, page, pageSize)
.flatMapPublisher {
updateFavorites(it)
}
}

fun getSpeakerCall(id: Long): Single<SpeakersCall> =
speakersCallDao.getSpeakerCall(id).onErrorResumeNext {
eventApi.getSpeakerCallForEvent(id).doAfterSuccess {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.fossasia.openevent.general.event.paging

import androidx.lifecycle.MutableLiveData
import androidx.paging.PageKeyedDataSource
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.plusAssign
import org.fossasia.openevent.general.event.Event
import org.fossasia.openevent.general.event.EventService
import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers
import timber.log.Timber

class SimilarEventsDataSource(
private val compositeDisposable: CompositeDisposable,
private val topicId: Long,
private val location: String?,
private val eventId: Long,
private val mutableProgress: MutableLiveData<Boolean>,
private val eventService: EventService
) : PageKeyedDataSource<Int, Event>() {

override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, Event>
) {
createObservable(1, 2, callback, null)
}

override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Event>) {
val page = params.key
createObservable(page, page + 1, null, callback)
}

override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Event>) {
val page = params.key
createObservable(page, page - 1, null, callback)
}

private fun createObservable(
requestedPage: Int,
adjacentPage: Int,
initialCallback: LoadInitialCallback<Int, Event>?,
callback: LoadCallback<Int, Event>?
) {
var similarEventsFlowable = eventService.getEventsByLocationPaged(location, requestedPage, 3)
if (topicId != -1L) {
similarEventsFlowable = similarEventsFlowable
.zipWith(eventService.getSimilarEventsPaged(topicId, requestedPage, 3),
BiFunction { firstList: List<Event>, secondList: List<Event> ->
val similarList = mutableSetOf<Event>()
similarList.addAll(firstList + secondList)
similarList.toList()
})
}

compositeDisposable += similarEventsFlowable
.take(1)
.withDefaultSchedulers()
.subscribe({ response ->
if (response.isEmpty()) {
mutableProgress.value = false
}
initialCallback?.onResult(response.filter { it.id != eventId }, null, adjacentPage)
callback?.onResult(response, adjacentPage)
}, { error ->
Timber.e(error, "Fail on fetching page of events")
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.fossasia.openevent.general.event.paging

import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
import io.reactivex.disposables.CompositeDisposable
import org.fossasia.openevent.general.event.Event
import org.fossasia.openevent.general.event.EventService

class SimilarEventsDataSourceFactory(
private val compositeDisposable: CompositeDisposable,
private val topicId: Long,
private val location: String?,
private val eventId: Long,
private val mutableProgress: MutableLiveData<Boolean>,
private val eventService: EventService
) : DataSource.Factory<Int, Event>() {
override fun create(): DataSource<Int, Event> {
return SimilarEventsDataSource(
compositeDisposable, topicId, location, eventId, mutableProgress, eventService
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package org.fossasia.openevent.general.event.similarevent

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.paging.PagedListAdapter
import org.fossasia.openevent.general.common.EventClickListener
import org.fossasia.openevent.general.common.EventsDiffCallback
import org.fossasia.openevent.general.common.FavoriteFabClickListener
import org.fossasia.openevent.general.databinding.ItemCardSimilarEventsBinding
import org.fossasia.openevent.general.event.Event

class SimilarEventsListAdapter : ListAdapter<Event, SimilarEventViewHolder>(EventsDiffCallback()) {
class SimilarEventsListAdapter : PagedListAdapter<Event, SimilarEventViewHolder>(EventsDiffCallback()) {

var onEventClick: EventClickListener? = null
var onFavFabClick: FavoriteFabClickListener? = null
Expand All @@ -21,18 +21,16 @@ class SimilarEventsListAdapter : ListAdapter<Event, SimilarEventViewHolder>(Even

override fun onBindViewHolder(holder: SimilarEventViewHolder, position: Int) {
val event = getItem(position)
holder.apply {
bind(event, position)
eventClickListener = onEventClick
favFabClickListener = onFavFabClick
if (event != null) {
holder.apply {
bind(event, position)
eventClickListener = onEventClick
favFabClickListener = onFavFabClick
}
}
}

fun clear() {
this.submitList(emptyList())
this.submitList(null)
}
}

interface EventHashTagClickListener {
fun onClick(hashTagValue: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import io.reactivex.Single
import org.fossasia.openevent.general.event.Event
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface EventTopicApi {

@GET("event-topics/{id}/events?include=event-topic")
fun getEventsUnderTopicId(@Path("id") id: Long): Single<List<Event>>

@GET("event-topics")
fun getEventTopics(): Single<List<EventTopic>>

@GET("events/{id}/event-topic")
fun getEventTopicOfEvent(@Path("id") id: Long): Single<EventTopic>

@GET("event-topics/{id}")
fun getEventTopic(@Path("id") id: Long): Single<EventTopic>
@GET("event-topics/{id}/events?include=event-topic")
fun getEventsUnderTopicIdPaged(
@Path("id") id: Long,
@Query("page[number]") page: Int,
@Query("page[size]") pageSize: Int = 5
): Single<List<Event>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ class OrderCompletedFragment : Fragment() {
orderCompletedViewModel.similarEvents
.nonNull()
.observe(viewLifecycleOwner, Observer {
similarEventsAdapter.submitList(it.toList())
rootView.similarEventLayout.isVisible = it.isNotEmpty()
similarEventsAdapter.submitList(it)
})

orderCompletedViewModel.message
Expand All @@ -101,8 +100,10 @@ class OrderCompletedFragment : Fragment() {
rootView.shimmerSimilarEvents.isVisible = it
if (it) {
rootView.shimmerSimilarEvents.startShimmer()
rootView.similarEventLayout.isVisible = true
} else {
rootView.shimmerSimilarEvents.stopShimmer()
rootView.isVisible = similarEventsAdapter.currentList?.isEmpty() ?: true
}
})

Expand Down
Loading