mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-21 21:35:03 +00:00
Implement basic pagination for challenges
This commit is contained in:
parent
74e936b649
commit
4b27e3858a
11 changed files with 71 additions and 47 deletions
|
|
@ -148,7 +148,7 @@ android {
|
|||
buildConfigField "String", "STORE", "\"google\""
|
||||
multiDexEnabled true
|
||||
|
||||
versionCode 2053
|
||||
versionCode 2055
|
||||
versionName "1.7"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,7 +302,9 @@ public interface ApiService {
|
|||
/* challenges api */
|
||||
|
||||
@GET("challenges/user")
|
||||
Flowable<HabitResponse<List<Challenge>>> getUserChallenges();
|
||||
Flowable<HabitResponse<List<Challenge>>> getUserChallenges(@Query("page") Integer page, @Query("member") boolean memberOnly);
|
||||
@GET("challenges/user")
|
||||
Flowable<HabitResponse<List<Challenge>>> getUserChallenges(@Query("page") Integer page);
|
||||
|
||||
@GET("tasks/challenge/{challengeId}")
|
||||
Flowable<HabitResponse<TaskList>> getChallengeTasks(@Path("challengeId") String challengeId);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ interface ApiClient {
|
|||
|
||||
/* challenges api */
|
||||
|
||||
val userChallenges: Flowable<List<Challenge>>
|
||||
fun getUserChallenges(page: Int, memberOnly: Boolean): Flowable<List<Challenge>>
|
||||
|
||||
val worldState: Flowable<WorldState>
|
||||
fun setLanguageCode(languageCode: String)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import io.realm.RealmResults
|
|||
|
||||
interface ChallengeRepository : BaseRepository {
|
||||
|
||||
fun retrieveChallenges(user: User): Flowable<List<Challenge>>
|
||||
fun retrieveChallenges(page: Int = 0, memberOnly: Boolean): Flowable<List<Challenge>>
|
||||
fun getChallenges(): Flowable<RealmResults<Challenge>>
|
||||
fun getChallenge(challengeId: String): Flowable<Challenge>
|
||||
fun getChallengeTasks(challengeId: String): Flowable<RealmResults<Task>>
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ import java.util.*
|
|||
interface SocialRepository : BaseRepository {
|
||||
fun getPublicGuilds(): Flowable<RealmResults<Group>>
|
||||
|
||||
fun getUserChallenges(): Flowable<List<Challenge>>
|
||||
|
||||
fun getUserGroups(): Flowable<RealmResults<Group>>
|
||||
fun retrieveGroupChat(groupId: String): Single<List<ChatMessage>>
|
||||
fun getGroupChat(groupId: String): Flowable<RealmResults<ChatMessage>>
|
||||
|
|
|
|||
|
|
@ -599,8 +599,13 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
|
|||
return apiService.deletePushDevice(regId).compose(configureApiCallObserver())
|
||||
}
|
||||
|
||||
override val userChallenges: Flowable<List<Challenge>>
|
||||
get() = apiService.userChallenges.compose(configureApiCallObserver())
|
||||
override fun getUserChallenges(page: Int, memberOnly: Boolean): Flowable<List<Challenge>> {
|
||||
return if (memberOnly) {
|
||||
apiService.getUserChallenges(page, memberOnly).compose(configureApiCallObserver())
|
||||
} else {
|
||||
apiService.getUserChallenges(page).compose(configureApiCallObserver())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChallengeTasks(challengeId: String): Flowable<TaskList> {
|
||||
return apiService.getChallengeTasks(challengeId).compose(configureApiCallObserver())
|
||||
|
|
|
|||
|
|
@ -9,14 +9,12 @@ import com.habitrpg.android.habitica.models.social.ChallengeMembership
|
|||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.tasks.TaskList
|
||||
import com.habitrpg.android.habitica.models.tasks.TasksOrder
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import io.reactivex.Flowable
|
||||
import io.realm.RealmResults
|
||||
|
||||
|
||||
class ChallengeRepositoryImpl(localRepository: ChallengeLocalRepository, apiClient: ApiClient, userID: String) : BaseRepositoryImpl<ChallengeLocalRepository>(localRepository, apiClient, userID), ChallengeRepository {
|
||||
|
||||
|
||||
override fun isChallengeMember(challengeID: String): Flowable<Boolean> {
|
||||
return localRepository.isChallengeMember(userID, challengeID)
|
||||
}
|
||||
|
|
@ -124,9 +122,9 @@ class ChallengeRepositoryImpl(localRepository: ChallengeLocalRepository, apiClie
|
|||
return localRepository.getUserChallenges(userId)
|
||||
}
|
||||
|
||||
override fun retrieveChallenges(user: User): Flowable<List<Challenge>> {
|
||||
return apiClient.userChallenges
|
||||
.doOnNext { localRepository.saveChallenges(it) }
|
||||
override fun retrieveChallenges(page: Int, memberOnly: Boolean): Flowable<List<Challenge>> {
|
||||
return apiClient.getUserChallenges(page, memberOnly)
|
||||
.doOnNext { localRepository.saveChallenges(it, page == 0, memberOnly) }
|
||||
}
|
||||
|
||||
override fun leaveChallenge(challenge: Challenge, keepTasks: String): Flowable<Void> {
|
||||
|
|
|
|||
|
|
@ -196,8 +196,6 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
|
|||
|
||||
override fun inviteToGroup(id: String, inviteData: Map<String, Any>): Flowable<List<String>> = apiClient.inviteToGroup(id, inviteData)
|
||||
|
||||
override fun getUserChallenges(): Flowable<List<Challenge>> = apiClient.userChallenges
|
||||
|
||||
override fun getMember(userId: String?): Flowable<Member> {
|
||||
return if (userId == null) {
|
||||
Flowable.empty()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.habitrpg.android.habitica.data.local
|
|||
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.social.ChallengeMembership
|
||||
import com.habitrpg.android.habitica.models.social.GroupMembership
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
|
||||
import io.reactivex.Flowable
|
||||
|
|
@ -19,7 +18,7 @@ interface ChallengeLocalRepository : BaseLocalRepository {
|
|||
|
||||
fun setParticipating(userID: String, challengeID: String, isParticipating: Boolean)
|
||||
|
||||
fun saveChallenges(challenges: List<Challenge>)
|
||||
fun saveChallenges(challenges: List<Challenge>, clearChallenges: Boolean, memberOnly: Boolean)
|
||||
fun getChallengeMembership(userId: String, id: String): Flowable<ChallengeMembership>
|
||||
fun getChallengeMemberships(userId: String): Flowable<RealmResults<ChallengeMembership>>
|
||||
fun isChallengeMember(userID: String, challengeID: String): Flowable<Boolean>
|
||||
|
|
|
|||
|
|
@ -94,17 +94,19 @@ class RealmChallengeLocalRepository(realm: Realm) : RealmBaseLocalRepository(rea
|
|||
}
|
||||
}
|
||||
|
||||
override fun saveChallenges(challenges: List<Challenge>) {
|
||||
val localChallenges = realm.where(Challenge::class.java).findAll().createSnapshot()
|
||||
val challengesToDelete = ArrayList<Challenge>()
|
||||
for (localTask in localChallenges) {
|
||||
if (!challenges.contains(localTask)) {
|
||||
challengesToDelete.add(localTask)
|
||||
override fun saveChallenges(challenges: List<Challenge>, clearChallenges: Boolean, memberOnly: Boolean) {
|
||||
if (clearChallenges && !memberOnly) {
|
||||
val localChallenges = realm.where(Challenge::class.java).findAll().createSnapshot()
|
||||
val challengesToDelete = ArrayList<Challenge>()
|
||||
for (localTask in localChallenges) {
|
||||
if (!challenges.contains(localTask)) {
|
||||
challengesToDelete.add(localTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
realm.executeTransaction {
|
||||
for (localTask in challengesToDelete) {
|
||||
localTask.deleteFromRealm()
|
||||
realm.executeTransaction {
|
||||
for (localTask in challengesToDelete) {
|
||||
localTask.deleteFromRealm()
|
||||
}
|
||||
}
|
||||
}
|
||||
realm.executeTransaction { realm1 -> realm1.insertOrUpdate(challenges) }
|
||||
|
|
|
|||
|
|
@ -2,24 +2,25 @@ package com.habitrpg.android.habitica.ui.fragments.social.challenges
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.view.MenuItemCompat
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import android.view.*
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.MenuItemCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.AppComponent
|
||||
import com.habitrpg.android.habitica.data.ChallengeRepository
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.android.habitica.extensions.notNull
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.activities.ChallengeFormActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.helpers.bindView
|
||||
|
|
@ -30,13 +31,20 @@ import io.reactivex.functions.Consumer
|
|||
import io.realm.RealmResults
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import android.widget.Toast
|
||||
import com.habitrpg.android.habitica.R.id.recyclerView
|
||||
|
||||
class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
|
||||
class ChallengeListFragment : BaseFragment(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
@Inject
|
||||
lateinit var challengeRepository: ChallengeRepository
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
@field:[Inject Named(AppModule.NAMED_USER_ID)]
|
||||
lateinit var userId: String
|
||||
var user: User? = null
|
||||
|
||||
private val swipeRefreshLayout: androidx.swiperefreshlayout.widget.SwipeRefreshLayout? by bindView(R.id.refreshLayout)
|
||||
private val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
|
||||
|
|
@ -45,6 +53,8 @@ class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
|
|||
private var challengeAdapter: ChallengesListViewAdapter? = null
|
||||
private var viewUserChallengesOnly: Boolean = false
|
||||
|
||||
private var nextPageToLoad = 0
|
||||
private var loadedAllData = false
|
||||
|
||||
private var challenges: RealmResults<Challenge>? = null
|
||||
|
||||
|
|
@ -82,11 +92,23 @@ class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
|
|||
this.recyclerView?.setBackgroundResource(R.color.white)
|
||||
}
|
||||
|
||||
compositeSubscription.add(userRepository.getUser().subscribe(Consumer { this.user = it}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
recyclerView?.setEmptyView(emptyView)
|
||||
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
|
||||
|
||||
challengeAdapter?.updateUnfilteredData(challenges)
|
||||
loadLocalChallenges()
|
||||
|
||||
recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
|
||||
if (!recyclerView.canScrollVertically(1)) {
|
||||
retrieveChallengesPage()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun openDetailFragment(challengeID: String) {
|
||||
|
|
@ -98,7 +120,7 @@ class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
|
|||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
fetchOnlineChallenges()
|
||||
retrieveChallengesPage()
|
||||
}
|
||||
|
||||
private fun setRefreshing(state: Boolean) {
|
||||
|
|
@ -114,26 +136,26 @@ class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
|
|||
|
||||
compositeSubscription.add(observable.firstElement().subscribe(Consumer { challenges ->
|
||||
if (challenges.size == 0) {
|
||||
fetchOnlineChallenges()
|
||||
retrieveChallengesPage()
|
||||
}
|
||||
this.challenges = challenges
|
||||
challengeAdapter?.updateUnfilteredData(challenges)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
private fun fetchOnlineChallenges() {
|
||||
setRefreshing(true)
|
||||
user.notNull {
|
||||
challengeRepository.retrieveChallenges(it).doOnComplete {
|
||||
setRefreshing(false)
|
||||
} .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
|
||||
private fun retrieveChallengesPage() {
|
||||
if (swipeRefreshLayout?.isRefreshing == true || loadedAllData) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun customTitle(): String {
|
||||
return if (!isAdded) {
|
||||
""
|
||||
} else getString(R.string.sidebar_challenges)
|
||||
setRefreshing(true)
|
||||
compositeSubscription.add(challengeRepository.retrieveChallenges(nextPageToLoad, viewUserChallengesOnly).doOnComplete {
|
||||
setRefreshing(false)
|
||||
} .subscribe(Consumer {
|
||||
if (it.size < 10) {
|
||||
loadedAllData = true
|
||||
}
|
||||
nextPageToLoad += 1
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
|
|
@ -180,7 +202,7 @@ class ChallengeListFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
|
|||
return true
|
||||
}
|
||||
R.id.action_reload -> {
|
||||
fetchOnlineChallenges()
|
||||
retrieveChallengesPage()
|
||||
return true
|
||||
}
|
||||
R.id.action_search -> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue