mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-20 04:39:04 +00:00
implement new header and group task display
This commit is contained in:
parent
03d5648003
commit
fc96c6146e
39 changed files with 293 additions and 137 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 109 B |
5
Habitica/res/drawable/icon_chat.xml
Normal file
5
Habitica/res/drawable/icon_chat.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
|
||||
</vector>
|
||||
|
|
@ -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">
|
||||
<TextView
|
||||
android:id="@+id/assigned_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Caption4"
|
||||
android:text="@string/pending_approval"
|
||||
android:textColor="@color/text_ternary"
|
||||
/>
|
||||
<com.habitrpg.android.habitica.ui.views.EllipsisTextView
|
||||
android:id="@+id/checkedTextView"
|
||||
style="@style/Subheader3"
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="com.habitrpg.android.habitica.TaskActivity">
|
||||
<item android:id="@+id/action_team_info"
|
||||
android:title="@string/team_information"
|
||||
android:icon="@drawable/team_info_icon"
|
||||
app:showAsAction="collapseActionView|always" />
|
||||
<item android:id="@+id/action_search"
|
||||
android:title="@string/search"
|
||||
android:icon="@drawable/ic_search"
|
||||
|
|
|
|||
|
|
@ -1255,4 +1255,13 @@
|
|||
<string name="copy_tasks_description">Show assigned and open tasks on your personal task lists</string>
|
||||
<string name="copy_shared_tasks">Copy shared tasks</string>
|
||||
<string name="group_plan_settings">Group Plan Settings</string>
|
||||
<plurals name="you_x_others">
|
||||
<item quantity="zero">You</item>
|
||||
<item quantity="one">You, %d other</item>
|
||||
<item quantity="other">You, %d others</item>
|
||||
</plurals>
|
||||
<plurals name="people">
|
||||
<item quantity="one">%d Person</item>
|
||||
<item quantity="other">%d People</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -78,8 +78,9 @@ interface SocialRepository : BaseRepository {
|
|||
|
||||
suspend fun postPrivateMessage(recipientId: String, message: String): List<ChatMessage>?
|
||||
|
||||
suspend fun getPartyMembers(id: String): Flow<List<Member>>
|
||||
suspend fun getGroupMembers(id: String): Flow<List<Member>>
|
||||
suspend fun retrieveGroupMembers(id: String, includeAllPublicFields: Boolean): List<Member>?
|
||||
suspend fun retrievePartyMembers(id: String, includeAllPublicFields: Boolean): List<Member>?
|
||||
|
||||
fun inviteToGroup(id: String, inviteData: Map<String, Any>): Flowable<List<Void>>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class SocialRepositoryImpl(
|
|||
|
||||
override suspend fun removeMemberFromGroup(groupID: String, userID: String): List<Member>? {
|
||||
apiClient.removeMemberFromGroup(groupID, userID)
|
||||
return retrieveGroupMembers(groupID, true)
|
||||
return retrievePartyMembers(groupID, true)
|
||||
}
|
||||
|
||||
override fun blockMember(userID: String): Flowable<List<String>> {
|
||||
|
|
@ -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<Member>? {
|
||||
override suspend fun retrievePartyMembers(id: String, includeAllPublicFields: Boolean): List<Member>? {
|
||||
val members = apiClient.getGroupMembers(id, includeAllPublicFields)
|
||||
members?.let { localRepository.saveGroupMembers(id, it) }
|
||||
members?.let { localRepository.savePartyMembers(id, it) }
|
||||
return members
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,14 @@ interface SocialLocalRepository : BaseLocalRepository {
|
|||
|
||||
fun deleteMessage(id: String)
|
||||
|
||||
fun getGroupMembers(partyId: String): Flow<List<Member>>
|
||||
fun getPartyMembers(partyId: String): Flow<List<Member>>
|
||||
fun getGroupMembers(groupID: String): Flow<List<Member>>
|
||||
|
||||
fun updateRSVPNeeded(user: User?, newValue: Boolean)
|
||||
|
||||
fun likeMessage(chatMessage: ChatMessage, userId: String, liked: Boolean)
|
||||
|
||||
fun saveGroupMembers(groupId: String?, members: List<Member>)
|
||||
fun savePartyMembers(groupId: String?, members: List<Member>)
|
||||
|
||||
fun removeQuest(partyId: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Member>) {
|
||||
override fun savePartyMembers(groupId: String?, members: List<Member>) {
|
||||
saveSyncronous(members)
|
||||
if (groupId != null) {
|
||||
val existingMembers = realm.where(Member::class.java).equalTo("party.id", groupId).findAll()
|
||||
|
|
|
|||
|
|
@ -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<String>): Flow<List<Task>> {
|
||||
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<String>): Flowable<out List<Task>> {
|
||||
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<String>
|
||||
): RealmResults<Task> {
|
||||
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<List<Task>> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import android.content.res.Resources
|
||||
|
||||
interface AssignedTextProvider {
|
||||
fun textForTask(resources: Resources, assignedUsers: List<String>): String
|
||||
}
|
||||
|
|
@ -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<User>? = 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) {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Task> {
|
||||
val viewHolder: BindableViewHolder<Task> = 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,10 @@ class DailiesRecyclerViewHolder(layoutResource: Int, viewModel: TasksViewModel)
|
|||
{
|
||||
task ->
|
||||
taskOpenEventsSubject.onNext(task)
|
||||
}
|
||||
) {
|
||||
}, {
|
||||
task ->
|
||||
brokenTaskEventsSubject.onNext(task)
|
||||
}
|
||||
}, viewModel)
|
||||
} else {
|
||||
super.onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,8 @@ import io.realm.OrderedRealmCollection
|
|||
|
||||
abstract class RealmBaseTasksRecyclerViewAdapter(
|
||||
private val layoutResource: Int,
|
||||
private val viewModel: TasksViewModel
|
||||
val viewModel: TasksViewModel
|
||||
) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), TaskRecyclerViewAdapter {
|
||||
override var canScoreTasks = true
|
||||
private var unfilteredData: List<Task>? = 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("")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Task>?,
|
||||
private val layoutResource: Int
|
||||
private val layoutResource: Int,
|
||||
val viewModel: TasksViewModel
|
||||
) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), 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<ShopItem>? = null
|
||||
|
||||
private val errorButtonEventsSubject: PublishSubject<String> = 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) {
|
||||
|
|
|
|||
|
|
@ -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<Task>
|
||||
|
||||
val errorButtonEvents: Flowable<String>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), 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))
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ class ChallengeDetailFragment : BaseMainFragment<FragmentChallengeDetailBinding>
|
|||
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<FragmentChallengeDetailBinding>
|
|||
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<FragmentChallengeDetailBinding>
|
|||
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<FragmentChallengeDetailBinding>
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ class NoPartyFragmentFragment : BaseMainFragment<FragmentNoPartyBinding>() {
|
|||
if (user?.hasParty == true) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val group = socialRepository.retrieveGroup("party")
|
||||
socialRepository.retrieveGroupMembers(group?.id ?: "", true)
|
||||
socialRepository.retrievePartyMembers(group?.id ?: "", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,12 +114,11 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
TaskType.HABIT -> 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<FragmentRefreshRecyclerviewBi
|
|||
viewModel.ownerID.observe(viewLifecycleOwner) {
|
||||
canEditTasks = viewModel.isPersonalBoard
|
||||
canScoreTaks = viewModel.isPersonalBoard
|
||||
recyclerAdapter?.canScoreTasks = canScoreTaks
|
||||
updateTaskSubscription(it)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
|
|
@ -346,7 +344,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
if (taskFlowJob?.isActive == true) {
|
||||
taskFlowJob?.cancel()
|
||||
}
|
||||
val additionalGroupIDs = viewModel.userViewModel.mirrorGroupTasks.toTypedArray()
|
||||
val additionalGroupIDs = if (ownerID == viewModel.userViewModel.userID) viewModel.userViewModel.mirrorGroupTasks.toTypedArray() else emptyArray()
|
||||
taskFlowJob = lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
taskRepository.getTasks(taskType, ownerID, additionalGroupIDs).collect {
|
||||
recyclerAdapter?.updateUnfilteredData(it)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
|
|
@ -27,7 +26,6 @@ import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
|
|||
import com.habitrpg.android.habitica.extensions.setTintWith
|
||||
import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
|
|
@ -81,7 +79,7 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), 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<FragmentViewpagerBinding>(), 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,9 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
abstract class BindableViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
abstract fun bind(data: T, position: Int, displayMode: String)
|
||||
abstract fun bind(
|
||||
data: T,
|
||||
position: Int,
|
||||
displayMode: String
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> Unit),
|
||||
var brokenTaskFunc: ((Task) -> Unit)
|
||||
var brokenTaskFunc: ((Task) -> Unit),
|
||||
var assignedTextProvider: AssignedTextProvider?
|
||||
) : BindableViewHolder<Task>(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
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> 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
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> 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
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> 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))
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> 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) {
|
||||
|
|
|
|||
|
|
@ -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<Task, View>) -> 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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>): 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?,
|
||||
|
|
|
|||
Loading…
Reference in a new issue