fix final bugs

This commit is contained in:
Phillip Thelen 2023-01-18 14:11:33 +01:00
parent 61bfeb6862
commit 1724c8e034
19 changed files with 139 additions and 51 deletions

View file

@ -9,7 +9,7 @@
<item android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/white" />
<solid android:color="?colorTintedBackground" />
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="2dp" />
</shape>
</item>

View file

@ -177,7 +177,7 @@
android:id="@+id/habit_adjust_positive_input_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/colorTintedBackgroundOffset"
android:background="@drawable/task_form_control_bg"
android:hint="@string/positive_habit_form"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatEditText
@ -194,7 +194,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/negative_habit_form"
app:boxBackgroundColor="?attr/colorTintedBackgroundOffset"
android:background="@drawable/task_form_control_bg"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/habit_adjust_negative_streak_view"

View file

@ -9,7 +9,7 @@
android:paddingEnd="20dp"
android:paddingBottom="10dp">
<ImageView
<com.habitrpg.common.habitica.views.PixelArtView
android:id="@+id/notification_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -35,8 +35,12 @@
android:text="@string/pending_approval"
android:textColor="@color/text_ternary"
android:drawablePadding="@dimen/spacing_small"
android:padding="2dp"
android:drawableStart="@drawable/completed"
android:drawableTint="@color/text_ternary"
android:paddingVertical="1dp"
android:paddingHorizontal="6dp"
android:background="@drawable/pill_bg_gray"
android:layout_marginStart="10dp"
/>
</LinearLayout>
<com.habitrpg.android.habitica.ui.views.EllipsisTextView

View file

@ -198,7 +198,10 @@ class TaskRepositoryImpl(
if (savedTask != null) {
savedTask.id = task.id
savedTask.position = task.position
savedTask.group?.assignedUsersDetail?.firstOrNull { it.assignedUserID == userID }?.completed = false
savedTask.group?.assignedUsersDetail?.firstOrNull { it.assignedUserID == userID }?.let {
it.completed = false
it.completedDate = null
}
localRepository.save(savedTask)
}
}

View file

