work in feedback

This commit is contained in:
Phillip Thelen 2023-01-13 10:24:05 +01:00
parent b199c2ea73
commit 553cd19176
28 changed files with 285 additions and 61 deletions

View file

@ -95,7 +95,7 @@
app:layout_anchorGravity="bottom"
app:layout_collapseMode="pin"
app:tabGravity="fill"
app:tabIndicatorColor="?colorPrimary"
app:tabIndicatorColor="?textColorPrimary"
app:tabMode="fixed" />
<FrameLayout
android:id="@+id/connection_issue_view"

View file

@ -114,7 +114,7 @@
android:text="@string/gem_purchase_subtitle"
android:gravity="center"
android:textStyle="normal|bold"
android:textColor="?colorPrimary"
android:textColor="?colorPrimaryText"
android:textSize="16sp"
android:lineSpacingExtra="4dp"
android:layout_marginTop="23dp"
@ -234,7 +234,7 @@
android:layout_height="wrap_content"
android:text="@string/gift_gems"
android:background="@color/transparent"
android:textColor="?colorAccent"
android:textColor="?colorPrimaryText"
android:textAllCaps="false"/>
<TextView android:id="@+id/supportTextView"
android:layout_height="wrap_content"

View file

@ -79,6 +79,7 @@
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
android:layout_gravity="center"
android:visibility="gone"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -1319,7 +1319,7 @@
<string name="gryphatrice_description">The rare, mystical Gryphatrice joins the birthday bash! Dont miss your chance to own this exclusive animated Pet.</string>
<string name="thanks_for_support">Thanks for your support!</string>
<string name="plenty_of_potions">Plenty of Potions</string>
<string name="for_for_free">For for Free</string>
<string name="for_for_free">Four for Free</string>
<string name="buy_for_x">Buy for %s</string>
<string name="buy_for">Buy for</string>
<string name="plenty_of_potions_description">Were bringing back 10 of the communitys favorite Magic Hatching Potions. Head over to the Market to fill out your collection!</string>
@ -1332,6 +1332,12 @@
<string name="jubilant_gryphatrice_confirmation">You purchased the Jubilant Gryphatrice!</string>
<string name="jubilant_gryphatrice_confirmation_gift">You gifted the Jubilant Gryphatrice!</string>
<string name="open_settings">Open Settings</string>
<string name="undo">Undo</string>
<string name="day_x">Day %d</string>
<string name="a_party_robe">A Party Robe</string>
<string name="twenty_gems">20 Gems</string>
<string name="background">Background</string>
<string name="birthday_set">Birthday Set</string>
<plurals name="you_x_others">
<item quantity="zero">You</item>

View file

