diff --git a/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png b/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png
deleted file mode 100644
index 6ab2cf339..000000000
Binary files a/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png and /dev/null differ
diff --git a/Habitica/res/drawable/icon_chat.xml b/Habitica/res/drawable/icon_chat.xml
new file mode 100644
index 000000000..42714253f
--- /dev/null
+++ b/Habitica/res/drawable/icon_chat.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/Habitica/res/layout/task_main_content.xml b/Habitica/res/layout/task_main_content.xml
index bf2e2f2ba..ac11e210c 100644
--- a/Habitica/res/layout/task_main_content.xml
+++ b/Habitica/res/layout/task_main_content.xml
@@ -12,6 +12,14 @@
android:paddingBottom="@dimen/task_top_bottom_padding"
android:layout_marginEnd="@dimen/task_text_padding"
android:layout_marginStart="@dimen/task_text_padding">
+
-
- Show assigned and open tasks on your personal task lists
Copy shared tasks
Group Plan Settings
+
+
- You
+ - You, %d other
+ - You, %d others
+
+
+ - %d Person
+ - %d People
+
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
index dfd31c660..dfb6713f7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
@@ -78,8 +78,9 @@ interface SocialRepository : BaseRepository {
suspend fun postPrivateMessage(recipientId: String, message: String): List?
+ suspend fun getPartyMembers(id: String): Flow>
suspend fun getGroupMembers(id: String): Flow>
- suspend fun retrieveGroupMembers(id: String, includeAllPublicFields: Boolean): List?
+ suspend fun retrievePartyMembers(id: String, includeAllPublicFields: Boolean): List?
fun inviteToGroup(id: String, inviteData: Map): Flowable>
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
index dd82f6db3..20de3196e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
@@ -35,7 +35,7 @@ class SocialRepositoryImpl(
override suspend fun removeMemberFromGroup(groupID: String, userID: String): List? {
apiClient.removeMemberFromGroup(groupID, userID)
- return retrieveGroupMembers(groupID, true)
+ return retrievePartyMembers(groupID, true)
}
override fun blockMember(userID: String): Flowable> {
@@ -237,11 +237,12 @@ class SocialRepositoryImpl(
return postPrivateMessage(recipientId, messageObject)
}
+ override suspend fun getPartyMembers(id: String) = localRepository.getPartyMembers(id)
override suspend fun getGroupMembers(id: String) = localRepository.getGroupMembers(id)
- override suspend fun retrieveGroupMembers(id: String, includeAllPublicFields: Boolean): List? {
+ override suspend fun retrievePartyMembers(id: String, includeAllPublicFields: Boolean): List? {
val members = apiClient.getGroupMembers(id, includeAllPublicFields)
- members?.let { localRepository.saveGroupMembers(id, it) }
+ members?.let { localRepository.savePartyMembers(id, it) }
return members
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
index c863f955c..15ff1a27b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
@@ -4,7 +4,6 @@ import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.data.local.UserLocalRepository
-import com.habitrpg.android.habitica.models.user.UserQuestStatus
import com.habitrpg.android.habitica.extensions.filterMapEmpty
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.ExceptionHandler
@@ -16,9 +15,11 @@ import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.responses.SkillResponse
import com.habitrpg.android.habitica.models.responses.UnlockResponse
import com.habitrpg.android.habitica.models.social.Group
+import com.habitrpg.android.habitica.models.social.GroupMembership
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
+import com.habitrpg.android.habitica.models.user.UserQuestStatus
import com.habitrpg.android.habitica.proxy.AnalyticsManager
import com.habitrpg.common.habitica.extensions.Optional
import com.habitrpg.shared.habitica.models.responses.TaskDirection
@@ -382,14 +383,16 @@ class UserRepositoryImpl(
override suspend fun retrieveTeamPlan(teamID: String): Group? {
val team = apiClient.getGroup(teamID) ?: return null
- team.tasks = apiClient.getTeamPlanTasks(teamID)
+ val tasks = apiClient.getTeamPlanTasks(teamID)
localRepository.save(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)
}
+ val members = apiClient.getGroupMembers(teamID, true) ?: return team
+ localRepository.save(members.map { it.id?.let { member -> GroupMembership(member, id) } }.filterNotNull())
+ members.let { localRepository.save(members) }
return team
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt
index 66a106520..b1f8dbdd5 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt
@@ -23,13 +23,14 @@ interface SocialLocalRepository : BaseLocalRepository {
fun deleteMessage(id: String)
- fun getGroupMembers(partyId: String): Flow>
+ fun getPartyMembers(partyId: String): Flow>
+ fun getGroupMembers(groupID: String): Flow>
fun updateRSVPNeeded(user: User?, newValue: Boolean)
fun likeMessage(chatMessage: ChatMessage, userId: String, liked: Boolean)
- fun saveGroupMembers(groupId: String?, members: List)
+ fun savePartyMembers(groupId: String?, members: List)
fun removeQuest(partyId: String)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt
index 06fd7a6af..eb1f5dec9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt
@@ -190,11 +190,18 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
executeTransaction { chatMessage?.deleteFromRealm() }
}
- override fun getGroupMembers(partyId: String) = realm.where(Member::class.java)
+ override fun getPartyMembers(partyId: String) = realm.where(Member::class.java)
.equalTo("party.id", partyId)
.findAll()
.toFlow()
+ override fun getGroupMembers(groupID: String) = realm.where(GroupMembership::class.java)
+ .equalTo("groupID", groupID)
+ .findAll()
+ .toFlow()
+ .map { memberships -> memberships.map { it.userID }.toTypedArray() }
+ .flatMapLatest { realm.where(Member::class.java).`in`("id", it).findAll().toFlow() }
+
override fun updateRSVPNeeded(user: User?, newValue: Boolean) {
executeTransaction { user?.party?.quest?.RSVPNeeded = newValue }
}
@@ -221,7 +228,7 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
}
}
- override fun saveGroupMembers(groupId: String?, members: List) {
+ override fun savePartyMembers(groupId: String?, members: List) {
saveSyncronous(members)
if (groupId != null) {
val existingMembers = realm.where(Member::class.java).equalTo("party.id", groupId).findAll()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
index 2f8d1e5bd..57bf84801 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
@@ -12,6 +12,7 @@ import hu.akarnokd.rxjava3.bridge.RxJavaBridge
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Maybe
import io.realm.Realm
+import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.toFlow
import kotlinx.coroutines.flow.Flow
@@ -22,32 +23,34 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getTasks(taskType: TaskType, userID: String, includedGroupIDs: Array): Flow> {
if (realm.isClosed) return emptyFlow()
- return realm.where(Task::class.java)
- .equalTo("typeValue", taskType.value)
- .beginGroup()
- .equalTo("userId", userID)
- .or()
- .`in`("group.groupID", includedGroupIDs)
- .endGroup()
- .sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING)
- .findAll()
+ return findTasks(taskType, userID, includedGroupIDs)
.toFlow()
.filter { it.isLoaded }
}
override fun getTasksFlowable(taskType: TaskType, userID: String, includedGroupIDs: Array): Flowable> {
if (realm.isClosed) return Flowable.empty()
- return RxJavaBridge.toV3Flowable(realm.where(Task::class.java)
+ return RxJavaBridge.toV3Flowable(findTasks(taskType, userID, includedGroupIDs)
+ .asFlowable()
+ .filter { it.isLoaded })
+ }
+
+ private fun findTasks(
+ taskType: TaskType,
+ ownerID: String,
+ includedGroupIDs: Array
+ ): RealmResults {
+ return realm.where(Task::class.java)
.equalTo("typeValue", taskType.value)
.beginGroup()
- .equalTo("userId", userID)
+ .equalTo("userId", ownerID)
.or()
.`in`("group.groupID", includedGroupIDs)
+ .or()
+ .equalTo("group.groupID", ownerID)
.endGroup()
.sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING)
.findAll()
- .asFlowable()
- .filter { it.isLoaded })
}
override fun getTasks(userId: String): Flow> {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt
new file mode 100644
index 000000000..4d97ad970
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt
@@ -0,0 +1,7 @@
+package com.habitrpg.android.habitica.helpers
+
+import android.content.res.Resources
+
+interface AssignedTextProvider {
+ fun textForTask(resources: Resources, assignedUsers: List): String
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/social/Group.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/social/Group.kt
index 85c8e53fb..433cf3c7d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/social/Group.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/social/Group.kt
@@ -4,7 +4,6 @@ import com.google.gson.annotations.SerializedName
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.tasks.TaskList
-import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.shared.habitica.models.tasks.TasksOrder
import io.realm.RealmList
import io.realm.RealmObject
@@ -34,7 +33,6 @@ open class Group : RealmObject(), BaseMainObject {
var logo: String? = null
var quest: Quest? = null
var privacy: String? = null
- var members: RealmList? = null
var challengeCount: Int = 0
var leaderMessage: String? = null
var leaderOnlyChallenges: Boolean = false
@@ -43,8 +41,6 @@ open class Group : RealmObject(), BaseMainObject {
@Ignore
var tasksOrder: TasksOrder? = null
- @Ignore
- var tasks: TaskList? = null
override fun equals(other: Any?): Boolean {
if (this === other) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
index 4e158006d..a041329ec 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
@@ -14,7 +14,6 @@ import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.ui.adapter.social.PartyMemberRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -66,7 +65,7 @@ class SkillMemberActivity : BaseActivity() {
userRepository.getUser()
.map { it?.party?.id }
.filterNotNull()
- .flatMapLatest { socialRepository.getGroupMembers(it) }
+ .flatMapLatest { socialRepository.getPartyMembers(it) }
.collect { viewAdapter?.data = it }
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
index 9e5a8dca9..b399fd74e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
@@ -8,7 +8,6 @@ import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.models.tasks.Task
-import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.android.habitica.ui.adapter.tasks.BaseTasksRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.viewHolders.BindableViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder
@@ -17,6 +16,7 @@ import com.habitrpg.android.habitica.ui.viewHolders.tasks.HabitViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.tasks.TodoViewHolder
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
+import com.habitrpg.shared.habitica.models.tasks.TaskType
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
@@ -69,18 +69,18 @@ class ChallengeTasksRecyclerViewAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindableViewHolder {
val viewHolder: BindableViewHolder = when (viewType) {
- TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { }) { task ->
+ TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { }, { task ->
taskOpenEventsSubject.onNext(task)
- }
- TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { }) { task ->
+ }, null)
+ TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { }, { task ->
taskOpenEventsSubject.onNext(task)
- }
- TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { }) { task ->
+ }, null)
+ TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { }, { task ->
taskOpenEventsSubject.onNext(task)
- }
- TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { }) { task ->
+ }, null)
+ TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { }, { task ->
taskOpenEventsSubject.onNext(task)
- }
+ }, null)
TYPE_ADD_ITEM -> AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), addItemSubject)
else -> DividerViewHolder(getContentView(parent, R.layout.challenge_task_divider))
}
@@ -124,7 +124,11 @@ class ChallengeTasksRecyclerViewAdapter(
addBtn.setOnClickListener { newTask?.let { callback.onNext(it) } }
}
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String
+ ) {
this.newTask = data
addBtn.text = data.text
}
@@ -134,7 +138,11 @@ class ChallengeTasksRecyclerViewAdapter(
private val dividerName: TextView = itemView.findViewById(R.id.divider_name)
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String
+ ) {
dividerName.text = data.text
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
index b645979aa..4dde8bfa9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
@@ -15,11 +15,10 @@ class DailiesRecyclerViewHolder(layoutResource: Int, viewModel: TasksViewModel)
{
task ->
taskOpenEventsSubject.onNext(task)
- }
- ) {
+ }, {
task ->
brokenTaskEventsSubject.onNext(task)
- }
+ }, viewModel)
} else {
super.onCreateViewHolder(parent, viewType)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
index f207c107e..f61beed69 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
@@ -14,11 +14,10 @@ class HabitsRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel)
{
task ->
taskOpenEventsSubject.onNext(task)
- }
- ) {
- task ->
- brokenTaskEventsSubject.onNext(task)
- }
+ }, {
+ task ->
+ brokenTaskEventsSubject.onNext(task)
+ }, viewModel)
} else {
super.onCreateViewHolder(parent, viewType)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
index 014c7ec48..e39b19af2 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
@@ -27,9 +27,8 @@ import io.realm.OrderedRealmCollection
abstract class RealmBaseTasksRecyclerViewAdapter(
private val layoutResource: Int,
- private val viewModel: TasksViewModel
+ val viewModel: TasksViewModel
) : BaseRecyclerViewAdapter(), TaskRecyclerViewAdapter {
- override var canScoreTasks = true
private var unfilteredData: List? = null
override var showAdventureGuide = false
set(value) {
@@ -80,8 +79,8 @@ abstract class RealmBaseTasksRecyclerViewAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position)
if (item != null && holder is BaseTaskViewHolder) {
- holder.isLocked = !canScoreTasks
- holder.bind(item, position, taskDisplayMode)
+ holder.isLocked = !viewModel.canScoreTask(item)
+ holder.bind(item, position, taskDisplayMode, viewModel.ownerID.value)
holder.errorButtonClicked = Action {
errorButtonEventsSubject.onNext("")
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
index 222d0a0a0..7983d1261 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
@@ -5,7 +5,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
-import com.habitrpg.shared.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
@@ -13,13 +12,16 @@ import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder
+import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
+import com.habitrpg.shared.habitica.models.responses.TaskDirection
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
class RewardsRecyclerViewAdapter(
private var customRewards: List?,
- private val layoutResource: Int
+ private val layoutResource: Int,
+ val viewModel: TasksViewModel
) : BaseRecyclerViewAdapter(), TaskRecyclerViewAdapter {
override var user: User? = null
set(value) {
@@ -30,7 +32,6 @@ class RewardsRecyclerViewAdapter(
notifyDataSetChanged()
}
override var showAdventureGuide: Boolean = false
- override var canScoreTasks = true
private var inAppRewards: List? = null
private val errorButtonEventsSubject: PublishSubject = PublishSubject.create()
@@ -80,8 +81,10 @@ class RewardsRecyclerViewAdapter(
taskScoreEventsSubject.onNext(Pair(task, direction))
}
},
- { task -> taskOpenEventsSubject.onNext(task) }
- ) { task -> brokenTaskEventsSubject.onNext(task) }
+ { task -> taskOpenEventsSubject.onNext(task) }, {
+ task ->
+ brokenTaskEventsSubject.onNext(task)
+ }, viewModel)
} else {
val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false))
viewHolder.purchaseCardAction = { purchaseCardSubject.onNext(it) }
@@ -93,8 +96,8 @@ class RewardsRecyclerViewAdapter(
if (customRewards != null && position < customRewardCount) {
val reward = customRewards?.get(position) ?: return
val gold = user?.stats?.gp ?: 0.0
- (holder as? RewardViewHolder)?.isLocked = !canScoreTasks
- (holder as? RewardViewHolder)?.bind(reward, position, reward.value <= gold, taskDisplayMode)
+ (holder as? RewardViewHolder)?.isLocked = false
+ (holder as? RewardViewHolder)?.bind(reward, position, reward.value <= gold, taskDisplayMode, viewModel.ownerID.value)
} else if (inAppRewards != null) {
val item = inAppRewards?.get(position - customRewardCount) ?: return
if (holder is ShopItemViewHolder) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
index 2f38295b6..73e650fa3 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
@@ -1,16 +1,15 @@
package com.habitrpg.android.habitica.ui.adapter.tasks
import android.view.View
-import com.habitrpg.shared.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
+import com.habitrpg.shared.habitica.models.responses.TaskDirection
import io.reactivex.rxjava3.core.Flowable
interface TaskRecyclerViewAdapter {
var user: User?
var showAdventureGuide: Boolean
- var canScoreTasks: Boolean
var data: List
val errorButtonEvents: Flowable
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
index dbebdd259..5be12d9b6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
@@ -15,11 +15,10 @@ class TodosRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel) :
{
task ->
taskOpenEventsSubject.onNext(task)
- }
- ) {
- task ->
- brokenTaskEventsSubject.onNext(task)
- }
+ }, {
+ task ->
+ brokenTaskEventsSubject.onNext(task)
+ }, viewModel)
} else {
super.onCreateViewHolder(parent, viewType)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
index 5eb2b0e9a..fca4bbc7d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
@@ -247,7 +247,7 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL
val user = userRepository.retrieveUser(false, true)
if (user?.hasParty == true) {
val party = socialRepository.retrieveGroup("party")
- socialRepository.retrieveGroupMembers(party?.id ?: "", true)
+ socialRepository.retrievePartyMembers(party?.id ?: "", true)
MainNavigationController.navigate(
R.id.partyFragment,
bundleOf(Pair("partyID", user.party?.id))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
index 0e19c117c..ca8aeef27 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
@@ -274,7 +274,7 @@ class ChallengeDetailFragment : BaseMainFragment
for (i in 0 until habits.size) {
val task = habits[i]
val entry = groupBinding.tasksLayout.inflate(R.layout.habit_item_card)
- val viewHolder = HabitViewHolder(entry, { _, _ -> }, {}, {})
+ val viewHolder = HabitViewHolder(entry, { _, _ -> }, {}, {}, null)
viewHolder.isLocked = true
viewHolder.bind(task, i, "normal")
groupBinding.tasksLayout.addView(entry)
@@ -289,7 +289,7 @@ class ChallengeDetailFragment : BaseMainFragment
for (i in 0 until dailies.size) {
val task = dailies[i]
val entry = groupBinding.tasksLayout.inflate(R.layout.daily_item_card)
- val viewHolder = DailyViewHolder(entry, { _, _ -> }, { _, _ -> }, {}, {})
+ val viewHolder = DailyViewHolder(entry, { _, _ -> }, { _, _ -> }, {}, {}, null)
viewHolder.isLocked = true
viewHolder.bind(task, i, "normal")
groupBinding.tasksLayout.addView(entry)
@@ -304,7 +304,7 @@ class ChallengeDetailFragment : BaseMainFragment
for (i in 0 until todos.size) {
val task = todos[i]
val entry = groupBinding.tasksLayout.inflate(R.layout.todo_item_card)
- val viewHolder = TodoViewHolder(entry, { _, _ -> }, { _, _ -> }, {}, {})
+ val viewHolder = TodoViewHolder(entry, { _, _ -> }, { _, _ -> }, {}, {}, null)
viewHolder.isLocked = true
viewHolder.bind(task, i, "normal")
groupBinding.tasksLayout.addView(entry)
@@ -319,9 +319,9 @@ class ChallengeDetailFragment : BaseMainFragment
for (i in 0 until rewards.size) {
val task = rewards[i]
val entry = groupBinding.tasksLayout.inflate(R.layout.reward_item_card)
- val viewHolder = RewardViewHolder(entry, { _, _ -> }, {}, {})
+ val viewHolder = RewardViewHolder(entry, { _, _ -> }, {}, {}, null)
viewHolder.isLocked = true
- viewHolder.bind(task, i, true, "normal")
+ viewHolder.bind(task, i, true, "normal", null)
groupBinding.tasksLayout.addView(entry)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/NoPartyFragmentFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/NoPartyFragmentFragment.kt
index f9ed1ea99..1843af8bd 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/NoPartyFragmentFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/NoPartyFragmentFragment.kt
@@ -181,7 +181,7 @@ class NoPartyFragmentFragment : BaseMainFragment() {
if (user?.hasParty == true) {
lifecycleScope.launch(ExceptionHandler.coroutine()) {
val group = socialRepository.retrieveGroup("party")
- socialRepository.retrieveGroupMembers(group?.id ?: "", true)
+ socialRepository.retrievePartyMembers(group?.id ?: "", true)
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
index 0e5a98fb3..f4552970b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
@@ -114,12 +114,11 @@ open class TaskRecyclerViewFragment : BaseFragment HabitsRecyclerViewAdapter(R.layout.habit_item_card, viewModel)
TaskType.DAILY -> DailiesRecyclerViewHolder(R.layout.daily_item_card, viewModel)
TaskType.TODO -> TodosRecyclerViewAdapter(R.layout.todo_item_card, viewModel)
- TaskType.REWARD -> RewardsRecyclerViewAdapter(null, R.layout.reward_item_card)
+ TaskType.REWARD -> RewardsRecyclerViewAdapter(null, R.layout.reward_item_card, viewModel)
else -> null
}
recyclerAdapter = adapter as? TaskRecyclerViewAdapter
- recyclerAdapter?.canScoreTasks = canScoreTaks
binding?.recyclerView?.adapter = adapter
viewModel.getFilterSet(taskType)?.observe(viewLifecycleOwner) {
@@ -152,7 +151,6 @@ open class TaskRecyclerViewFragment : BaseFragment(), SearchView.O
if (args.ownerID?.isNotBlank() == true) {
viewModel.canSwitchOwners.value = false
viewModel.ownerID.value = args.ownerID ?: viewModel.userViewModel.userID
- } else {
+ } else if (viewModel.ownerID.value?.isNotBlank() != true) {
viewModel.ownerID.value = viewModel.userViewModel.userID
}
if (taskTypeValue?.isNotBlank() == true) {
@@ -185,10 +183,6 @@ class TasksFragment : BaseMainFragment(), SearchView.O
viewModel.refreshData { }
true
}
- R.id.action_team_info -> {
- MainNavigationController.navigate(R.id.guildFragment, bundleOf(Pair("groupID", viewModel.ownerID)))
- true
- }
else -> super.onOptionsItemSelected(item)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
index 1d9936845..fa1f6469f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
@@ -5,5 +5,9 @@ import androidx.recyclerview.widget.RecyclerView
abstract class BindableViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- abstract fun bind(data: T, position: Int, displayMode: String)
+ abstract fun bind(
+ data: T,
+ position: Int,
+ displayMode: String
+ )
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
index eef5b0bcd..9b8e25deb 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
@@ -12,7 +12,9 @@ import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.content.ContextCompat
+import androidx.lifecycle.MutableLiveData
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.viewHolders.BindableViewHolder
@@ -34,7 +36,8 @@ abstract class BaseTaskViewHolder constructor(
itemView: View,
var scoreTaskFunc: ((Task, TaskDirection) -> Unit),
var openTaskFunc: ((Pair) -> Unit),
- var brokenTaskFunc: ((Task) -> Unit)
+ var brokenTaskFunc: ((Task) -> Unit),
+ var assignedTextProvider: AssignedTextProvider?
) : BindableViewHolder(itemView), View.OnTouchListener {
private val scope = MainScope()
@@ -44,6 +47,7 @@ abstract class BaseTaskViewHolder constructor(
var isLocked = false
protected var context: Context
private val mainTaskWrapper: ViewGroup = itemView.findViewById(R.id.main_task_wrapper)
+ protected val assignedTextView: EllipsisTextView = itemView.findViewById(R.id.assigned_textview)
protected val titleTextView: EllipsisTextView = itemView.findViewById(R.id.checkedTextView)
protected val notesTextView: EllipsisTextView? = itemView.findViewById(R.id.notesTextView)
protected val calendarIconView: ImageView? = itemView.findViewById(R.id.iconViewCalendar)
@@ -52,7 +56,7 @@ abstract class BaseTaskViewHolder constructor(
private val iconViewChallenge: ImageView? = itemView.findViewById(R.id.iconviewChallenge)
private val iconViewReminder: ImageView? = itemView.findViewById(R.id.iconviewReminder)
private val taskIconWrapper: LinearLayout? = itemView.findViewById(R.id.taskIconWrapper)
- private val approvalRequiredTextView: TextView? = itemView.findViewById(R.id.approvalRequiredTextField)
+ private val approvalRequiredTextView: TextView = itemView.findViewById(R.id.approvalRequiredTextField)
private val expandNotesButton: Button? = itemView.findViewById(R.id.expand_notes_button)
private val syncingView: ProgressBar? = itemView.findViewById(R.id.syncing_view)
private val errorIconView: ImageButton? = itemView.findViewById(R.id.error_icon)
@@ -133,6 +137,15 @@ abstract class BaseTaskViewHolder constructor(
}
override fun bind(data: Task, position: Int, displayMode: String) {
+ bind(data, position, displayMode, null)
+ }
+
+ open fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String,
+ ownerID: String?
+ ) {
notesExpanded = false
task = data
itemView.setBackgroundColor(context.getThemeColor(R.attr.colorContentBackground))
@@ -227,7 +240,7 @@ abstract class BaseTaskViewHolder constructor(
}
configureSpecialTaskTextView(data)
- iconViewTeam?.visibility = if (data.isGroupTask) View.VISIBLE else View.GONE
+ iconViewTeam?.visibility = if (data.isGroupTask && ownerID != data.group?.groupID) View.VISIBLE else View.GONE
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
} else {
@@ -236,9 +249,16 @@ abstract class BaseTaskViewHolder constructor(
}
if (data.isPendingApproval) {
- approvalRequiredTextView?.visibility = View.VISIBLE
+ approvalRequiredTextView.visibility = View.VISIBLE
} else {
- approvalRequiredTextView?.visibility = View.GONE
+ approvalRequiredTextView.visibility = View.GONE
+ }
+
+ if (data.group?.assignedUsers?.isEmpty() != false) {
+ assignedTextView.text = assignedTextProvider?.textForTask(context.resources, data.group?.assignedUsers ?: emptyList())
+ assignedTextView.visibility = View.VISIBLE
+ } else {
+ assignedTextView.visibility = View.GONE
}
syncingView?.visibility = if (task?.isSaving == true) View.VISIBLE else View.GONE
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt
index 6d331221a..211c293df 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt
@@ -12,6 +12,7 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
@@ -30,8 +31,9 @@ abstract class ChecklistedViewHolder(
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
openTaskFunc: ((Pair) -> Unit),
- brokenTaskFunc: ((Task) -> Unit)
-) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc) {
+ brokenTaskFunc: ((Task) -> Unit),
+ assignedTextProvider: AssignedTextProvider?
+) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
private val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder)
private val checkmarkView: ImageView = itemView.findViewById(R.id.checkmark)
@@ -48,7 +50,12 @@ abstract class ChecklistedViewHolder(
checklistIndicatorWrapper.setOnClickListener { onChecklistIndicatorClicked() }
}
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String,
+ ownerID: String?
+ ) {
var completed = data.completed
if (data.isPendingApproval) {
completed = false
@@ -69,7 +76,7 @@ abstract class ChecklistedViewHolder(
this.updateChecklistDisplay()
this.checklistIndicatorWrapper.visibility = if (data.checklist?.size == 0) View.GONE else View.VISIBLE
- super.bind(data, position, displayMode)
+ super.bind(data, position, displayMode, ownerID)
val regularBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_unchecked else R.drawable.todo_unchecked
val completedBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_checked else R.drawable.todo_checked
val inactiveBoxBackground = R.drawable.daily_inactive
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt
index 9b1665f04..26ab150a7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt
@@ -1,9 +1,10 @@
package com.habitrpg.android.habitica.ui.viewHolders.tasks
import android.view.View
-import com.habitrpg.shared.habitica.models.responses.TaskDirection
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
+import com.habitrpg.shared.habitica.models.responses.TaskDirection
import java.text.DateFormat
import java.util.Calendar
import java.util.Date
@@ -13,8 +14,9 @@ class DailyViewHolder(
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
openTaskFunc: ((Pair) -> Unit),
- brokenTaskFunc: ((Task) -> Unit)
-) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc) {
+ brokenTaskFunc: ((Task) -> Unit),
+ assignedTextProvider: AssignedTextProvider?
+) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
override val taskIconWrapperIsVisible: Boolean
get() {
@@ -25,7 +27,12 @@ class DailyViewHolder(
return isVisible
}
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String,
+ ownerID: String?
+ ) {
this.task = data
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
@@ -59,7 +66,7 @@ class DailyViewHolder(
reminderTextView.text = reminderString
}
- super.bind(data, position, displayMode)
+ super.bind(data, position, displayMode, ownerID)
}
override fun shouldDisplayAsActive(newTask: Task?): Boolean {
@@ -68,7 +75,7 @@ class DailyViewHolder(
override fun configureSpecialTaskTextView(task: Task) {
super.configureSpecialTaskTextView(task)
- if (task.streak ?: 0 > 0) {
+ if ((task.streak ?: 0) > 0) {
this.streakTextView.text = task.streak.toString()
this.streakTextView.visibility = View.VISIBLE
this.streakIconView.visibility = View.VISIBLE
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt
index 2c8cf24ab..e31f0337d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt
@@ -6,7 +6,9 @@ import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.core.content.ContextCompat
+import androidx.lifecycle.MutableLiveData
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.shared.habitica.models.responses.TaskDirection
@@ -14,8 +16,9 @@ class HabitViewHolder(
itemView: View,
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
openTaskFunc: ((Pair) -> Unit),
- brokenTaskFunc: ((Task) -> Unit)
-) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc) {
+ brokenTaskFunc: ((Task) -> Unit),
+ assignedTextProvider: AssignedTextProvider?
+) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
private val btnPlusWrapper: FrameLayout = itemView.findViewById(R.id.btnPlusWrapper)
private val btnPlusIconView: ImageView = itemView.findViewById(R.id.btnPlusIconView)
@@ -33,7 +36,12 @@ class HabitViewHolder(
btnMinus.isClickable = true
}
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String,
+ ownerID: String?
+ ) {
this.task = data
if (data.up == true) {
val plusIcon = if (isLocked) {
@@ -121,7 +129,7 @@ class HabitViewHolder(
}
reminderTextView.visibility = View.GONE
calendarIconView?.visibility = View.GONE
- super.bind(data, position, displayMode)
+ super.bind(data, position, displayMode, ownerID)
if (data.up == false && data.down == false) {
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt
index fb5b8b7d5..06f778ecb 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt
@@ -6,18 +6,20 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.RewardItemCardBinding
-import com.habitrpg.common.habitica.helpers.NumberAbbreviator
-import com.habitrpg.shared.habitica.models.responses.TaskDirection
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.ItemDetailDialog
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
+import com.habitrpg.common.habitica.helpers.NumberAbbreviator
+import com.habitrpg.shared.habitica.models.responses.TaskDirection
class RewardViewHolder(
itemView: View,
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
openTaskFunc: ((Pair) -> Unit),
- brokenTaskFunc: ((Task) -> Unit)
-) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc) {
+ brokenTaskFunc: ((Task) -> Unit),
+ assignedTextProvider: AssignedTextProvider?
+) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
private val binding = RewardItemCardBinding.bind(itemView)
private val isItem: Boolean
@@ -61,10 +63,10 @@ class RewardViewHolder(
binding.buyButton.isEnabled = !taskActionsDisabled
}
- fun bind(reward: Task, position: Int, canBuy: Boolean, displayMode: String) {
+ fun bind(reward: Task, position: Int, canBuy: Boolean, displayMode: String, ownerID: String?) {
this.task = reward
streakTextView.visibility = View.GONE
- super.bind(reward, position, displayMode)
+ super.bind(reward, position, displayMode, ownerID)
binding.priceLabel.text = NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0)
if (isLocked) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt
index 6657681ba..b7bf10b2a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt
@@ -1,9 +1,10 @@
package com.habitrpg.android.habitica.ui.viewHolders.tasks
import android.view.View
-import com.habitrpg.shared.habitica.models.responses.TaskDirection
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
import com.habitrpg.android.habitica.models.tasks.Task
+import com.habitrpg.shared.habitica.models.responses.TaskDirection
import java.text.DateFormat
class TodoViewHolder(
@@ -11,17 +12,23 @@ class TodoViewHolder(
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
openTaskFunc: ((Pair) -> Unit),
- brokenTaskFunc: ((Task) -> Unit)
-) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc) {
+ brokenTaskFunc: ((Task) -> Unit),
+ assignedTextProvider: AssignedTextProvider?
+) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
private val dateFormatter: DateFormat = android.text.format.DateFormat.getDateFormat(context)
- override fun bind(data: Task, position: Int, displayMode: String) {
+ override fun bind(
+ data: Task,
+ position: Int,
+ displayMode: String,
+ ownerID: String?
+ ) {
this.task = data
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
reminderTextView.visibility = View.GONE
this.streakTextView.visibility = View.GONE
- super.bind(data, position, displayMode)
+ super.bind(data, position, displayMode, ownerID)
}
override fun configureSpecialTaskTextView(task: Task) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
index e80a95bfd..0b07975b4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
@@ -20,7 +20,6 @@ import io.realm.kotlin.toFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -129,7 +128,7 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
}) {
val group = socialRepository.retrieveGroup(groupID ?: "")
if (groupViewType == GroupViewType.PARTY) {
- socialRepository.retrieveGroupMembers(group?.id ?: "", true)
+ socialRepository.retrievePartyMembers(group?.id ?: "", true)
}
function?.invoke()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt
index 810881d17..dcf766b42 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt
@@ -43,6 +43,10 @@ class MainUserViewModel(private val providedUserID: String, val userRepository:
.filterNotNull()
.distinctUntilChanged { old, new -> old.id == new.id }
.flatMapLatest { socialRepository.getGroup(it.id) }
+ var currentTeamPlanMembers = currentTeamPlan
+ .filterNotNull()
+ .distinctUntilChanged { old, new -> old.id == new.id }
+ .flatMapLatest { socialRepository.getGroupMembers(it.id) }
fun onCleared() {
userRepository.close()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
index b00f435aa..344def085 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
@@ -24,7 +24,7 @@ class PartyViewModel(initializeComponent: Boolean) : GroupViewModel(initializeCo
private val membersFlow = groupIDFlow
.filterNotNull()
- .flatMapLatest { socialRepository.getGroupMembers(it) }
+ .flatMapLatest { socialRepository.getPartyMembers(it) }
private val members = membersFlow.asLiveData()
init {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt
index 0f3a0d098..566958c84 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt
@@ -1,15 +1,18 @@
package com.habitrpg.android.habitica.ui.viewmodels
import android.content.SharedPreferences
+import android.content.res.Resources
import android.text.format.DateUtils
import androidx.core.content.edit
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
+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.data.TaskRepository
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.helpers.AppConfigManager
+import com.habitrpg.android.habitica.helpers.AssignedTextProvider
import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.models.TeamPlan
import com.habitrpg.android.habitica.models.tasks.Task
@@ -25,7 +28,7 @@ import kotlinx.coroutines.launch
import java.util.Date
import javax.inject.Inject
-class TasksViewModel : BaseViewModel() {
+class TasksViewModel : BaseViewModel(), AssignedTextProvider {
private var compositeSubscription: CompositeDisposable = CompositeDisposable()
override fun inject(component: UserComponent) {
@@ -301,4 +304,19 @@ class TasksViewModel : BaseViewModel() {
}
return query
}
+
+ fun canScoreTask(item: Task): Boolean {
+ if (!item.isGroupTask) {
+ return true
+ }
+ return item.isAssignedToUser(userViewModel.userID) || item.group?.assignedUsers?.isEmpty() != false
+ }
+
+ override fun textForTask(resources: Resources, assignedUsers: List): String {
+ return if (assignedUsers.contains(userViewModel.userID)) {
+ resources.getQuantityString(R.plurals.you_x_others, assignedUsers.size - 1)
+ } else {
+ resources.getQuantityString(R.plurals.people, assignedUsers.size)
+ }
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
index 43ac140c9..efcd3b6aa 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
@@ -11,13 +11,16 @@ import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
@@ -33,15 +36,19 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.common.habitica.helpers.NumberAbbreviator
@@ -104,7 +111,8 @@ fun AppHeaderView(
viewModel: MainUserViewModel,
) {
val user by viewModel.user.observeAsState(null)
- val displayedTeamPlan by viewModel.currentTeamPlan.collectAsState()
+ val teamPlan by viewModel.currentTeamPlan.collectAsState(null)
+ val teamPlanMembers by viewModel.currentTeamPlanMembers.collectAsState(null)
Column {
Row {
ComposableAvatarView(
@@ -114,7 +122,7 @@ fun AppHeaderView(
.padding(end = 16.dp)
)
Column(modifier = Modifier.height(100.dp)) {
- Row(modifier = Modifier.weight(1f)) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.weight(1f)) {
LabeledBar(
icon = HabiticaIconsHelper.imageOfHeartLightBg(),
@@ -122,7 +130,7 @@ fun AppHeaderView(
color = colorResource(R.color.hpColor),
value = user?.stats?.hp ?: 0.0,
maxValue = user?.stats?.maxHealth?.toDouble() ?: 0.0,
- displayCompact = displayedTeamPlan != null,
+ displayCompact = teamPlan != null,
modifier = Modifier.weight(1f)
)
LabeledBar(
@@ -131,7 +139,7 @@ fun AppHeaderView(
color = colorResource(R.color.xpColor),
value = user?.stats?.exp ?: 0.0,
maxValue = user?.stats?.toNextLevel?.toDouble() ?: 0.0,
- displayCompact = displayedTeamPlan != null,
+ displayCompact = teamPlan != null,
modifier = Modifier.weight(1f)
)
if (user?.hasClass == true) {
@@ -141,50 +149,73 @@ fun AppHeaderView(
color = colorResource(R.color.mpColor),
value = user?.stats?.mp ?: 0.0,
maxValue = user?.stats?.maxMP?.toDouble() ?: 0.0,
- displayCompact = displayedTeamPlan != null,
+ displayCompact = teamPlan != null,
modifier = Modifier.weight(1f)
)
}
}
val animWidth = with(LocalDensity.current) { 48.dp.roundToPx() }
AnimatedVisibility(
- visible = displayedTeamPlan != null,
+ visible = teamPlan != null,
enter = slideInHorizontally { animWidth } + fadeIn(),
exit = slideOutHorizontally { animWidth } + fadeOut()) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
- .height(72.dp)
- .width(48.dp)
.padding(start = 12.dp)
+ .width(72.dp)
+ .height(48.dp)
.clip(RoundedCornerShape(8.dp))
.background(
colorResource(R.color.window_background)
)
+ .clickable {
+ MainNavigationController.navigate(
+ R.id.guildFragment,
+ bundleOf("groupID" to teamPlan?.id)
+ )
+ }
) {
- Text("M")
+ Image(painterResource(R.drawable.icon_chat), null, colorFilter = ColorFilter.tint(
+ colorResource(R.color.text_ternary)))
}
}
}
val animHeight = with(LocalDensity.current) { 40.dp.roundToPx() }
AnimatedVisibility(
- visible = displayedTeamPlan != null,
+ visible = teamPlan != null,
enter = slideInVertically { animHeight } + fadeIn(),
exit = slideOutVertically { animHeight } + fadeOut()) {
Row(
- horizontalArrangement = Arrangement.Center,
+ horizontalArrangement = Arrangement.spacedBy(14.dp, Alignment.CenterHorizontally),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
- .height(40.dp)
.padding(top = 12.dp)
+ .height(40.dp)
+ .width(72.dp)
.clip(RoundedCornerShape(8.dp))
.background(
colorResource(R.color.window_background)
)
+ .clickable {
+ MainNavigationController.navigate(
+ R.id.guildFragment,
+ bundleOf("groupID" to teamPlan?.id)
+ )
+ }
) {
- Text("A")
+ for (member in teamPlanMembers?.filter { it.id != user?.id }?.take(6) ?: emptyList()) {
+ Box(modifier = Modifier.clip(CircleShape).size(26.dp).padding(end = 6.dp, top = 4.dp)) {
+ ComposableAvatarView(
+ avatar = member,
+ Modifier
+ .size(64.dp)
+ .requiredSize(64.dp)
+ )
+ }
+ }
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ComposableAvatarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ComposableAvatarView.kt
index 8b2180169..5bee6721a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ComposableAvatarView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ComposableAvatarView.kt
@@ -1,11 +1,26 @@
package com.habitrpg.android.habitica.ui.views
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.viewinterop.AndroidView
import com.habitrpg.common.habitica.views.AvatarView
import com.habitrpg.shared.habitica.models.Avatar
+object AvatarCircleShape : Shape {
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density
+ ): Outline =
+ CircleShape.createOutline(size, 20f, 20f, 20f, 20f, layoutDirection)
+}
+
@Composable
fun ComposableAvatarView(
avatar: Avatar?,