@ -38,7 +38,11 @@ class UserRepositoryImpl(
private val analyticsManager: AnalyticsManager
) : BaseRepositoryImpl<UserLocalRepository>(localRepository, apiClient, userID), UserRepository {
private var lastSync: Date? = null
companion object {
private var lastReadNotification: String? = null
private var lastSync: Date? = null
}
override fun getUser(): Flow<User?> = getUser(userID)
override fun getUser(userID: String): Flow<User?> = localRepository.getUser(userID)
@ -64,7 +68,7 @@ class UserRepositoryImpl(
@Suppress("ReturnCount")
override suspend fun retrieveUser(withTasks: Boolean, forced: Boolean, overrideExisting: Boolean): User? {
// Only retrieve again after 3 minutes or it's forced.
if (forced || this.lastSync == null || Date().time - (this.lastSync?.time ?: 0) > 180000) {
if (forced || lastSync == null || Date().time - (lastSync?.time ?: 0) > 180000) {
val user = apiClient.retrieveUser(withTasks) ?: return null
lastSync = Date()
withContext(Dispatchers.Main) {
@ -154,11 +158,12 @@ class UserRepositoryImpl(
override suspend fun unlockPath(path: String, price: Int): UnlockResponse? {
val unlockResponse = apiClient.unlockPath(path) ?: return null
val user = localRepository.getUser(userID).firstOrNull() ?: return unlockResponse
user.preferences = unlockResponse.preferences
user.purchased = unlockResponse.purchased
user.items = unlockResponse.items
user.balance = user.balance - (price / 4.0)
localRepository.saveUser(user, false)
localRepository.modify(user) { liveUser ->
liveUser.preferences = unlockResponse.preferences
liveUser.purchased = unlockResponse.purchased
liveUser.items = unlockResponse.items
liveUser.balance = liveUser.balance - (price / 4.0)
}
return unlockResponse
}
@ -166,7 +171,6 @@ class UserRepositoryImpl(
runCron(ArrayList())
}
private var lastReadNotification: String? = null
override suspend fun readNotification(id: String): List<Any>? {
if (lastReadNotification == id) return null
lastReadNotification = id

View file

@ -14,6 +14,7 @@ import com.habitrpg.android.habitica.models.promotions.HabiticaWebPromotion
import com.habitrpg.android.habitica.models.promotions.getHabiticaPromotionFromKey
import com.habitrpg.common.habitica.helpers.AppTestingLevel
import kotlinx.coroutines.MainScope
import java.util.Date
class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.common.habitica.helpers.AppConfigManager() {
@ -169,6 +170,6 @@ class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.comm
fun getBirthdayEvent(): WorldStateEvent? {
val events = ((worldState?.events as? List<WorldStateEvent>) ?: listOf(worldState?.currentEvent))
return events.firstOrNull { it?.eventKey == "birthday10" }
return events.firstOrNull { it?.eventKey == "birthday10" && it.end?.after(Date()) == true }
}
}

View file

@ -24,16 +24,19 @@ import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.helpers.launchCatching
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.models.Notification
import com.habitrpg.common.habitica.models.notifications.GroupTaskApprovedData
import com.habitrpg.common.habitica.models.notifications.GroupTaskNeedsWorkData
import com.habitrpg.common.habitica.models.notifications.GroupTaskRequiresApprovalData
import com.habitrpg.common.habitica.models.notifications.GuildInvitationData
import com.habitrpg.common.habitica.models.notifications.ItemReceivedData
import com.habitrpg.common.habitica.models.notifications.NewChatMessageData
import com.habitrpg.common.habitica.models.notifications.NewStuffData
import com.habitrpg.common.habitica.models.notifications.PartyInvitationData
import com.habitrpg.common.habitica.models.notifications.QuestInvitationData
import com.habitrpg.common.habitica.models.notifications.UnallocatedPointsData
import com.habitrpg.common.habitica.views.PixelArtView
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -134,6 +137,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
Notification.Type.PARTY_INVITATION.type -> createPartyInvitationNotification(it)
Notification.Type.GUILD_INVITATION.type -> createGuildInvitationNotification(it)
Notification.Type.QUEST_INVITATION.type -> createQuestInvitationNotification(it)
Notification.Type.ITEM_RECEIVED.type -> createItemReceivedNotification(it)
else -> null
}
@ -165,6 +169,15 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
)
}
private fun createItemReceivedNotification(notification: Notification): View? {
val data = notification.data as? ItemReceivedData
return createDismissableNotificationItem(
notification,
fromHtml("<b>" + data?.title + "</b><br>" + data?.text),
imageName = data?.icon
)
}
private fun createNewStuffNotification(notification: Notification): View? {
val data = notification.data as? NewStuffData
val text = if (data?.title != null) {
@ -206,7 +219,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notification,
fromHtml(message),
null,
R.color.yellow_5
textColor = R.color.yellow_5
)
}
@ -218,7 +231,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notification,
fromHtml(message),
null,
R.color.green_10
textColor = R.color.green_10
)
}
@ -252,6 +265,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notification: Notification,
messageText: CharSequence,
imageResourceId: Int? = null,
imageName: String? = null,
textColor: Int? = null
): View? {
val item = inflater?.inflate(R.layout.notification_item, binding.notificationItems, false)
@ -276,6 +290,12 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notificationImage?.visibility = View.VISIBLE
}
if (imageName != null) {
val notificationImage = item?.findViewById(R.id.notification_image) as? PixelArtView
notificationImage?.loadImage(imageName)
notificationImage?.visibility = View.VISIBLE
}
if (textColor != null) {
messageTextView?.setTextColor(ContextCompat.getColor(this, textColor))
}

View file

@ -110,7 +110,8 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
var lastSet = CustomizationSet()
val today = Date()
for (customization in newCustomizationList) {
val isUsable = customization.isUsable(ownedCustomizations.contains(customization.id))
val isOwned = ownedCustomizations.contains(customization.id)
val isUsable = customization.isUsable(isOwned)
if (customization.availableFrom != null || customization.availableUntil != null) {
if (((customization.availableFrom?.compareTo(today)
?: 0) > 0 || (customization.availableUntil?.compareTo(today)
@ -119,7 +120,7 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
continue
}
}
if ((customization.identifier?.contains("HabitversaryBash") == true || customization.identifier?.contains("birthday") == true) && !isUsable) {
if (customization.identifier?.contains("birthday_bash") == true && !isOwned) {
continue
}
if (customization.customizationSet != null && customization.customizationSet != lastSet.identifier) {

View file

@ -252,7 +252,7 @@ abstract class BaseTaskViewHolder constructor(
val completedCount = data.group?.assignedUsersDetail?.filter { it.completed }?.size ?: 0
if (completedCount > 0) {
completedCountTextView.text = "$completedCount / ${data?.group?.assignedUsersDetail?.size}"
completedCountTextView.text = "$completedCount/${data?.group?.assignedUsersDetail?.size}"
completedCountTextView.visibility = View.VISIBLE
} else {
completedCountTextView.visibility = View.GONE

View file

@ -16,6 +16,7 @@ import com.habitrpg.common.habitica.models.Notification
import com.habitrpg.common.habitica.models.notifications.GroupTaskRequiresApprovalData
import com.habitrpg.common.habitica.models.notifications.GuildInvitationData
import com.habitrpg.common.habitica.models.notifications.GuildInvite
import com.habitrpg.common.habitica.models.notifications.ItemReceivedData
import com.habitrpg.common.habitica.models.notifications.NewChatMessageData
import com.habitrpg.common.habitica.models.notifications.NewStuffData
import com.habitrpg.common.habitica.models.notifications.PartyInvitationData
@ -41,7 +42,8 @@ open class NotificationsViewModel : BaseViewModel() {
Notification.Type.NEW_MYSTERY_ITEMS.type,
Notification.Type.GROUP_TASK_NEEDS_WORK.type,
Notification.Type.GROUP_TASK_APPROVED.type,
Notification.Type.UNALLOCATED_STATS_POINTS.type
Notification.Type.UNALLOCATED_STATS_POINTS.type,
Notification.Type.ITEM_RECEIVED.type
)
private val actionableNotificationTypes = listOf(
@ -262,6 +264,19 @@ open class NotificationsViewModel : BaseViewModel() {
// Group tasks should go to Group tasks view if that is added to this app at some point
Notification.Type.GROUP_TASK_APPROVED.type -> navController.navigate(R.id.tasksFragment)
Notification.Type.GROUP_TASK_NEEDS_WORK.type -> navController.navigate(R.id.tasksFragment)
Notification.Type.ITEM_RECEIVED.type -> clickItemReceivedNotification(notification, navController)
}
}
private fun clickItemReceivedNotification(
notification: Notification,
navController: MainNavigationController
) {
val data = notification.data as? ItemReceivedData
when (data?.destination) {
"equipment" -> navController.navigate(R.id.equipmentOverviewFragment)
"customization" -> navController.navigate(R.id.avatarCustomizationFragment)
else -> navController.navigate(R.id.itemsFragment)
}
}

View file

@ -1,7 +1,5 @@
package com.habitrpg.android.habitica.ui.views.promo
import android.os.Handler
import android.os.Looper
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -17,7 +15,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -25,34 +23,30 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
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.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.getShortRemainingString
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.ui.views.PixelArtView
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import java.util.Date
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
@Composable
fun BirthdayBanner(endDate: Date, modifier: Modifier = Modifier) {
var value by remember { mutableStateOf(0) }
DisposableEffect(Unit) {
val handler = Handler(Looper.getMainLooper())
val runnable = {
value += 1
}
handler.postDelayed(runnable, 1000)
onDispose {
handler.removeCallbacks(runnable)
}
if (endDate.before(Date())) {
return
}
Column(
modifier
@ -114,11 +108,9 @@ fun BirthdayBanner(endDate: Date, modifier: Modifier = Modifier) {
.background(colorResource(R.color.brand_300))
.padding(horizontal = 10.dp)
) {
Text(
stringResource(
R.string.ends_in_x,
endDate.getShortRemainingString()
).uppercase(),
TimeRemainingText(
endDate,
R.string.ends_in_x,
color = colorResource(R.color.yellow_50),
fontSize = 12.sp,
fontWeight = FontWeight.Bold
@ -133,4 +125,35 @@ fun BirthdayBanner(endDate: Date, modifier: Modifier = Modifier) {
}
}
}
}
@Composable
fun TimeRemainingText(
endDate: Date,
formatString: Int,
color: Color,
fontSize: TextUnit,
fontWeight: FontWeight
) {
var value by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
val diff = endDate.time - Date().time
if (diff.milliseconds > 1.hours) {
delay(1.minutes)
} else if (diff < 0) {
this.cancel()
} else {
delay(1.seconds)
}
value += 1
}
Text(
stringResource(
formatString,
endDate.getShortRemainingString()
).uppercase(),
color = color,
fontSize = fontSize,
fontWeight = fontWeight
)
}

View file

@ -401,9 +401,13 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
observable = { inventoryRepository.purchaseItem(shopItem.purchaseType, shopItem.key, quantity) }
}
lifecycleScope.launchCatching {
val result = observable() ?: return@launchCatching
val text = snackbarText[0].ifEmpty {
context.getString(R.string.successful_purchase, shopItem.text)
observable()
val text = snackbarText[0].ifBlank {
if (shopItem.text?.isNotBlank() == true) {
context.getString(R.string.successful_purchase, shopItem.text)
} else {
context.getString(R.string.purchased)
}
}
val rightTextColor = when (item.currency) {
"gold" -> ContextCompat.getColor(context, R.color.text_yellow)

View file

@ -93,7 +93,7 @@ fun AssignedView(
Image(
painterResource(R.drawable.edit),
null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary)
colorFilter = ColorFilter.tint(color)
)
Text(
stringResource(R.string.edit_assignees), color = color,

View file

@ -79,9 +79,12 @@ private fun TaskDifficultySelection(
) {
val selectedState = updateTransition(selected)
val context = LocalContext.current
val textColor = selectedState.animateColor {
val iconColor = selectedState.animateColor {
if (it) HabiticaTheme.colors.tintedUiDetails else Color(context.getThemeColor(R.attr.textColorTintedSecondary))
}
val textColor = selectedState.animateColor {
if (it) Color(context.getThemeColor(R.attr.textColorTintedPrimary)) else Color(context.getThemeColor(R.attr.textColorTintedSecondary))
}
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(6.dp), modifier = modifier) {
Box(
contentAlignment = Alignment.Center, modifier = Modifier
@ -105,7 +108,7 @@ private fun TaskDifficultySelection(
.background(HabiticaTheme.colors.tintedUiMain, MaterialTheme.shapes.medium)
)
}
Image(icon, null, colorFilter = ColorFilter.tint(textColor.value))
Image(icon, null, colorFilter = ColorFilter.tint(iconColor.value))
}
Text(
text,

View file

@ -7,6 +7,7 @@ import com.habitrpg.common.habitica.models.notifications.GroupTaskApprovedData
import com.habitrpg.common.habitica.models.notifications.GroupTaskNeedsWorkData
import com.habitrpg.common.habitica.models.notifications.GroupTaskRequiresApprovalData
import com.habitrpg.common.habitica.models.notifications.GuildInvitationData
import com.habitrpg.common.habitica.models.notifications.ItemReceivedData
import com.habitrpg.common.habitica.models.notifications.LoginIncentiveData
import com.habitrpg.common.habitica.models.notifications.NewChatMessageData
import com.habitrpg.common.habitica.models.notifications.NewStuffData
@ -27,6 +28,7 @@ class Notification {
GROUP_TASK_REQUIRES_APPROVAL("GROUP_TASK_REQUIRES_APPROVAL"),
UNALLOCATED_STATS_POINTS("UNALLOCATED_STATS_POINTS"),
WON_CHALLENGE("WON_CHALLENGE"),
ITEM_RECEIVED("ITEM_RECEIVED"),
// Achievements
ACHIEVEMENT_PARTY_UP("ACHIEVEMENT_PARTY_UP"),
@ -93,6 +95,7 @@ class Notification {
Type.FIRST_DROP.type -> FirstDropData::class.java
Type.ACHIEVEMENT_GENERIC.type -> AchievementData::class.java
Type.WON_CHALLENGE.type -> ChallengeWonData::class.java
Type.ITEM_RECEIVED.type -> ItemReceivedData::class.java
Type.ACHIEVEMENT_ALL_YOUR_BASE.type -> AchievementData::class.java
Type.ACHIEVEMENT_BACK_TO_BASICS.type -> AchievementData::class.java

View file

@ -0,0 +1,8 @@
package com.habitrpg.common.habitica.models.notifications
open class ItemReceivedData: NotificationData {
var title: String? = null
var text: String? = null
var icon: String? = null
var destination: String? = null
}

View file

@ -1,6 +1,5 @@
package com.habitrpg.common.habitica.models.notifications
open class QuestInvitationData : NotificationData {
var questKey: String? = null
}

View file

@ -1,2 +1,2 @@
NAME=4.1
CODE=5071
CODE=5081