diff --git a/Habitica/res/drawable/shop_header_background.xml b/Habitica/res/drawable/shop_header_background.xml
index c00c2c6b2..c5aee6ded 100644
--- a/Habitica/res/drawable/shop_header_background.xml
+++ b/Habitica/res/drawable/shop_header_background.xml
@@ -2,5 +2,4 @@
-
diff --git a/Habitica/res/layout/fragment_faq_overview.xml b/Habitica/res/layout/fragment_faq_overview.xml
index 24ce17a89..38a92a57c 100644
--- a/Habitica/res/layout/fragment_faq_overview.xml
+++ b/Habitica/res/layout/fragment_faq_overview.xml
@@ -8,6 +8,11 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?attr/colorContentBackground">
+
- @string/font_family_medium
- - 10sp
+ - 12sp
- @color/text_ternary
- true
- @dimen/spacing_large
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/ChecklistItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/ChecklistItem.kt
index 7a8c538e6..f526f11d7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/ChecklistItem.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/ChecklistItem.kt
@@ -18,7 +18,7 @@ open class ChecklistItem : RealmObject, BaseMainObject, Parcelable {
get() = "id"
@PrimaryKey
- var id: String? = null
+ var id: String? = UUID.randomUUID().toString()
var text: String? = null
var completed: Boolean = false
var position: Int = 0
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/RemindersItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/RemindersItem.kt
index ef70a2b6a..f7250cf4a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/RemindersItem.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/RemindersItem.kt
@@ -11,6 +11,7 @@ import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.TemporalAccessor
+import java.util.UUID
open class RemindersItem : RealmObject, Parcelable {
@PrimaryKey
@@ -43,7 +44,9 @@ open class RemindersItem : RealmObject, Parcelable {
time = source.readString()
}
- constructor()
+ constructor() {
+ id = UUID.randomUUID().toString()
+ }
override fun equals(other: Any?): Boolean {
return if (other is RemindersItem) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
index bf050d9db..b7bcd3e08 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInviteFragment.kt
@@ -11,11 +11,10 @@ import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
-import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -24,8 +23,8 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
@@ -39,11 +38,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
-import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -67,15 +64,23 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.UUID
import javax.inject.Inject
+fun uUIDFromStringOrNull(name: String): UUID? {
+ return try {
+ UUID.fromString(name)
+ } catch (_: IllegalArgumentException) {
+ null
+ }
+}
+
@HiltViewModel
class PartyInviteViewModel @Inject constructor(
- userRepository : UserRepository,
- userViewModel : MainUserViewModel,
- val socialRepository : SocialRepository
+ userRepository: UserRepository,
+ userViewModel: MainUserViewModel,
+ val socialRepository: SocialRepository
) : BaseViewModel(userRepository, userViewModel) {
val invites = mutableStateListOf("")
- suspend fun sendInvites() : List? {
+ suspend fun sendInvites(): List? {
val inviteMap = mapOf>(
"emails" to mutableListOf(),
"uuids" to mutableListOf(),
@@ -84,7 +89,7 @@ class PartyInviteViewModel @Inject constructor(
for (invite in invites) {
if (invite.isValidEmail()) {
inviteMap["emails"]?.add(invite)
- } else if (UUID.fromString(invite) != null) {
+ } else if (uUIDFromStringOrNull(invite) != null) {
inviteMap["uuids"]?.add(invite)
} else if (invite.isNotBlank()) {
inviteMap["usernames"]?.add(invite)
@@ -96,22 +101,22 @@ class PartyInviteViewModel @Inject constructor(
@AndroidEntryPoint
class PartyInviteFragment : BaseFragment() {
- val viewModel : PartyInviteViewModel by viewModels()
+ val viewModel: PartyInviteViewModel by viewModels()
- override var binding : FragmentComposeBinding? = null
+ override var binding: FragmentComposeBinding? = null
override fun createBinding(
- inflater : LayoutInflater,
- container : ViewGroup?
- ) : FragmentComposeBinding {
+ inflater: LayoutInflater,
+ container: ViewGroup?
+ ): FragmentComposeBinding {
return FragmentComposeBinding.inflate(inflater, container, false)
}
override fun onCreateView(
- inflater : LayoutInflater,
- container : ViewGroup?,
- savedInstanceState : Bundle?
- ) : View? {
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)
binding?.composeView?.setContent {
HabiticaTheme {
@@ -125,18 +130,18 @@ class PartyInviteFragment : BaseFragment() {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PartyInviteView(
- viewModel : PartyInviteViewModel
+ viewModel: PartyInviteViewModel
) {
- var inviteButtonState : LoadingButtonState by remember { mutableStateOf(LoadingButtonState.CONTENT) }
+ var inviteButtonState: LoadingButtonState by remember { mutableStateOf(LoadingButtonState.CONTENT) }
val scope = rememberCoroutineScope()
val scrollableState = rememberScrollState()
- val invites = viewModel.invites
LazyColumn(
Modifier
.fillMaxSize()
.padding(14.dp)
- .scrollable(scrollableState, Orientation.Vertical)) {
+ .scrollable(scrollableState, Orientation.Vertical)
+ ) {
item {
Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
@@ -155,40 +160,61 @@ fun PartyInviteView(
)
}
}
- items(invites.indices.toList()) { index ->
- val invite = invites[index]
- val transition = updateTransition(invites.size - 1 == index, label = "isLast")
+ items(viewModel.invites.indices.toList()) { index ->
+ val invite = viewModel.invites[index]
+ val transition = updateTransition(viewModel.invites.size - 1 == index, label = "isLast")
val rotation = transition.animateFloat(
label = "isAssigned",
transitionSpec = { spring(Spring.DampingRatioLowBouncy, Spring.StiffnessMediumLow) }
) {
if (it) 135f else 0f
}
- Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
- .fillMaxWidth()
- .padding(0.dp, 4.dp)
- .background(HabiticaTheme.colors.windowBackground, HabiticaTheme.shapes.medium)
- .padding(4.dp, 4.dp)
- .animateItemPlacement()) {
- Image(
- painterResource(R.drawable.ic_close_white_24dp),
- null,
- colorFilter = ColorFilter.tint(HabiticaTheme.colors.textPrimary),
+ Row(
+ verticalAlignment = Alignment.CenterVertically, modifier = Modifier
+ .fillMaxWidth()
+ .padding(0.dp, 4.dp)
+ .background(HabiticaTheme.colors.windowBackground, HabiticaTheme.shapes.medium)
+ .padding(4.dp, 4.dp)
+ .animateItemPlacement()
+ ) {
+ Button(
+ onClick = {
+ if (viewModel.invites.size - 1 >= index && viewModel.invites[index].isNotBlank()) {
+ viewModel.invites.removeAt(index)
+ }
+ },
+ colors = ButtonDefaults.textButtonColors(),
+ elevation = ButtonDefaults.elevation(0.dp),
+ contentPadding = PaddingValues(0.dp),
modifier = Modifier
- .rotate(rotation.value)
.size(32.dp)
.padding(3.dp)
- )
+ ) {
+ Image(
+ painterResource(R.drawable.ic_close_white_24dp),
+ null,
+ colorFilter = ColorFilter.tint(HabiticaTheme.colors.textPrimary),
+ modifier = Modifier
+ .rotate(rotation.value)
+ .size(32.dp)
+ )
+ }
+
TextField(
value = invite, onValueChange = { value ->
- if (invites.size - 1 == index && invites[index].isBlank()) {
+ if (viewModel.invites.size - 1 == index && viewModel.invites[index].isBlank()) {
viewModel.invites.add("")
}
viewModel.invites[index] = value
},
singleLine = true,
placeholder = { Text(stringResource(R.string.username_or_email)) },
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, focusedIndicatorColor = Color.Transparent),
+ colors = TextFieldDefaults.textFieldColors(
+ backgroundColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ focusedIndicatorColor = Color.Transparent,
+ textColor = HabiticaTheme.colors.textPrimary
+ ),
modifier = Modifier
.onFocusChanged {
if (!it.isFocused) {
@@ -202,7 +228,7 @@ fun PartyInviteView(
}
item {
InviteButton(
- state = if (invites.any { it.isNotBlank() }) inviteButtonState else LoadingButtonState.DISABLED,
+ state = if (viewModel.invites.any { it.isNotBlank() }) inviteButtonState else LoadingButtonState.DISABLED,
modifier = Modifier.fillMaxWidth(),
onClick = {
inviteButtonState = LoadingButtonState.LOADING
@@ -213,6 +239,7 @@ fun PartyInviteView(
if (responses?.isNotEmpty() == true) {
inviteButtonState = LoadingButtonState.SUCCESS
viewModel.invites.clear()
+ viewModel.invites.add("")
} else {
inviteButtonState = LoadingButtonState.FAILED
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
index 44f3fc5ca..401066fe1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
@@ -20,8 +20,9 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -73,8 +74,9 @@ class PartySeekingViewModel @Inject constructor(
val socialRepository : SocialRepository
) : BaseViewModel(userRepository, userViewModel) {
val isRefreshing = mutableStateOf(false)
- var seekingUsers : Flow>
-
+ val seekingUsers : Flow>
+ val successfulInvites = mutableStateListOf()
+ val inviteStates = mutableStateMapOf()
init {
seekingUsers = Pager(
config = PagingConfig(
@@ -163,9 +165,6 @@ fun PartySeekingView(
val pullRefreshState = rememberPullRefreshState(refreshing, { pageData.refresh() })
val scope = rememberCoroutineScope()
- val successfulInvites = remember { mutableListOf() }
- val inviteStates = remember { mutableMapOf() }
-
Box(
modifier = modifier
.fillMaxSize()
@@ -209,26 +208,26 @@ fun PartySeekingView(
if (it == null) return@items
PartySeekingListItem(
user = it,
- inviteState = inviteStates[it.id] ?: LoadingButtonState.CONTENT,
- isInvited = successfulInvites.contains(it.id),
+ inviteState =viewModel.inviteStates[it.id] ?: LoadingButtonState.CONTENT,
+ isInvited = viewModel.successfulInvites.contains(it.id),
modifier = Modifier.animateItemPlacement()
) { member ->
scope.launchCatching({
- inviteStates[member.id] = LoadingButtonState.FAILED
+ viewModel.inviteStates[member.id] = LoadingButtonState.FAILED
}) {
- inviteStates[member.id] = LoadingButtonState.LOADING
- val response = if (successfulInvites.contains(member.id)) viewModel.inviteUser(member) else viewModel.inviteUser(member)
+ viewModel.inviteStates[member.id] = LoadingButtonState.LOADING
+ val response: Any? = if (viewModel.successfulInvites.contains(member.id)) viewModel.rescindInvite(member) else viewModel.inviteUser(member)
if (response != null) {
- if (successfulInvites.contains(member.id)) {
- successfulInvites.remove(member.id)
+ if (viewModel.successfulInvites.contains(member.id)) {
+ viewModel.successfulInvites.remove(member.id)
} else {
- successfulInvites.add(member.id)
+ viewModel.successfulInvites.add(member.id)
}
- inviteStates[member.id] = LoadingButtonState.SUCCESS
+ viewModel.inviteStates[member.id] = LoadingButtonState.SUCCESS
delay(4.toDuration(DurationUnit.SECONDS))
- inviteStates[member.id] = LoadingButtonState.CONTENT
+ viewModel.inviteStates[member.id] = LoadingButtonState.CONTENT
} else {
- inviteStates[member.id] = LoadingButtonState.FAILED
+ viewModel.inviteStates[member.id] = LoadingButtonState.FAILED
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt
index 3dfd60305..90cce8a47 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt
@@ -7,19 +7,21 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.FAQRepository
import com.habitrpg.android.habitica.databinding.FragmentFaqOverviewBinding
import com.habitrpg.android.habitica.databinding.SupportFaqItemBinding
+import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.common.habitica.extensions.layoutInflater
import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.common.habitica.helpers.setMarkdown
-import javax.inject.Inject
import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
@AndroidEntryPoint
class FAQOverviewFragment : BaseMainFragment() {
@@ -32,6 +34,8 @@ class FAQOverviewFragment : BaseMainFragment() {
@Inject
lateinit var faqRepository: FAQRepository
+ @Inject
+ lateinit var configManager: AppConfigManager
override fun onCreateView(
inflater: LayoutInflater,
@@ -46,6 +50,11 @@ class FAQOverviewFragment : BaseMainFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ binding?.npcHeader?.npcBannerView?.shopSpriteSuffix = configManager.shopSpriteSuffix()
+ binding?.npcHeader?.npcBannerView?.identifier = "tavern"
+ binding?.npcHeader?.namePlate?.setText(R.string.tavern_owner)
+ binding?.npcHeader?.descriptionView?.isVisible = false
+
binding?.healthSection?.findViewById(R.id.icon_view)?.setImageBitmap(
HabiticaIconsHelper.imageOfHeartLarge()
)
diff --git a/settings.gradle b/settings.gradle
index d2fe38dbf..24fb3f996 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,7 @@
+plugins {
+ id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0'
+}
+
include 'Habitica', ':Habitica'
include ':wearos'
include ':common'