mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-13 17:51:57 +00:00
work in feedback
This commit is contained in:
parent
b199c2ea73
commit
553cd19176
28 changed files with 285 additions and 61 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -1319,7 +1319,7 @@
|
|||
<string name="gryphatrice_description">The rare, mystical Gryphatrice joins the birthday bash! Don’t 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">We’re bringing back 10 of the community’s 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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(), {
|
||||
|
||||
}, {}, {})
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
TavernDetailFragment()
|
||||
}
|
||||
1 -> {
|
||||
ChatFragment()
|
||||
val fragment = ChatFragment()
|
||||
fragment.viewModel = viewModel
|
||||
fragment
|
||||
}
|
||||
else -> Fragment()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
}
|
||||
1 -> {
|
||||
chatFragment = ChatFragment()
|
||||
chatFragment?.viewModel = viewModel
|
||||
fragment = chatFragment
|
||||
}
|
||||
else -> fragment = Fragment()
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
}
|
||||
1 -> {
|
||||
chatFragment = ChatFragment()
|
||||
chatFragment?.viewModel = viewModel
|
||||
chatFragment
|
||||
}
|
||||
else -> Fragment()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
NAME=4.1
|
||||
CODE=4991
|
||||
CODE=5031
|
||||
Loading…
Reference in a new issue