@ -459,4 +459,7 @@ interface ApiService {
@GET("hall/heroes/{memberID}")
suspend fun getHallMember(@Path("memberID") memberID: String): HabitResponse<Member>
@POST("tasks/{taskID}/needs-work/{userID}")
suspend fun markTaskNeedsWork(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse<Task>
}

View file

@ -275,4 +275,5 @@ interface ApiClient {
suspend fun unassignFromTask(taskId: String, userID: String): Task?
suspend fun updateMember(memberID: String, updateData: Map<String, Any?>): Member?
suspend fun getHallMember(userId: String): Member?
suspend fun markTaskNeedsWork(taskID: String, userID: String): Task?
}

View file

@ -65,6 +65,7 @@ interface TaskRepository : BaseRepository {
suspend fun retrieveCompletedTodos(userId: String? = null): TaskList?
suspend fun syncErroredTasks(): List<Task>?
suspend fun unlinkAllTasks(challengeID: String?, keepOption: String): Void?
fun getTasksForChallenge(challengeID: String?): Flow<out List<Task>>
fun getTasksForChallenge(challengeID: String?): Flow<List<Task>>
suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData?
suspend fun markTaskNeedsWork(task: Task, userID: String)
}

View file

@ -606,14 +606,22 @@ class ApiClientImpl(
return process { apiService.leaveQuest(groupId) }
}
private val lastPurchaseValidation: Date? = null
override suspend fun validatePurchase(request: PurchaseValidationRequest): PurchaseValidationResult? {
return process { apiService.validatePurchase(request) }
// make sure a purchase attempt doesn't happen
return if (lastPurchaseValidation == null || Date().time - lastPurchaseValidation.time > 5000) {
return process { apiService.validatePurchase(request) }
} else null
}
override suspend fun changeCustomDayStart(updateObject: Map<String, Any>): User? {
return process { apiService.changeCustomDayStart(updateObject) }
}
override suspend fun markTaskNeedsWork(taskID: String, userID: String): Task? {
return process { apiService.markTaskNeedsWork(taskID, userID) }
}
override suspend fun getMember(memberId: String) = processResponse(apiService.getMember(memberId))
override suspend fun getMemberWithUsername(username: String) = processResponse(apiService.getMemberWithUsername(username))

View file

@ -147,6 +147,16 @@ class TaskRepositoryImpl(
bgTask.counterDown = (bgTask.counterDown ?: 0) + 1
}
}
if (bgTask.isGroupTask) {
val entry = bgTask.group?.assignedUsersDetail?.firstOrNull { it.assignedUserID == user.id }
entry?.completed = up
if (up) {
entry?.completedDate = Date()
} else {
entry?.completedDate = null
}
}
}
res._tmp?.drop?.key?.let { key ->
val type = when (res._tmp?.drop?.type?.lowercase(Locale.US)) {
@ -183,6 +193,14 @@ class TaskRepositoryImpl(
}
}
override suspend fun markTaskNeedsWork(task: Task, userID: String) {
val savedTask = apiClient.markTaskNeedsWork(task.id ?: "", userID)
if (savedTask != null) {
savedTask.position = task.position
localRepository.save(savedTask)
}
}
override suspend fun taskChecked(
user: User?,
taskId: String,
@ -319,7 +337,7 @@ class TaskRepositoryImpl(
val savedTask = apiClient.assignToTask(taskID, assignments) ?: return@let
savedTask.id = task.id
savedTask.position = task.position
localRepository.save(task)
localRepository.save(savedTask)
}
assignChanges["unassign"]?.let { unassignments ->
@ -330,7 +348,7 @@ class TaskRepositoryImpl(
if (savedTask != null) {
savedTask.id = task.id
savedTask.position = task.position
localRepository.save(task)
localRepository.save(savedTask)
}
}
}

View file

@ -1,7 +1,7 @@
package com.habitrpg.android.habitica.helpers
object PurchaseTypes {
const val JubilantGrphatrice = "com.habitrpg.android.habitica.iap.gryphatrice"
const val JubilantGrphatrice = "com.habitrpg.android.habitica.iap.pets.gryphatrice_jubilant"
const val Purchase4Gems = "com.habitrpg.android.habitica.iap.4gems"
const val Purchase21Gems = "com.habitrpg.android.habitica.iap.21gems"
const val Purchase42Gems = "com.habitrpg.android.habitica.iap.42gems"

View file

@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.models.social
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.user.SubscriptionPlan
import com.habitrpg.shared.habitica.models.tasks.TasksOrder
import io.realm.RealmList
import io.realm.RealmObject
@ -11,6 +12,10 @@ import io.realm.annotations.PrimaryKey
open class Group : RealmObject(), BaseMainObject {
val isGroupPlan: Boolean
get() {
return purchased?.isActive == true
}
override val realmClass: Class<Group>
get() = Group::class.java
override val primaryIdentifier: String?
@ -38,6 +43,7 @@ open class Group : RealmObject(), BaseMainObject {
var leaderOnlyChallenges: Boolean = false
var leaderOnlyGetGems: Boolean = false
var categories: RealmList<GroupCategory>? = null
var purchased: SubscriptionPlan? = null
@Ignore
var tasksOrder: TasksOrder? = null

View file

@ -18,6 +18,7 @@ open class SubscriptionPlan : RealmObject(), BaseObject {
@JvmField
var planId: String? = null
var active: Boolean? = null
var gemsBought: Int? = null
var extraMonths: Int? = null
var quantity: Int? = null
@ -34,7 +35,7 @@ open class SubscriptionPlan : RealmObject(), BaseObject {
val isActive: Boolean
get() {
val today = Date()
return customerId != null && (dateTerminated == null || dateTerminated!!.after(today))
return customerId != null && (dateTerminated == null || dateTerminated!!.after(today) || active == true)
}
val totalNumberOfGems: Int

View file

@ -19,10 +19,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -59,7 +59,10 @@ import com.habitrpg.android.habitica.helpers.launchCatching
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.CurrencyText
import com.habitrpg.android.habitica.ui.views.PixelArtView
import com.habitrpg.common.habitica.extensions.DataBindingUtils
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.map
import java.text.SimpleDateFormat
import java.util.Date
@ -68,13 +71,17 @@ import javax.inject.Inject
class BirthdayActivity : BaseActivity() {
@Inject
lateinit var userViewModel: MainUserViewModel
@Inject
lateinit var purchaseHandler: PurchaseHandler
@Inject
lateinit var inventoryRepository: InventoryRepository
@Inject
lateinit var configManager: AppConfigManager
private val isPurchasing = mutableStateOf(false)
private val price = mutableStateOf("")
private val hasGryphatrice = mutableStateOf(false)
private var gryphatriceProductDetails: ProductDetails? = null
@ -86,20 +93,33 @@ class BirthdayActivity : BaseActivity() {
val event = configManager.getBirthdayEvent()
setContent {
HabiticaTheme {
val user = userViewModel.user.observeAsState()
BirthdayActivityView(hasGryphatrice.value, price.value, event?.start ?: Date(), event?.end ?: Date(), {
gryphatriceProductDetails?.let {
purchaseHandler.purchase(this, it)
}
}, {
lifecycleScope.launchCatching {
inventoryRepository.purchaseItem("", "Gryphatrice-Jubilant", 1)
}
}, {
lifecycleScope.launchCatching {
inventoryRepository.equip("pets", "Gryphatrice-Jubilant")
}
})
BirthdayActivityView(
isPurchasing.value,
hasGryphatrice.value,
price.value,
event?.start ?: Date(),
event?.end ?: Date(),
{
gryphatriceProductDetails?.let {
isPurchasing.value = true
purchaseHandler.purchase(this, it)
}
},
{
lifecycleScope.launchCatching({
isPurchasing.value = false
}) {
isPurchasing.value = true
inventoryRepository.purchaseItem("pets", "Gryphatrice-Jubilant", 1)
userRepository.retrieveUser(false, true)
isPurchasing.value = false
}
},
{
lifecycleScope.launchCatching {
inventoryRepository.equip("pet", "Gryphatrice-Jubilant")
}
})
}
}
@ -112,6 +132,11 @@ class BirthdayActivity : BaseActivity() {
hasGryphatrice.value = (it?.trained ?: 0) >= 5
}
}
lifecycleScope.launchCatching {
gryphatriceProductDetails = purchaseHandler.getGryphatriceSKU()
price.value = gryphatriceProductDetails?.oneTimePurchaseOfferDetails?.formattedPrice ?: ""
}
}
override fun injectActivity(component: UserComponent?) {
@ -150,9 +175,17 @@ fun BirthdayTitle(text: String) {
}
}
@Composable
fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date, endDate: Date, onPurchaseClick: () -> Unit, onGemPurchaseClick: () -> Unit, onEquipClick: () -> Unit) {
fun BirthdayActivityView(
isPurchasing: Boolean,
hasGryphatrice: Boolean,
price: String,
startDate: Date,
endDate: Date,
onPurchaseClick: () -> Unit,
onGemPurchaseClick: () -> Unit,
onEquipClick: () -> Unit
) {
val activity = LocalContext.current as? Activity
val dateFormat = SimpleDateFormat("MMM dd", java.util.Locale.getDefault())
val textColor = Color.White
@ -192,8 +225,13 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth()) {
Image(painterResource(R.drawable.birthday_header), null, Modifier.padding(bottom = 8.dp))
.fillMaxWidth()
) {
Image(
painterResource(R.drawable.birthday_header),
null,
Modifier.padding(bottom = 8.dp)
)
Row(verticalAlignment = Alignment.CenterVertically) {
Image(painterResource(R.drawable.birthday_gifts), null)
Column(
@ -207,7 +245,11 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
fontWeight = FontWeight.Bold
)
Text(
stringResource(R.string.x_to_y, dateFormat.format(startDate), dateFormat.format(endDate)),
stringResource(
R.string.x_to_y,
dateFormat.format(startDate),
dateFormat.format(endDate)
),
fontSize = 12.sp,
color = textColor,
fontWeight = FontWeight.Bold
@ -266,6 +308,8 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
) {
Text(stringResource(R.string.equip))
}
} else if (isPurchasing) {
CircularProgressIndicator()
} else {
Text(buildAnnotatedString {
append("Buy for ")
@ -281,7 +325,7 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
Color.White,
colorResource(R.color.brand_200),
{
onPurchaseClick()
onPurchaseClick()
},
modifier = Modifier.padding(top = 20.dp)
) {
@ -291,7 +335,7 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
Color.White,
colorResource(R.color.brand_200),
{
onGemPurchaseClick()
onGemPurchaseClick()
},
modifier = Modifier.padding(top = 20.dp)
) {
@ -314,7 +358,11 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
Color.White,
colorResource(R.color.brand_200),
{
onEquipClick()
MainScope().launchCatching {
activity?.finish()
delay(500)
MainNavigationController.navigate(R.id.marketFragment)
}
},
modifier = Modifier.padding(top = 20.dp)
) {
@ -328,6 +376,42 @@ fun BirthdayActivityView(hasGryphatrice: Boolean, price: String, startDate: Date
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
Column(verticalArrangement = Arrangement.spacedBy(14.dp), modifier = Modifier.padding(vertical = 20.dp)) {
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 1,
title = stringResource(R.string.a_party_robe),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 1,
title = stringResource(R.string.twenty_gems),
imageName = "",
modifier = Modifier.weight(1f)
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth()
) {
FourFreeItem(
day = 5,
title = stringResource(R.string.birthday_set),
imageName = "",
modifier = Modifier.weight(1f)
)
FourFreeItem(
day = 10,
title = stringResource(R.string.background),
imageName = "",
modifier = Modifier.weight(1f)
)
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@ -369,15 +453,24 @@ fun PotionGrid() {
"Peppermint",
"Shimmer"
).windowed(4, 4, true)
Column(verticalArrangement = Arrangement.spacedBy(8.dp), horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(top = 20.dp)) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(top = 20.dp)
) {
for (potionGroup in potions) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
for (potion in potionGroup) {
Box(
Modifier
.size(68.dp)
.background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))) {
AsyncImage(model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename("Pet_HatchingPotion_$potion"), null, Modifier.size(68.dp))
.background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))
) {
AsyncImage(
model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename(
"Pet_HatchingPotion_$potion"
), null, Modifier.size(68.dp)
)
}
}
}
@ -385,6 +478,34 @@ fun PotionGrid() {
}
}
@Composable
fun FourFreeItem(
day: Int,
title: String,
imageName: String,
modifier: Modifier = Modifier
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(18.dp),
modifier = modifier
.background(colorResource(R.color.brand_50), HabiticaTheme.shapes.medium)
.padding(16.dp)
) {
Text(
stringResource(R.string.day_x, day).uppercase(),
color = colorResource(R.color.yellow_50),
fontSize = 12.sp,
fontWeight = FontWeight.Bold
)
PixelArtView(imageName,
Modifier
.size(121.dp, 84.dp)
.background(colorResource(R.color.brand_100), HabiticaTheme.shapes.medium))
Text(title, color = Color.White, fontSize = 16.sp)
}
}
@Composable
fun HabiticaButton(
background: Color,
@ -413,7 +534,7 @@ fun HabiticaButton(
@Preview(device = Devices.PIXEL_4)
@Composable
private fun Preview() {
BirthdayActivityView(false, "", Date(), Date(), {
BirthdayActivityView(true, false, "", Date(), Date(), {
}, {}, {})
}

View file

@ -19,7 +19,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.CheckBox
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.widget.AppCompatCheckBox
import androidx.compose.runtime.mutableStateListOf
@ -236,6 +235,12 @@ class TaskFormActivity : BaseActivity() {
{
showAssignDialog()
},
{
taskCompletedMap.remove(it)
lifecycleScope.launchCatching {
task?.let { it1 -> taskRepository.markTaskNeedsWork(it1, it) }
}
},
showEditButton = true
)
}

View file

@ -100,7 +100,7 @@ class RewardsRecyclerViewAdapter(
}
override fun getItemViewType(position: Int): Int {
return if (customRewards != null && position < customRewardCount) {
return if ((customRewards != null && position < customRewardCount) || (customRewardCount == 0 && inAppRewardCount == 0)) {
VIEWTYPE_CUSTOM_REWARD
} else {
VIEWTYPE_IN_APP_REWARD
@ -139,6 +139,6 @@ class RewardsRecyclerViewAdapter(
companion object {
private const val VIEWTYPE_CUSTOM_REWARD = 0
private const val VIEWTYPE_IN_APP_REWARD = 2
private const val VIEWTYPE_IN_APP_REWARD = 3
}
}

View file

@ -104,7 +104,7 @@ class NavigationDrawerFragment : DialogFragment() {
val context = context
adapter = if (context != null) {
NavigationDrawerAdapter(
context.getThemeColor(R.attr.colorPrimary),
context.getThemeColor(R.attr.colorPrimaryText),
context.getThemeColor(R.attr.colorPrimaryOffset)
)
} else {

View file

@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.habitrpg.android.habitica.MainNavDirections
@ -32,7 +31,7 @@ import javax.inject.Inject
import kotlin.time.DurationUnit
import kotlin.time.toDuration
class ChatFragment() : BaseFragment<FragmentChatBinding>() {
class ChatFragment : BaseFragment<FragmentChatBinding>() {
override var binding: FragmentChatBinding? = null
@ -40,9 +39,7 @@ class ChatFragment() : BaseFragment<FragmentChatBinding>() {
return FragmentChatBinding.inflate(inflater, container, false)
}
val viewModel: GroupViewModel by viewModels(
ownerProducer = { requireParentFragment() }
)
lateinit var viewModel: GroupViewModel
@Inject
lateinit var configManager: AppConfigManager

View file

@ -91,7 +91,9 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
TavernDetailFragment()
}
1 -> {
ChatFragment()
val fragment = ChatFragment()
fragment.viewModel = viewModel
fragment
}
else -> Fragment()
}

View file

@ -8,6 +8,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.MainNavDirections
import com.habitrpg.android.habitica.R
@ -197,6 +198,8 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
binding?.guildBankText?.text = guild?.gemCount.toString()
binding?.guildSummary?.setMarkdown(guild?.summary)
binding?.guildDescription?.setMarkdown(guild?.description)
binding?.inviteButton?.isVisible = guild?.isGroupPlan == true
}
companion object {

View file

@ -154,6 +154,7 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
}
1 -> {
chatFragment = ChatFragment()
chatFragment?.viewModel = viewModel
fragment = chatFragment
}
else -> fragment = Fragment()

View file

@ -196,6 +196,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
}
1 -> {
chatFragment = ChatFragment()
chatFragment?.viewModel = viewModel
chatFragment
}
else -> Fragment()

View file

@ -45,7 +45,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
(layoutManager as? GridLayoutManager)?.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if ((recyclerAdapter?.getItemViewType(position) ?: 0) < 2) {
return if ((recyclerAdapter?.getItemViewType(position) ?: 0) < 3) {
(layoutManager as? GridLayoutManager)?.spanCount ?: 1
} else {
1

View file

@ -207,7 +207,7 @@ abstract class ChecklistedViewHolder(
override fun onLeftActionTouched() {
super.onLeftActionTouched()
if (task?.isValid == true) {
if (task?.isValid == true && !isLocked) {
onCheckedChanged(!(task?.completed(userID) ?: false))
}
}

View file

@ -137,12 +137,16 @@ class HabitViewHolder(
override fun onLeftActionTouched() {
super.onLeftActionTouched()
onPlusButtonClicked()
if (!isLocked) {
onPlusButtonClicked()
}
}
override fun onRightActionTouched() {
super.onRightActionTouched()
onMinusButtonClicked()
if (!isLocked) {
onMinusButtonClicked()
}
}
private fun onPlusButtonClicked() {

View file

@ -202,10 +202,13 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
}
fun likeMessage(message: ChatMessage) {
val index = _chatMessages.value?.indexOf(message)
if (index == null || index < 0) return
viewModelScope.launchCatching {
val message = socialRepository.likeMessage(message)
val index = _chatMessages.value?.indexOfFirst { it.id == message?.id }
if (index == null || index < 0) {
retrieveGroupChat { }
return@launchCatching
}
val list = _chatMessages.value?.toMutableList()
if (message != null) {
list?.set(index, message)
@ -246,7 +249,10 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
}
fun retrieveGroupChat(onComplete: () -> Unit) {
val groupID = groupID
var groupID = groupID
if (groupViewType == GroupViewType.PARTY) {
groupID = "party"
}
if (groupID.isNullOrEmpty()) {
onComplete()
return

View file

@ -26,6 +26,7 @@ fun UserRow(
username: String,
avatar: Avatar?,
modifier: Modifier = Modifier,
mainContentModifier: Modifier = Modifier,
extraContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null,
color: Color? = null
@ -46,7 +47,7 @@ fun UserRow(
}
}
Column {
Column(mainContentModifier) {
Text(
"@$username",
fontSize = 16.sp,

View file

@ -3,23 +3,31 @@ package com.habitrpg.android.habitica.ui.views.tasks
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.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
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.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.models.Assignable
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.views.CompletedAt
import com.habitrpg.android.habitica.ui.views.UserRow
import java.util.Date
@ -31,6 +39,7 @@ fun AssignedView(
backgroundColor: Color,
color: Color,
onEditClick: () -> Unit,
onUndoClick: (String) -> Unit,
modifier: Modifier = Modifier,
showEditButton: Boolean = false
) {
@ -41,14 +50,18 @@ fun AssignedView(
backgroundColor,
MaterialTheme.shapes.medium
)
.padding(15.dp, 12.dp)
.heightIn(min = 24.dp)
.heightIn(min = 66.dp)
.padding(start = 16.dp)
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
for (assignable in assigned) {
UserRow(
username = assignable.identifiableName,
avatar = assignable.avatar,
modifier = rowModifier,
mainContentModifier = Modifier
.padding(vertical = 12.dp)
.heightIn(min = 24.dp),
color = color,
extraContent = {
completedAt[assignable.id]?.let { CompletedAt(completedAt = it) }
@ -56,7 +69,9 @@ fun AssignedView(
endContent = {
completedAt[assignable.id]?.let {
if (showEditButton) {
UndoTaskCompletion()
UndoTaskCompletion(Modifier.clickable {
assignable.id?.let { it1 -> onUndoClick(it1) }
})
}
}
}
@ -90,5 +105,28 @@ fun AssignedView(
}
@Composable
fun UndoTaskCompletion() {
fun UndoTaskCompletion(modifier: Modifier = Modifier) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = modifier
.width(51.dp)
.heightIn(min = 66.dp)
.fillMaxHeight()
.background(HabiticaTheme.colors.contentBackgroundOffset)
) {
Image(
painterResource(R.drawable.checkmark),
null,
contentScale = ContentScale.None,
modifier = Modifier
.size(24.dp)
.background(HabiticaTheme.colors.windowBackground, HabiticaTheme.shapes.small)
)
Text(
stringResource(R.string.undo),
fontSize = 12.sp,
color = HabiticaTheme.colors.textSecondary
)
}
}

View file

@ -1,2 +1,2 @@
NAME=4.1
CODE=4991
CODE=5031