mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-20 12:49:02 +00:00
add ability to view team task boards
This commit is contained in:
parent
25f82c8d3c
commit
f5e5a987d9
26 changed files with 717 additions and 109 deletions
|
|
@ -21,6 +21,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="?barColor" />
|
||||
<LinearLayout
|
||||
android:id="@+id/cutout_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
|
@ -68,6 +69,7 @@
|
|||
android:paddingTop="@dimen/spacing_small"
|
||||
android:paddingBottom="@dimen/spacing_small"/>
|
||||
<androidx.legacy.widget.Space
|
||||
android:id="@+id/cutout_space"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content" />
|
||||
<com.habitrpg.android.habitica.ui.views.navigation.BottomNavigationItem
|
||||
|
|
|
|||
|
|
@ -430,4 +430,12 @@
|
|||
android:id="@+id/promoInfoFragment"
|
||||
android:name="com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment"
|
||||
android:label="" />
|
||||
<fragment
|
||||
android:id="@+id/teamBoardFragment"
|
||||
android:name="com.habitrpg.android.habitica.ui.fragments.tasks.TeamBoardFragment"
|
||||
android:label="TeamBoardFragment" >
|
||||
<argument
|
||||
android:name="teamID"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
</navigation>
|
||||
|
|
@ -1136,4 +1136,5 @@
|
|||
<string name="three_months_one_time">3 month one-time subscription</string>
|
||||
<string name="six_months_one_time">6 month one-time subscription</string>
|
||||
<string name="twelve_months_one_time">12 month one-time subscription</string>
|
||||
<string name="sidebar_teams">Teams</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -392,4 +392,12 @@ interface ApiService {
|
|||
|
||||
@POST("user/reroll")
|
||||
fun reroll(): Flowable<HabitResponse<User>>
|
||||
|
||||
// Team Plans
|
||||
|
||||
@GET("group-plans")
|
||||
fun getTeamPlans(): Flowable<HabitResponse<List<TeamPlan>>>
|
||||
|
||||
@GET("tasks/group/{groupID}")
|
||||
fun getTeamPlanTasks(@Path("groupID") groupId: String): Flowable<HabitResponse<TaskList>>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ import com.habitrpg.android.habitica.ui.fragments.support.FAQOverviewFragment;
|
|||
import com.habitrpg.android.habitica.ui.fragments.support.SupportMainFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.tasks.TaskRecyclerViewFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.tasks.TasksFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.tasks.TeamBoardFragment;
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel;
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.InboxViewModel;
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel;
|
||||
|
|
@ -337,4 +338,6 @@ public interface UserComponent {
|
|||
void inject(AdventureGuideActivity adventureGuideFragment);
|
||||
|
||||
void inject(PromoInfoFragment promoInfoFragment);
|
||||
|
||||
void inject(@NotNull TeamBoardFragment teamBoardFragment);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,4 +262,6 @@ interface ApiClient {
|
|||
fun transferGems(giftedID: String, amount: Int): Flowable<Void>
|
||||
fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable<Void>
|
||||
fun blockMember(userID: String): Flowable<List<String>>
|
||||
fun getTeamPlans(): Flowable<List<TeamPlan>>
|
||||
fun getTeamPlanTasks(teamID: String): Flowable<TaskList>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import com.habitrpg.android.habitica.data.local.UserQuestStatus
|
|||
import com.habitrpg.android.habitica.models.Achievement
|
||||
import com.habitrpg.android.habitica.models.QuestAchievement
|
||||
import com.habitrpg.android.habitica.models.Skill
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.responses.SkillResponse
|
||||
import com.habitrpg.android.habitica.models.responses.UnlockResponse
|
||||
import com.habitrpg.android.habitica.models.responses.VerifyUsernameResponse
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
|
|
@ -81,4 +83,8 @@ interface UserRepository : BaseRepository {
|
|||
fun getUserQuestStatus(): Flowable<UserQuestStatus>
|
||||
|
||||
fun reroll(): Flowable<User>
|
||||
fun retrieveTeamPlans(): Flowable<List<TeamPlan>>
|
||||
fun getTeamPlans(): Flowable<RealmResults<TeamPlan>>
|
||||
fun retrieveTeamPlan(teamID: String): Flowable<Group>
|
||||
fun getTeamPlan(teamID: String): Flowable<Group>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -780,6 +780,14 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
|
|||
return apiService.transferGems(mapOf(Pair("toUserId", giftedID), Pair("gemAmount", amount))).compose(configureApiCallObserver())
|
||||
}
|
||||
|
||||
override fun getTeamPlans(): Flowable<List<TeamPlan>> {
|
||||
return apiService.getTeamPlans().compose(configureApiCallObserver())
|
||||
}
|
||||
|
||||
override fun getTeamPlanTasks(teamID: String): Flowable<TaskList> {
|
||||
return apiService.getTeamPlanTasks(teamID).compose(configureApiCallObserver())
|
||||
}
|
||||
|
||||
override fun bulkAllocatePoints(strength: Int, intelligence: Int, constitution: Int, perception: Int): Flowable<Stats> {
|
||||
val body = HashMap<String, Map<String, Int>>()
|
||||
val stats = HashMap<String, Int>()
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli
|
|||
override fun getCurrentUserTasks(taskType: String): Flowable<RealmResults<Task>> =
|
||||
this.localRepository.getTasks(taskType, userID)
|
||||
|
||||
override fun saveTasks(userId: String, order: TasksOrder, tasks: TaskList) {
|
||||
localRepository.saveTasks(userId, order, tasks)
|
||||
override fun saveTasks(ownerID: String, order: TasksOrder, tasks: TaskList) {
|
||||
localRepository.saveTasks(ownerID, order, tasks)
|
||||
}
|
||||
|
||||
override fun retrieveTasks(userId: String, tasksOrder: TasksOrder): Flowable<TaskList> {
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
|||
import com.habitrpg.android.habitica.models.Achievement
|
||||
import com.habitrpg.android.habitica.models.QuestAchievement
|
||||
import com.habitrpg.android.habitica.models.Skill
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.responses.SkillResponse
|
||||
import com.habitrpg.android.habitica.models.responses.UnlockResponse
|
||||
import com.habitrpg.android.habitica.models.responses.VerifyUsernameResponse
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
|
|
@ -340,6 +342,38 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
return localRepository.getQuestAchievements(userID)
|
||||
}
|
||||
|
||||
override fun retrieveTeamPlans(): Flowable<List<TeamPlan>> {
|
||||
return apiClient.getTeamPlans().doOnNext { teams ->
|
||||
teams.forEach { it.userID = userID }
|
||||
localRepository.save(teams)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTeamPlans(): Flowable<RealmResults<TeamPlan>> {
|
||||
return localRepository.getTeamPlans(userID)
|
||||
}
|
||||
|
||||
override fun retrieveTeamPlan(teamID: String): Flowable<Group> {
|
||||
return Flowable.zip(apiClient.getGroup(teamID), apiClient.getTeamPlanTasks(teamID),
|
||||
{ team, tasks ->
|
||||
team.tasks = tasks
|
||||
team
|
||||
})
|
||||
.doOnNext { localRepository.save(it) }
|
||||
.doOnNext { team ->
|
||||
val id = team.id
|
||||
val tasksOrder = team.tasksOrder
|
||||
val tasks = team.tasks
|
||||
if (id.isNotBlank() && tasksOrder != null && tasks != null) {
|
||||
taskRepository.saveTasks(id, tasksOrder, tasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTeamPlan(teamID: String): Flowable<Group> {
|
||||
return localRepository.getTeamPlan(teamID)
|
||||
}
|
||||
|
||||
private fun mergeUser(oldUser: User?, newUser: User): User {
|
||||
if (oldUser == null || !oldUser.isValid) {
|
||||
return oldUser ?: newUser
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package com.habitrpg.android.habitica.data.local
|
||||
|
||||
import com.habitrpg.android.habitica.models.Achievement
|
||||
import com.habitrpg.android.habitica.models.QuestAchievement
|
||||
import com.habitrpg.android.habitica.models.Skill
|
||||
import com.habitrpg.android.habitica.models.TutorialStep
|
||||
import com.habitrpg.android.habitica.models.*
|
||||
import com.habitrpg.android.habitica.models.social.ChatMessage
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.realm.RealmResults
|
||||
|
|
@ -32,4 +30,6 @@ interface UserLocalRepository : BaseLocalRepository {
|
|||
fun getAchievements(): Flowable<RealmResults<Achievement>>
|
||||
fun getQuestAchievements(userID: String): Flowable<RealmResults<QuestAchievement>>
|
||||
fun getUserQuestStatus(userID: String): Flowable<UserQuestStatus>
|
||||
fun getTeamPlans(userID: String): Flowable<RealmResults<TeamPlan>>
|
||||
fun getTeamPlan(teamID: String): Flowable<Group>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,21 +37,23 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
|
|||
.filter { it.isLoaded })
|
||||
}
|
||||
|
||||
override fun saveTasks(userId: String, tasksOrder: TasksOrder, tasks: TaskList) {
|
||||
override fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList) {
|
||||
val sortedTasks = ArrayList<Task>()
|
||||
sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.habits))
|
||||
sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.dailys))
|
||||
sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.todos))
|
||||
sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.rewards))
|
||||
removeOldTasks(userId, sortedTasks)
|
||||
removeOldTasks(ownerID, sortedTasks)
|
||||
|
||||
val allChecklistItems = ArrayList<ChecklistItem>()
|
||||
sortedTasks.forEach { it.checklist?.let { it1 -> allChecklistItems.addAll(it1) } }
|
||||
removeOldChecklists(allChecklistItems)
|
||||
|
||||
val allReminders = ArrayList<RemindersItem>()
|
||||
sortedTasks.forEach { it.reminders?.let { it1 -> allReminders.addAll(it1) } }
|
||||
sortedTasks.forEach {
|
||||
if (it.userId.isBlank()) it.userId = ownerID
|
||||
it.checklist?.let { it1 -> allChecklistItems.addAll(it1) }
|
||||
it.reminders?.let { it1 -> allReminders.addAll(it1) }
|
||||
}
|
||||
removeOldReminders(allReminders)
|
||||
removeOldChecklists(allChecklistItems)
|
||||
|
||||
executeTransaction { realm1 -> realm1.insertOrUpdate(sortedTasks) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
|
|||
|
||||
override fun getTutorialSteps(): Flowable<RealmResults<TutorialStep>> = RxJavaBridge.toV3Flowable(realm.where(TutorialStep::class.java).findAll().asFlowable()
|
||||
.filter { it.isLoaded })
|
||||
override fun getUser(userID: String): Flowable<User> {
|
||||
|
||||
override fun getUser(userID: String): Flowable<User> {
|
||||
if (realm.isClosed) return Flowable.empty()
|
||||
return RxJavaBridge.toV3Flowable(realm.where(User::class.java)
|
||||
.equalTo("id", userID)
|
||||
|
|
@ -134,6 +135,24 @@ override fun getUser(userID: String): Flowable<User> {
|
|||
}
|
||||
}
|
||||
|
||||
override fun getTeamPlans(userID: String): Flowable<RealmResults<TeamPlan>> {
|
||||
return RxJavaBridge.toV3Flowable(realm.where(TeamPlan::class.java)
|
||||
.equalTo("userID", userID)
|
||||
.findAll()
|
||||
.asFlowable()
|
||||
.filter { it.isLoaded })
|
||||
}
|
||||
|
||||
override fun getTeamPlan(teamID: String): Flowable<Group> {
|
||||
if (realm.isClosed) return Flowable.empty()
|
||||
return RxJavaBridge.toV3Flowable(realm.where(Group::class.java)
|
||||
.equalTo("id", teamID)
|
||||
.findAll()
|
||||
.asFlowable()
|
||||
.filter { realmObject -> realmObject.isLoaded && realmObject.isValid && !realmObject.isEmpty() }
|
||||
.map { teams -> teams.first() })
|
||||
}
|
||||
|
||||
override fun getSkills(user: User): Flowable<RealmResults<Skill>> {
|
||||
val habitClass = if (user.preferences?.disableClasses == true) "none" else user.stats?.habitClass
|
||||
return RxJavaBridge.toV3Flowable(realm.where(Skill::class.java)
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@ open class Tag : RealmObject() {
|
|||
}
|
||||
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (Tag::class.java.isAssignableFrom(o!!.javaClass)) {
|
||||
val otherTag = o as Tag?
|
||||
return this.id == otherTag!!.id
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is Tag) {
|
||||
return this.id == other.id
|
||||
}
|
||||
return super.equals(o)
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.habitrpg.android.habitica.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
open class TeamPlan : RealmObject() {
|
||||
@PrimaryKey
|
||||
var id: String = ""
|
||||
|
||||
var userID: String? = null
|
||||
var summary: String = ""
|
||||
@SerializedName("leader")
|
||||
var leaderID: String? = null
|
||||
//var managers: RealmList<String> = RealmList()
|
||||
var isActive: Boolean = false
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is TeamPlan) {
|
||||
return this.id == other.id
|
||||
}
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id.hashCode()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,13 @@ package com.habitrpg.android.habitica.models.social
|
|||
import com.google.gson.annotations.SerializedName
|
||||
import com.habitrpg.android.habitica.models.BaseObject
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
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.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Ignore
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
open class Group : RealmObject(), BaseObject {
|
||||
|
|
@ -38,6 +41,12 @@ open class Group : RealmObject(), BaseObject {
|
|||
var leaderOnlyChallenges: Boolean = false
|
||||
var leaderOnlyGetGems: Boolean = false
|
||||
|
||||
|
||||
@Ignore
|
||||
var tasksOrder: TasksOrder? = null
|
||||
@Ignore
|
||||
var tasks: TaskList? = null
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -626,6 +626,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
pushNotificationManager.setUser(user1)
|
||||
pushNotificationManager.addPushDeviceUsingStoredToken()
|
||||
}
|
||||
.flatMap { userRepository.retrieveTeamPlans() }
|
||||
.flatMap { contentRepository.retrieveContent(this,false) }
|
||||
.flatMap { contentRepository.retrieveWorldState(this) }
|
||||
.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.dpToPx
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.promotions.HabiticaPromotion
|
||||
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment
|
||||
import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem
|
||||
import com.habitrpg.android.habitica.ui.views.adventureGuide.AdventureGuideMenuBanner
|
||||
import com.habitrpg.android.habitica.ui.views.promo.PromoMenuView
|
||||
|
|
@ -66,6 +69,30 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun setTeams(teams: List<TeamPlan>) {
|
||||
var teamHeaderIndex = -1
|
||||
var nextHeaderIndex = -1
|
||||
for ((index, item) in items.withIndex()) {
|
||||
if (teamHeaderIndex != -1 && item.isHeader) {
|
||||
nextHeaderIndex = index
|
||||
break
|
||||
} else if (item.identifier == NavigationDrawerFragment.SIDEBAR_TEAMS) {
|
||||
teamHeaderIndex = index
|
||||
}
|
||||
}
|
||||
if (teamHeaderIndex != -1 && nextHeaderIndex != -1) {
|
||||
for (x in nextHeaderIndex-1 downTo teamHeaderIndex+1) {
|
||||
items.removeAt(x)
|
||||
}
|
||||
for ((index, team) in teams.withIndex()) {
|
||||
val item = HabiticaDrawerItem(R.id.teamBoardFragment, team.id, team.summary)
|
||||
item.bundle = bundleOf(Pair("teamID", team.id))
|
||||
items.add(teamHeaderIndex + index + 1, item)
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val drawerItem = getItem(position)
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import io.reactivex.rxjava3.core.Flowable
|
|||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import io.realm.OrderedRealmCollection
|
||||
|
||||
class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollection<Task>?, private val layoutResource: Int, user: User?) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), TaskRecyclerViewAdapter {
|
||||
var user = user
|
||||
class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollection<Task>?, private val layoutResource: Int) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), TaskRecyclerViewAdapter {
|
||||
var user: User? = null
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
|
|
|
|||
|
|
@ -185,6 +185,13 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
questContent = it
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
subscriptions?.add(userRepository.getTeamPlans()
|
||||
.distinctUntilChanged { firstTeams, secondTeams -> firstTeams == secondTeams }
|
||||
.subscribe( {
|
||||
getItemWithIdentifier(SIDEBAR_TEAMS)?.isVisible = it.size != 0
|
||||
adapter.setTeams(it)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
subscriptions?.add(userRepository.getUser().subscribe({
|
||||
updateUser(it)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
|
@ -313,6 +320,8 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
items.add(HabiticaDrawerItem(R.id.statsFragment, SIDEBAR_STATS, context.getString(R.string.sidebar_stats)))
|
||||
items.add(HabiticaDrawerItem(R.id.achievementsFragment, SIDEBAR_ACHIEVEMENTS, context.getString(R.string.sidebar_achievements)))
|
||||
|
||||
items.add(HabiticaDrawerItem(0, SIDEBAR_TEAMS, context.getString(R.string.sidebar_teams), true))
|
||||
|
||||
items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_shops), true))
|
||||
items.add(HabiticaDrawerItem(R.id.marketFragment, SIDEBAR_SHOPS_MARKET, context.getString(R.string.market)))
|
||||
items.add(HabiticaDrawerItem(R.id.questShopFragment, SIDEBAR_SHOPS_QUEST, context.getString(R.string.questShop)))
|
||||
|
|
@ -325,11 +334,11 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
items.add(HabiticaDrawerItem(R.id.stableFragment, SIDEBAR_STABLE, context.getString(R.string.sidebar_stable)))
|
||||
items.add(HabiticaDrawerItem(R.id.avatarOverviewFragment, SIDEBAR_AVATAR, context.getString(R.string.sidebar_avatar)))
|
||||
items.add(HabiticaDrawerItem(R.id.gemPurchaseActivity, SIDEBAR_GEMS, context.getString(R.string.sidebar_gems)))
|
||||
items.add(HabiticaDrawerItem(R.id.subscriptionPurchaseActivity, SIDEBAR_SUBSCRIPTION, context.getString(R.string.sidebar_subscription), isHeader = false))
|
||||
items.add(HabiticaDrawerItem(R.id.subscriptionPurchaseActivity, SIDEBAR_SUBSCRIPTION, context.getString(R.string.sidebar_subscription)))
|
||||
|
||||
items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
|
||||
items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
|
||||
items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern), isHeader = false))
|
||||
items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern)))
|
||||
items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
|
||||
items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
|
||||
|
||||
|
|
@ -355,7 +364,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
|
||||
fun setSelection(transitionId: Int?, bundle: Bundle? = null, openSelection: Boolean = true, preventReselection: Boolean = true) {
|
||||
closeDrawer()
|
||||
if (adapter.selectedItem != null && adapter.selectedItem == transitionId && preventReselection) return
|
||||
if (adapter.selectedItem != null && adapter.selectedItem == transitionId && bundle == null && preventReselection) return
|
||||
adapter.selectedItem = transitionId
|
||||
|
||||
if (!openSelection) {
|
||||
|
|
@ -517,6 +526,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
const val SIDEBAR_SKILLS = "skills"
|
||||
const val SIDEBAR_STATS = "stats"
|
||||
const val SIDEBAR_ACHIEVEMENTS = "achievements"
|
||||
const val SIDEBAR_TEAMS = "teams"
|
||||
const val SIDEBAR_SOCIAL = "social"
|
||||
const val SIDEBAR_TAVERN = "tavern"
|
||||
const val SIDEBAR_PARTY = "party"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.*
|
|||
|
||||
class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
|
||||
|
||||
private var showCustomRewards: Boolean = true
|
||||
private var selectedCard: ShopItem? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
|
|
@ -51,9 +52,11 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
|
|||
}
|
||||
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
|
||||
|
||||
compositeSubscription.add(inventoryRepository.getInAppRewards().subscribe({
|
||||
(recyclerAdapter as? RewardsRecyclerViewAdapter)?.updateItemRewards(it)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
if (showCustomRewards) {
|
||||
compositeSubscription.add(inventoryRepository.getInAppRewards().subscribe({
|
||||
(recyclerAdapter as? RewardsRecyclerViewAdapter)?.updateItemRewards(it)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
(recyclerAdapter as? RewardsRecyclerViewAdapter)?.purchaseCardEvents?.subscribe({
|
||||
selectedCard = it
|
||||
|
|
@ -100,7 +103,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
|
|||
when (requestCode) {
|
||||
11 -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
userRepository.useSkill(user,
|
||||
userRepository.useSkill(null,
|
||||
selectedCard?.key ?: "",
|
||||
"member",
|
||||
data.getStringExtra("member_id") ?: "")
|
||||
|
|
@ -118,11 +121,11 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
|
|||
|
||||
companion object {
|
||||
|
||||
fun newInstance(context: Context?, user: User?, classType: String): RewardsRecyclerviewFragment {
|
||||
fun newInstance(context: Context?, classType: String, showCustomRewards: Boolean): RewardsRecyclerviewFragment {
|
||||
val fragment = RewardsRecyclerviewFragment()
|
||||
fragment.retainInstance = true
|
||||
fragment.user = user
|
||||
fragment.classType = classType
|
||||
fragment.showCustomRewards = showCustomRewards
|
||||
|
||||
if (context != null) {
|
||||
fragment.tutorialStepIdentifier = "rewards"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
|
|
@ -31,7 +30,6 @@ import com.habitrpg.android.habitica.models.tasks.Task
|
|||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.tasks.*
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
|
|
@ -54,8 +52,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
|
||||
var recyclerAdapter: TaskRecyclerViewAdapter? = null
|
||||
var itemAnimator = SafeDefaultItemAnimator()
|
||||
@field:[Inject Named(AppModule.NAMED_USER_ID)]
|
||||
lateinit var userID: String
|
||||
var ownerID: String = ""
|
||||
@Inject
|
||||
lateinit var apiClient: ApiClient
|
||||
@Inject
|
||||
|
|
@ -74,9 +71,10 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
internal var layoutManager: RecyclerView.LayoutManager? = null
|
||||
|
||||
internal var classType: String? = null
|
||||
internal var user: User? = null
|
||||
private var itemTouchCallback: ItemTouchHelper.Callback? = null
|
||||
|
||||
var refreshAction: ((() -> Unit) -> Unit)? = null
|
||||
|
||||
internal val className: String
|
||||
get() = this.classType ?: ""
|
||||
|
||||
|
|
@ -93,7 +91,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
TodosRecyclerViewAdapter(null, true, R.layout.todo_item_card, taskFilterHelper)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
RewardsRecyclerViewAdapter(null, R.layout.reward_item_card, user)
|
||||
RewardsRecyclerViewAdapter(null, R.layout.reward_item_card)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -161,9 +159,9 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
when (classType) {
|
||||
Task.TYPE_TODO -> taskFilterHelper.setActiveFilter(Task.TYPE_TODO, Task.FILTER_ACTIVE)
|
||||
Task.TYPE_DAILY -> {
|
||||
if (user?.isValid == true && user?.preferences?.dailyDueDefaultView == true) {
|
||||
/*if (user?.isValid == true && user?.preferences?.dailyDueDefaultView == true) {
|
||||
taskFilterHelper.setActiveFilter(Task.TYPE_DAILY, Task.FILTER_ACTIVE)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +254,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}?.subscribeWithErrorHandler {}?.let { compositeSubscription.add(it) }
|
||||
recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { compositeSubscription.add(it) }
|
||||
|
||||
compositeSubscription.add(taskRepository.getTasks(this.classType ?: "", userID).subscribe({
|
||||
compositeSubscription.add(taskRepository.getTasks(this.classType ?: "", ownerID).subscribe({
|
||||
this.recyclerAdapter?.updateUnfilteredData(it)
|
||||
this.recyclerAdapter?.filter()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
|
@ -280,7 +278,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
setEmptyLabels()
|
||||
|
||||
if (Task.TYPE_REWARD == className) {
|
||||
compositeSubscription.add(taskRepository.getTasks(this.className, userID)
|
||||
compositeSubscription.add(taskRepository.getTasks(this.className, ownerID)
|
||||
.subscribe({ recyclerAdapter?.data = it }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
|
|
@ -366,7 +364,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
|
||||
private fun scoreTask(task: Task, direction: TaskDirection) {
|
||||
compositeSubscription.add(taskRepository.taskChecked(user, task, direction == TaskDirection.UP, false) { result ->
|
||||
compositeSubscription.add(taskRepository.taskChecked(null, task, direction == TaskDirection.UP, false) { result ->
|
||||
handleTaskResult(result, task.value.toInt())
|
||||
}.subscribeWithErrorHandler {})
|
||||
}
|
||||
|
|
@ -381,10 +379,9 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
|
||||
override fun onRefresh() {
|
||||
binding?.refreshLayout?.isRefreshing = true
|
||||
compositeSubscription.add(userRepository.retrieveUser(true, true)
|
||||
.doOnTerminate {
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
refreshAction?.invoke {
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -399,35 +396,19 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
setEmptyLabels()
|
||||
|
||||
if (activeFilter == Task.FILTER_COMPLETED) {
|
||||
compositeSubscription.add(taskRepository.retrieveCompletedTodos(userID).subscribe({}, RxErrorHandler.handleEmptyError()))
|
||||
compositeSubscription.add(taskRepository.retrieveCompletedTodos(ownerID).subscribe({}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun openTaskForm(task: Task) {
|
||||
if (Date().time - (TasksFragment.lastTaskFormOpen?.time ?: 0) < 2000 || !task.isValid) {
|
||||
return
|
||||
}
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, task.type)
|
||||
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id)
|
||||
bundle.putDouble(TaskFormActivity.TASK_VALUE_KEY, task.value)
|
||||
|
||||
val intent = Intent(activity, TaskFormActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
TasksFragment.lastTaskFormOpen = Date()
|
||||
if (isAdded) {
|
||||
startActivityForResult(intent, TasksFragment.TASK_UPDATED_RESULT)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CLASS_TYPE_KEY = "CLASS_TYPE_KEY"
|
||||
|
||||
fun newInstance(context: Context?, user: User?, classType: String): TaskRecyclerViewFragment {
|
||||
fun newInstance(context: Context?, classType: String): TaskRecyclerViewFragment {
|
||||
val fragment = TaskRecyclerViewFragment()
|
||||
fragment.retainInstance = true
|
||||
fragment.user = user
|
||||
fragment.classType = classType
|
||||
var tutorialTexts: List<String>? = null
|
||||
if (context != null) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.habitrpg.android.habitica.models.tasks.Task
|
|||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.views.navigation.HabiticaBottomNavigationViewListener
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.TaskFilterDialog
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import java.util.*
|
||||
|
|
@ -30,7 +31,7 @@ import javax.inject.Inject
|
|||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.OnQueryTextListener {
|
||||
class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.OnQueryTextListener, HabiticaBottomNavigationViewListener {
|
||||
|
||||
override var binding: FragmentViewpagerBinding? = null
|
||||
|
||||
|
|
@ -50,13 +51,6 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
|
||||
private var filterMenuItem: MenuItem? = null
|
||||
|
||||
override var user: User?
|
||||
get() = super.user
|
||||
set(value) {
|
||||
super.user = value
|
||||
viewFragmentsDictionary?.values?.forEach { it.user = value }
|
||||
}
|
||||
|
||||
private val activeFragment: TaskRecyclerViewFragment?
|
||||
get() {
|
||||
var fragment = viewFragmentsDictionary?.get(binding?.viewPager?.currentItem)
|
||||
|
|
@ -89,26 +83,14 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
3 -> Task.TYPE_REWARD
|
||||
else -> Task.TYPE_HABIT
|
||||
}
|
||||
bottomNavigation?.onTabSelectedListener = {
|
||||
val newItem = when (it) {
|
||||
Task.TYPE_HABIT -> 0
|
||||
Task.TYPE_DAILY -> 1
|
||||
Task.TYPE_TODO -> 2
|
||||
Task.TYPE_REWARD -> 3
|
||||
else -> 0
|
||||
}
|
||||
binding?.viewPager?.currentItem = newItem
|
||||
updateBottomBarBadges()
|
||||
}
|
||||
bottomNavigation?.onAddListener = { type ->
|
||||
openNewTaskActivity(type)
|
||||
}
|
||||
bottomNavigation?.flipAddBehaviour = appConfigManager.flipAddTaskBehaviour()
|
||||
bottomNavigation?.listener = this
|
||||
bottomNavigation?.canAddTasks = true
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
bottomNavigation?.onTabSelectedListener = null
|
||||
bottomNavigation?.onAddListener = null
|
||||
if (bottomNavigation?.listener == this) {
|
||||
bottomNavigation?.listener = null
|
||||
}
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
|
@ -220,12 +202,18 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
|
||||
override fun getItem(position: Int): androidx.fragment.app.Fragment {
|
||||
val fragment: TaskRecyclerViewFragment = when (position) {
|
||||
0 -> TaskRecyclerViewFragment.newInstance(context, user, Task.TYPE_HABIT)
|
||||
1 -> TaskRecyclerViewFragment.newInstance(context, user, Task.TYPE_DAILY)
|
||||
3 -> RewardsRecyclerviewFragment.newInstance(context, user, Task.TYPE_REWARD)
|
||||
else -> TaskRecyclerViewFragment.newInstance(context, user, Task.TYPE_TODO)
|
||||
0 -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_HABIT)
|
||||
1 -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_DAILY)
|
||||
3 -> RewardsRecyclerviewFragment.newInstance(context, Task.TYPE_REWARD)
|
||||
else -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_TODO)
|
||||
}
|
||||
fragment.ownerID = user?.id ?: ""
|
||||
fragment.refreshAction = {
|
||||
compositeSubscription.add(userRepository.retrieveUser(true, true)
|
||||
.doOnTerminate {
|
||||
it()
|
||||
}.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
viewFragmentsDictionary?.put(position, fragment)
|
||||
|
||||
return fragment
|
||||
|
|
@ -412,4 +400,21 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
internal const val TASK_CREATED_RESULT = 1
|
||||
const val TASK_UPDATED_RESULT = 2
|
||||
}
|
||||
|
||||
|
||||
override fun onTabSelected(taskType: String) {
|
||||
val newItem = when (taskType) {
|
||||
Task.TYPE_HABIT -> 0
|
||||
Task.TYPE_DAILY -> 1
|
||||
Task.TYPE_TODO -> 2
|
||||
Task.TYPE_REWARD -> 3
|
||||
else -> 0
|
||||
}
|
||||
binding?.viewPager?.currentItem = newItem
|
||||
updateBottomBarBadges()
|
||||
}
|
||||
|
||||
override fun onAdd(taskType: String) {
|
||||
openNewTaskActivity(taskType)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,435 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.tasks
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.TagRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
|
||||
import com.habitrpg.android.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.android.habitica.extensions.setTintWith
|
||||
import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.TaskFilterHelper
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.views.navigation.HabiticaBottomNavigationViewListener
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.TaskFilterDialog
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.OnQueryTextListener, HabiticaBottomNavigationViewListener {
|
||||
|
||||
override var binding: FragmentViewpagerBinding? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentViewpagerBinding {
|
||||
return FragmentViewpagerBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
var teamID: String = ""
|
||||
|
||||
@Inject
|
||||
lateinit var taskFilterHelper: TaskFilterHelper
|
||||
@Inject
|
||||
lateinit var tagRepository: TagRepository
|
||||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
private var refreshItem: MenuItem? = null
|
||||
internal var viewFragmentsDictionary: MutableMap<Int, TaskRecyclerViewFragment>? = WeakHashMap()
|
||||
|
||||
private var filterMenuItem: MenuItem? = null
|
||||
|
||||
private val activeFragment: TaskRecyclerViewFragment?
|
||||
get() {
|
||||
var fragment = viewFragmentsDictionary?.get(binding?.viewPager?.currentItem)
|
||||
if (fragment == null) {
|
||||
if (isAdded) {
|
||||
fragment = (childFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + binding?.viewPager?.currentItem) as? TaskRecyclerViewFragment)
|
||||
}
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
this.usesTabLayout = false
|
||||
this.hidesToolbar = true
|
||||
this.usesBottomNavigation = true
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
arguments?.let {
|
||||
val args = TeamBoardFragmentArgs.fromBundle(it)
|
||||
teamID = args.teamID
|
||||
}
|
||||
|
||||
compositeSubscription.add(userRepository.getTeamPlan(teamID)
|
||||
.subscribe( {
|
||||
activity?.title = it.name
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
compositeSubscription.add(userRepository.retrieveTeamPlan(teamID).subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
loadTaskLists()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
bottomNavigation?.activeTaskType = when (binding?.viewPager?.currentItem) {
|
||||
0 -> Task.TYPE_HABIT
|
||||
1 -> Task.TYPE_DAILY
|
||||
2 -> Task.TYPE_TODO
|
||||
3 -> Task.TYPE_REWARD
|
||||
else -> Task.TYPE_HABIT
|
||||
}
|
||||
bottomNavigation?.listener = this
|
||||
bottomNavigation?.canAddTasks = false
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (bottomNavigation?.listener == this) {
|
||||
bottomNavigation?.listener = null
|
||||
}
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
tagRepository.close()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun injectFragment(component: UserComponent) {
|
||||
component.inject(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_main_activity, menu)
|
||||
|
||||
filterMenuItem = menu.findItem(R.id.action_filter)
|
||||
updateFilterIcon()
|
||||
|
||||
val item = menu.findItem(R.id.action_search)
|
||||
tintMenuIcon(item)
|
||||
val sv = item.actionView as? SearchView
|
||||
sv?.setOnQueryTextListener(this)
|
||||
sv?.setIconifiedByDefault(false)
|
||||
item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
filterMenuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
// Do something when expanded
|
||||
filterMenuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
taskFilterHelper.searchQuery = newText
|
||||
viewFragmentsDictionary?.values?.forEach { values -> values.recyclerAdapter?.filter() }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_filter -> {
|
||||
showFilterDialog()
|
||||
true
|
||||
}
|
||||
R.id.action_reload -> {
|
||||
refreshItem = item
|
||||
refresh()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFilterDialog() {
|
||||
context?.let {
|
||||
var disposable: Disposable? = null
|
||||
val dialog = TaskFilterDialog(it, HabiticaBaseApplication.userComponent)
|
||||
if (user != null) {
|
||||
dialog.setTags(user?.tags?.createSnapshot() ?: emptyList())
|
||||
disposable = tagRepository.getTags(user?.id ?: "").subscribe({ tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
dialog.setActiveTags(taskFilterHelper.tags)
|
||||
if (activeFragment != null) {
|
||||
val taskType = activeFragment?.classType
|
||||
if (taskType != null) {
|
||||
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
|
||||
}
|
||||
}
|
||||
dialog.setListener(object : TaskFilterDialog.OnFilterCompletedListener {
|
||||
override fun onFilterCompleted(activeTaskFilter: String?, activeTags: MutableList<String>) {
|
||||
if (viewFragmentsDictionary == null) {
|
||||
return
|
||||
}
|
||||
taskFilterHelper.tags = activeTags
|
||||
if (activeTaskFilter != null) {
|
||||
activeFragment?.setActiveFilter(activeTaskFilter)
|
||||
}
|
||||
viewFragmentsDictionary?.values?.forEach { values -> values.recyclerAdapter?.filter() }
|
||||
updateFilterIcon()
|
||||
}
|
||||
})
|
||||
dialog.setOnDismissListener {
|
||||
if (disposable?.isDisposed == false) {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
activeFragment?.onRefresh()
|
||||
}
|
||||
|
||||
private fun loadTaskLists() {
|
||||
val fragmentManager = childFragmentManager
|
||||
|
||||
binding?.viewPager?.adapter = object : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
|
||||
override fun getItem(position: Int): androidx.fragment.app.Fragment {
|
||||
val fragment: TaskRecyclerViewFragment = when (position) {
|
||||
0 -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_HABIT)
|
||||
1 -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_DAILY)
|
||||
3 -> RewardsRecyclerviewFragment.newInstance(context, Task.TYPE_REWARD)
|
||||
else -> TaskRecyclerViewFragment.newInstance(context, Task.TYPE_TODO)
|
||||
}
|
||||
fragment.ownerID = teamID
|
||||
fragment.refreshAction = {
|
||||
compositeSubscription.add(userRepository.retrieveTeamPlan(teamID)
|
||||
.doOnTerminate {
|
||||
it()
|
||||
}.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
viewFragmentsDictionary?.put(position, fragment)
|
||||
|
||||
return fragment
|
||||
}
|
||||
|
||||
override fun getCount(): Int = 4
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? = when (position) {
|
||||
0 -> activity?.getString(R.string.habits)
|
||||
1 -> activity?.getString(R.string.dailies)
|
||||
2 -> activity?.getString(R.string.todos)
|
||||
3 -> activity?.getString(R.string.rewards)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
binding?.viewPager?.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { /* no-op */ }
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
bottomNavigation?.selectedPosition = position
|
||||
updateFilterIcon()
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) { /* no-op */ }
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateFilterIcon() {
|
||||
if (filterMenuItem == null) {
|
||||
return
|
||||
}
|
||||
var filterCount = 0
|
||||
if (activeFragment != null) {
|
||||
filterCount = taskFilterHelper.howMany(activeFragment?.classType)
|
||||
}
|
||||
if (filterCount == 0) {
|
||||
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)
|
||||
context?.let {
|
||||
val filterIcon = ContextCompat.getDrawable(it, R.drawable.ic_action_filter_list)
|
||||
filterIcon?.setTintWith(it.getThemeColor(R.attr.headerTextColor), PorterDuff.Mode.MULTIPLY)
|
||||
filterMenuItem?.setIcon(filterIcon)
|
||||
}
|
||||
} else {
|
||||
context?.let {
|
||||
val filterIcon = ContextCompat.getDrawable(it, R.drawable.ic_filters_active)
|
||||
filterIcon?.setTintWith(it.getThemeColor(R.attr.textColorPrimaryDark), PorterDuff.Mode.MULTIPLY)
|
||||
filterMenuItem?.setIcon(filterIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBottomBarBadges() {
|
||||
if (bottomNavigation == null) {
|
||||
return
|
||||
}
|
||||
compositeSubscription.add(tutorialRepository.getTutorialSteps(listOf("habits", "dailies", "todos", "rewards")).subscribe({ tutorialSteps ->
|
||||
val activeTutorialFragments = ArrayList<String>()
|
||||
for (step in tutorialSteps) {
|
||||
var id = -1
|
||||
val taskType = when (step.identifier) {
|
||||
"habits" -> {
|
||||
id = R.id.habits_tab
|
||||
Task.TYPE_HABIT
|
||||
}
|
||||
"dailies" -> {
|
||||
id = R.id.dailies_tab
|
||||
Task.TYPE_DAILY
|
||||
}
|
||||
"todos" -> {
|
||||
id = R.id.todos_tab
|
||||
Task.TYPE_TODO
|
||||
}
|
||||
"rewards" -> {
|
||||
id = R.id.rewards_tab
|
||||
Task.TYPE_REWARD
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
val tab = bottomNavigation?.tabWithId(id)
|
||||
if (step.shouldDisplay()) {
|
||||
tab?.badgeCount = 1
|
||||
activeTutorialFragments.add(taskType)
|
||||
} else {
|
||||
tab?.badgeCount = 0
|
||||
}
|
||||
}
|
||||
if (activeTutorialFragments.size == 1) {
|
||||
val fragment = viewFragmentsDictionary?.get(indexForTaskType(activeTutorialFragments[0]))
|
||||
if (fragment?.tutorialTexts != null && context != null) {
|
||||
val finalText = context?.getString(R.string.tutorial_tasks_complete)
|
||||
if (!fragment.tutorialTexts.contains(finalText) && finalText != null) {
|
||||
fragment.tutorialTexts.add(finalText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
// endregion
|
||||
|
||||
private fun openNewTaskActivity(type: String) {
|
||||
if (Date().time - (lastTaskFormOpen?.time ?: 0) < 2000) {
|
||||
return
|
||||
}
|
||||
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["created task type"] = type
|
||||
additionalData["viewed task type"] = when (binding?.viewPager?.currentItem) {
|
||||
0 -> Task.TYPE_HABIT
|
||||
1 -> Task.TYPE_DAILY
|
||||
2 -> Task.TYPE_TODO
|
||||
3 -> Task.TYPE_REWARD
|
||||
else -> ""
|
||||
}
|
||||
AmplitudeManager.sendEvent("open create task form", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, type)
|
||||
bundle.putStringArrayList(TaskFormActivity.SELECTED_TAGS_KEY, ArrayList(taskFilterHelper.tags))
|
||||
|
||||
val intent = Intent(activity, TaskFormActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
|
||||
if (this.isAdded) {
|
||||
lastTaskFormOpen = Date()
|
||||
startActivityForResult(intent, TASK_CREATED_RESULT)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion Events
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
when (requestCode) {
|
||||
TASK_CREATED_RESULT -> {
|
||||
onTaskCreatedResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onTaskCreatedResult(resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val taskType = data?.getStringExtra(TaskFormActivity.TASK_TYPE_KEY)
|
||||
if (taskType != null) {
|
||||
switchToTaskTab(taskType)
|
||||
|
||||
val index = indexForTaskType(taskType)
|
||||
if (index != -1) {
|
||||
val fragment = viewFragmentsDictionary?.get(index)
|
||||
fragment?.binding?.recyclerView?.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchToTaskTab(taskType: String) {
|
||||
val index = indexForTaskType(taskType)
|
||||
if (binding?.viewPager != null && index != -1) {
|
||||
binding?.viewPager?.currentItem = index
|
||||
updateBottomBarBadges()
|
||||
}
|
||||
}
|
||||
|
||||
private fun indexForTaskType(taskType: String?): Int {
|
||||
if (taskType != null) {
|
||||
for (index in 0 until (viewFragmentsDictionary?.size ?: 0)) {
|
||||
val fragment = viewFragmentsDictionary?.get(index)
|
||||
if (fragment != null && taskType == fragment.className) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override val displayedClassName: String?
|
||||
get() = null
|
||||
|
||||
override fun addToBackStack(): Boolean = false
|
||||
|
||||
companion object {
|
||||
var lastTaskFormOpen: Date? = null
|
||||
internal const val TASK_CREATED_RESULT = 1
|
||||
const val TASK_UPDATED_RESULT = 2
|
||||
}
|
||||
|
||||
override fun onTabSelected(taskType: String) {
|
||||
val newItem = when (taskType) {
|
||||
Task.TYPE_HABIT -> 0
|
||||
Task.TYPE_DAILY -> 1
|
||||
Task.TYPE_TODO -> 2
|
||||
Task.TYPE_REWARD -> 3
|
||||
else -> 0
|
||||
}
|
||||
binding?.viewPager?.currentItem = newItem
|
||||
updateBottomBarBadges()
|
||||
}
|
||||
|
||||
override fun onAdd(taskType: String) {
|
||||
openNewTaskActivity(taskType)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.graphics.PorterDuff
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.BounceInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
|
|
@ -20,12 +21,16 @@ import com.habitrpg.android.habitica.extensions.layoutInflater
|
|||
import com.habitrpg.android.habitica.extensions.setTintWith
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
|
||||
interface HabiticaBottomNavigationViewListener {
|
||||
fun onTabSelected(taskType: String)
|
||||
fun onAdd(taskType: String)
|
||||
}
|
||||
|
||||
class HabiticaBottomNavigationView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
private val binding = MainNavigationViewBinding.inflate(context.layoutInflater, this)
|
||||
|
||||
var flipAddBehaviour = true
|
||||
private var isShowingSubmenu: Boolean = false
|
||||
var selectedPosition: Int
|
||||
get() {
|
||||
|
|
@ -44,15 +49,28 @@ class HabiticaBottomNavigationView @JvmOverloads constructor(
|
|||
else -> Task.TYPE_HABIT
|
||||
}
|
||||
}
|
||||
var onTabSelectedListener: ((String) -> Unit)? = null
|
||||
var onAddListener: ((String) -> Unit)? = null
|
||||
var listener: HabiticaBottomNavigationViewListener? = null
|
||||
var activeTaskType: String = Task.TYPE_HABIT
|
||||
set(value) {
|
||||
val wasChanged = field != value
|
||||
field = value
|
||||
if (wasChanged) {
|
||||
updateItemSelection()
|
||||
onTabSelectedListener?.invoke(value)
|
||||
listener?.onTabSelected(value)
|
||||
}
|
||||
}
|
||||
|
||||
var canAddTasks = true
|
||||
set(value) {
|
||||
field = value
|
||||
if (field) {
|
||||
binding.cutoutWrapper.visibility = View.VISIBLE
|
||||
binding.cutoutSpace.visibility = View.VISIBLE
|
||||
binding.addButtonBackground.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.cutoutWrapper.visibility = View.GONE
|
||||
binding.cutoutSpace.visibility = View.GONE
|
||||
binding.addButtonBackground.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,23 +83,15 @@ class HabiticaBottomNavigationView @JvmOverloads constructor(
|
|||
binding.todosTab.setOnClickListener { activeTaskType = Task.TYPE_TODO }
|
||||
binding.rewardsTab.setOnClickListener { activeTaskType = Task.TYPE_REWARD }
|
||||
binding.addButton.setOnClickListener {
|
||||
if (flipAddBehaviour) {
|
||||
if (isShowingSubmenu) {
|
||||
hideSubmenu()
|
||||
} else {
|
||||
onAddListener?.invoke(activeTaskType)
|
||||
}
|
||||
if (isShowingSubmenu) {
|
||||
hideSubmenu()
|
||||
} else {
|
||||
showSubmenu()
|
||||
listener?.onAdd(activeTaskType)
|
||||
}
|
||||
animateButtonTap()
|
||||
}
|
||||
binding.addButton.setOnLongClickListener {
|
||||
if (flipAddBehaviour) {
|
||||
showSubmenu()
|
||||
} else {
|
||||
onAddListener?.invoke(activeTaskType)
|
||||
}
|
||||
showSubmenu()
|
||||
animateButtonTap()
|
||||
true
|
||||
}
|
||||
|
|
@ -169,7 +179,7 @@ class HabiticaBottomNavigationView @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
view.onAddListener = {
|
||||
onAddListener?.invoke(taskType)
|
||||
listener?.onAdd(taskType)
|
||||
hideSubmenu()
|
||||
}
|
||||
binding.submenuWrapper.addView(view)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.habitrpg.android.habitica.models.inventory.Quest
|
|||
import com.habitrpg.android.habitica.models.inventory.QuestRageStrike
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.tasks.TasksOrder
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmList
|
||||
import java.lang.reflect.Type
|
||||
|
|
@ -107,6 +108,11 @@ class GroupSerialization : JsonDeserializer<Group>, JsonSerializer<Group> {
|
|||
group.leaderOnlyGetGems = leaderOnly.get("getGems").asBoolean
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.has("tasksOrder")) {
|
||||
group.tasksOrder = context.deserialize(obj.get("tasksOrder"), TasksOrder::class.java)
|
||||
}
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue