diff --git a/Habitica/res/layout/fragment_subscription.xml b/Habitica/res/layout/fragment_subscription.xml
index 951b73876..d2d47c673 100644
--- a/Habitica/res/layout/fragment_subscription.xml
+++ b/Habitica/res/layout/fragment_subscription.xml
@@ -17,6 +17,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
+
@POST("user/mark-pms-read")
- suspend fun markPrivateMessagesRead(): Void
+ suspend fun markPrivateMessagesRead(): Void?
/* Group API */
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
index 3b1c7e948..2f6060b29 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
@@ -131,7 +131,7 @@ interface ApiClient {
suspend fun disableClasses(): User?
- suspend fun markPrivateMessagesRead(): Void?
+ suspend fun markPrivateMessagesRead()
/* Group API */
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 bf5e37ecf..af0b2674f 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
@@ -18,7 +18,7 @@ interface SocialRepository : BaseRepository {
fun getUserGroups(type: String?): Flow>
suspend fun retrieveGroupChat(groupId: String): List?
- fun getGroupChat(groupId: String): Flow>
+ fun getGroupChat(groupId: String): Flow>
suspend fun markMessagesSeen(seenGroupId: String)
@@ -92,7 +92,7 @@ interface SocialRepository : BaseRepository {
id: String? = null
): List?
- suspend fun markPrivateMessagesRead(user: User?): Void?
+ suspend fun markPrivateMessagesRead(user: User?)
fun markSomePrivateMessagesAsRead(user: User?, messages: List)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
index 3bce8ab70..446bf4c41 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
@@ -499,8 +499,8 @@ class ApiClientImpl(
override suspend fun disableClasses(): User? = process { apiService.disableClasses() }
- override suspend fun markPrivateMessagesRead(): Void {
- return apiService.markPrivateMessagesRead()
+ override suspend fun markPrivateMessagesRead() {
+ apiService.markPrivateMessagesRead()
}
override suspend fun listGroups(type: String): List? {
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 75933a88e..df5c4589b 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
@@ -274,7 +274,7 @@ class SocialRepositoryImpl(
return apiClient.findUsernames(username, context, id)
}
- override suspend fun markPrivateMessagesRead(user: User?): Void? {
+ override suspend fun markPrivateMessagesRead(user: User?) {
if (user?.isManaged == true) {
localRepository.modify(user) {
it.inbox?.hasUserSeenInbox = true
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 6aeae16c6..cfbad3f3d 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
@@ -20,9 +20,11 @@ import com.habitrpg.android.habitica.models.user.UserQuestStatus
import com.habitrpg.android.habitica.proxy.AnalyticsManager
import com.habitrpg.shared.habitica.models.responses.TaskDirection
import com.habitrpg.shared.habitica.models.tasks.Attribute
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
import java.util.Date
import java.util.GregorianCalendar
import java.util.concurrent.TimeUnit
@@ -65,7 +67,9 @@ class UserRepositoryImpl(
if (forced || this.lastSync == null || Date().time - (this.lastSync?.time ?: 0) > 180000) {
val user = apiClient.retrieveUser(withTasks) ?: return null
lastSync = Date()
- localRepository.saveUser(user)
+ withContext(Dispatchers.Main) {
+ localRepository.saveUser(user)
+ }
if (withTasks) {
val id = user.id
val tasksOrder = user.tasksOrder
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
index 7c8b3b0e3..76d056f4d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
@@ -426,11 +426,17 @@ class PurchaseHandler(
}
}
+ private val displayedConfirmations = mutableListOf()
+
private fun displayConfirmationDialog(purchase: Purchase, giftedTo: String? = null) {
- CoroutineScope(Dispatchers.Main).launch(ExceptionHandler.coroutine()) {
+ if (displayedConfirmations.contains(purchase.orderId)) {
+ return
+ }
+ displayedConfirmations.add(purchase.orderId)
+ CoroutineScope(Dispatchers.Main).launchCatching {
val application = (context as? HabiticaBaseApplication)
- ?: (context.applicationContext as? HabiticaBaseApplication) ?: return@launch
- val sku = purchase.products.firstOrNull() ?: return@launch
+ ?: (context.applicationContext as? HabiticaBaseApplication) ?: return@launchCatching
+ val sku = purchase.products.firstOrNull() ?: return@launchCatching
var title = context.getString(R.string.successful_purchase_generic)
val message = when {
PurchaseTypes.allSubscriptionNoRenewTypes.contains(sku) -> {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/GemsPurchaseFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/GemsPurchaseFragment.kt
index 118eb8552..9cb17712b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/GemsPurchaseFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/GemsPurchaseFragment.kt
@@ -6,6 +6,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.android.billingclient.api.ProductDetails
@@ -26,7 +31,9 @@ import com.habitrpg.android.habitica.ui.activities.GiftGemsActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
+import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
+import com.habitrpg.android.habitica.ui.views.promo.BirthdayBanner
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -92,6 +99,17 @@ class GemsPurchaseFragment : BaseFragment() {
binding?.promoBanner?.visibility = View.GONE
}
+ val birthdayEventEnd = appConfigManager.getBirthdayEvent()?.end
+ if (birthdayEventEnd != null) {
+ binding?.promoComposeView?.setContent {
+ HabiticaTheme {
+ BirthdayBanner(endDate = birthdayEventEnd, Modifier.padding(horizontal = 20.dp).clip(HabiticaTheme.shapes.medium)
+ .padding(bottom = 20.dp))
+ }
+ }
+ binding?.promoComposeView?.isVisible = true
+ }
+
AmplitudeManager.sendNavigationEvent("gem screen")
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
index d9f6b9820..1665a76c0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
@@ -7,6 +7,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.android.billingclient.api.ProductDetails
@@ -26,7 +31,9 @@ import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.GiftSubscriptionActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment
+import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
+import com.habitrpg.android.habitica.ui.views.promo.BirthdayBanner
import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionView
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import com.habitrpg.common.habitica.extensions.layoutInflater
@@ -87,6 +94,17 @@ class SubscriptionFragment : BaseFragment() {
binding?.promoBanner?.visibility = View.GONE
}
+ val birthdayEventEnd = appConfigManager.getBirthdayEvent()?.end
+ if (birthdayEventEnd != null) {
+ binding?.promoComposeView?.setContent {
+ HabiticaTheme {
+ BirthdayBanner(endDate = birthdayEventEnd, Modifier.padding(horizontal = 20.dp).clip(HabiticaTheme.shapes.medium)
+ .padding(bottom = 10.dp))
+ }
+ }
+ binding?.promoComposeView?.isVisible = true
+ }
+
binding?.refreshLayout?.setOnRefreshListener { refresh() }
lifecycleScope.launchCatching {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/promo/BirthdayMenuView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/promo/BirthdayMenuView.kt
index d4ba11603..a7da9773f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/promo/BirthdayMenuView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/promo/BirthdayMenuView.kt
@@ -33,7 +33,7 @@ import com.habitrpg.android.habitica.helpers.MainNavigationController
import java.util.Date
@Composable
-fun BirthdayBanner(endDate: Date) {
+fun BirthdayBanner(endDate: Date, modifier: Modifier = Modifier) {
var value by remember { mutableStateOf(0) }
DisposableEffect(Unit) {
@@ -50,7 +50,7 @@ fun BirthdayBanner(endDate: Date) {
}
}
Column(
- Modifier
+ modifier
.fillMaxWidth()
.clickable {
MainNavigationController.navigate(R.id.birthdayActivity)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
index c49a79b95..cedab5f2b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
@@ -9,7 +9,6 @@ import com.habitrpg.android.habitica.extensions.fromHtml
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.common.habitica.extensions.dpToPx
-import com.habitrpg.common.habitica.extensions.loadGif
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.views.PixelArtView
@@ -29,7 +28,7 @@ abstract class PurchaseDialogContent @JvmOverloads constructor(
open fun setItem(item: ShopItem) {
if (item.path?.contains("timeTravelBackgrounds") == true) {
- imageView.loadGif(item.imageName?.replace("icon_", ""))
+ imageView.loadImage(item.imageName?.replace("icon_", ""))
val params = imageView.layoutParams
params.height = 147.dpToPx(context)
params.width = 140.dpToPx(context)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignedView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignedView.kt
index 18ca09270..839c3cc4f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignedView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignedView.kt
@@ -52,6 +52,13 @@ fun AssignedView(
color = color,
extraContent = {
completedAt[assignable.id]?.let { CompletedAt(completedAt = it) }
+ },
+ endContent = {
+ completedAt[assignable.id]?.let {
+ if (showEditButton) {
+ UndoTaskCompletion()
+ }
+ }
}
)
}
@@ -80,4 +87,8 @@ fun AssignedView(
}
}
}
-}
\ No newline at end of file
+}
+
+@Composable
+fun UndoTaskCompletion() {
+}
diff --git a/common/src/main/java/com/habitrpg/common/habitica/extensions/Application-Extensions.kt b/common/src/main/java/com/habitrpg/common/habitica/extensions/Application-Extensions.kt
index 363d913e9..e3101625c 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/extensions/Application-Extensions.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/extensions/Application-Extensions.kt
@@ -12,6 +12,7 @@ import com.habitrpg.common.habitica.BuildConfig
fun Application.setupCoil() {
var builder = ImageLoader.Builder(this)
.allowHardware(false)
+ .crossfade(false)
.components {
if (Build.VERSION.SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
diff --git a/common/src/main/java/com/habitrpg/common/habitica/extensions/DataBindingUtils.kt b/common/src/main/java/com/habitrpg/common/habitica/extensions/DataBindingUtils.kt
index 1a78961a1..a75c17c87 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/extensions/DataBindingUtils.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/extensions/DataBindingUtils.kt
@@ -2,6 +2,7 @@ package com.habitrpg.common.habitica.extensions
import android.content.Context
import android.graphics.PorterDuff
+import android.graphics.drawable.Animatable
import android.graphics.drawable.Drawable
import android.view.View
import android.view.animation.Animation
@@ -13,7 +14,6 @@ import coil.imageLoader
import coil.request.ImageRequest
import com.habitrpg.android.habitica.extensions.setTintWith
import com.habitrpg.common.habitica.R
-import com.habitrpg.common.habitica.extensions.DataBindingUtils.BASE_IMAGE_URL
import com.habitrpg.common.habitica.helpers.AppConfigManager
import com.habitrpg.common.habitica.views.PixelArtView
import java.util.Collections
@@ -26,30 +26,22 @@ fun PixelArtView.loadImage(imageName: String?, imageFormat: String? = null) {
return
}
tag = fullname
- bitmap = null
+ setImageDrawable(null)
DataBindingUtils.loadImage(context, imageName, imageFormat) {
if (tag == fullname) {
- bitmap = it.toBitmap()
+ if (fullname.endsWith("gif")) {
+ setImageDrawable(it)
+ if (it is Animatable) {
+ it.start()
+ }
+ } else {
+ bitmap = it.toBitmap()
+ }
}
}
}
}
-fun PixelArtView.loadGif(
- imageName: String?,
- builder: ImageRequest.Builder.() -> Unit = {}
-) {
- if (imageName != null) {
- val fullname = BASE_IMAGE_URL + DataBindingUtils.getFullFilename(imageName)
- val request = ImageRequest.Builder(context)
- .data(fullname)
- .target(this)
- .apply(builder)
- .build()
- context.imageLoader.enqueue(request)
- }
-}
-
object DataBindingUtils {
fun loadImage(context: Context, imageName: String, imageResult: (Drawable) -> Unit) {
diff --git a/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt b/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
index 4657863e8..69df63be5 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
@@ -7,6 +7,7 @@ import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
+import android.graphics.drawable.Animatable
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.AttributeSet
@@ -170,6 +171,9 @@ class AvatarView : FrameLayout {
result.isFilterBitmap = false
super.onSuccess(result)
imageView.setImageDrawable(result)
+ if (result is Animatable) {
+ result.start()
+ }
val bounds = getLayerBounds(layerKey, layerName, result)
imageView.imageMatrix = avatarMatrix
val layoutParams = imageView.layoutParams as? LayoutParams
@@ -288,7 +292,7 @@ class AvatarView : FrameLayout {
val hair = prefs.hair
if (!hasVisualBuffs) {
- if (!TextUtils.isEmpty(prefs.chair)) {
+ if (prefs.chair?.isNotBlank() == true && prefs.chair != "chair_none") {
layerMap[LayerType.CHAIR] = prefs.chair
}