From d32fe481e40bf64a2a90d1578b34971c87a43862 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Tue, 6 Dec 2022 08:33:08 +0100 Subject: [PATCH] Display assigned avatars --- Habitica/res/values/strings.xml | 2 + .../android/habitica/data/SocialRepository.kt | 1 + .../implementation/SocialRepositoryImpl.kt | 4 + .../data/local/SocialLocalRepository.kt | 1 + .../RealmSocialLocalRepository.kt | 9 ++ .../habitica/interactors/BuyRewardUseCase.kt | 4 +- .../interactors/CheckClassSelectionUseCase.kt | 4 +- .../interactors/DisplayItemDropUseCase.kt | 4 +- .../habitica/interactors/FeedPetUseCase.kt | 4 +- .../habitica/interactors/HatchPetUseCase.kt | 4 +- .../habitica/interactors/LevelUpUseCase.kt | 4 +- .../habitica/interactors/NotifyUserUseCase.kt | 4 +- .../android/habitica/interactors/UseCase.kt | 2 +- .../habitica/ui/activities/MainActivity.kt | 7 +- .../ui/activities/TaskSummaryActivity.kt | 14 +++- .../habitica/ui/theme/HabiticaTheme.kt | 2 +- .../habitica/ui/views/AppHeaderView.kt | 20 +++-- .../habitica/ui/views/BottomSheetUtils.kt | 7 +- .../android/habitica/ui/views/CurrencyText.kt | 5 +- .../habitica/ui/views/GroupPlanMemberList.kt | 84 ++++++++++++------- .../android/habitica/ui/views/LabeledBar.kt | 6 +- .../android/habitica/ui/views/UserRow.kt | 29 ++++++- .../habitica/ui/views/stats/StatsView.kt | 3 +- .../habitica/ui/views/tasks/AssignSheet.kt | 1 + .../habitica/ui/views/tasks/AssignedView.kt | 4 +- version.properties | 2 +- 26 files changed, 162 insertions(+), 69 deletions(-) diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index e1decdb78..8a6ddfd35 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -1270,6 +1270,8 @@ Edit assignees Assign to... Promote to Manager + Manager + Member List You You, %d other 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 5ccb1674d..7ef147f55 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 @@ -119,4 +119,5 @@ interface SocialRepository : BaseRepository { fun getGroupMembership(id: String): Flow fun getGroupMemberships(): Flow> suspend fun blockMember(userID: String): List? + fun getMember(userID: String?): Flow } 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 a99a5fcae..33d407af2 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 @@ -40,6 +40,10 @@ class SocialRepositoryImpl( return apiClient.blockMember(userID) } + override fun getMember(userID: String?): Flow { + return localRepository.getMember(userID) + } + override fun getGroupMembership(id: String) = localRepository.getGroupMembership(userID, id) override fun getGroupMemberships(): Flow> { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt index c40981364..7f712f980 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/SocialLocalRepository.kt @@ -54,4 +54,5 @@ interface SocialLocalRepository : BaseLocalRepository { page: Int ) fun saveInboxConversations(userID: String, conversations: List) + fun getMember(userID: String?): Flow } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt index c63e9094a..032948ad5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmSocialLocalRepository.kt @@ -102,6 +102,15 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm) } } + override fun getMember(userID: String?): Flow { + return realm.where(Member::class.java) + .equalTo("id", userID) + .findAll() + .toFlow() + .filter { member -> member.isLoaded && member.isValid } + .map { member -> member.firstOrNull() } + } + override fun saveGroupMemberships(userID: String?, memberships: List) { save(memberships) if (userID != null) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt index 617c50a8a..9bb470426 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt @@ -11,7 +11,7 @@ class BuyRewardUseCase @Inject constructor( private val taskRepository: TaskRepository, private val soundManager: SoundManager, -) : FlowUseCase() { +) : UseCase() { override suspend fun run(requestValues: RequestValues): TaskScoringResult? { val response = taskRepository.taskChecked(requestValues.user, requestValues.task, false, false, requestValues.notifyFunc) @@ -23,5 +23,5 @@ constructor( internal val user: User?, val task: Task, val notifyFunc: (TaskScoringResult) -> Unit - ) : FlowUseCase.RequestValues + ) : UseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/CheckClassSelectionUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/CheckClassSelectionUseCase.kt index 1c2cf3fb7..b6f377caf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/CheckClassSelectionUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/CheckClassSelectionUseCase.kt @@ -7,7 +7,7 @@ import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity import javax.inject.Inject -class CheckClassSelectionUseCase @Inject constructor() : FlowUseCase() { +class CheckClassSelectionUseCase @Inject constructor() : UseCase() { override suspend fun run(requestValues: RequestValues) { val user = requestValues.user @@ -42,5 +42,5 @@ class CheckClassSelectionUseCase @Inject constructor() : FlowUseCase() { + UseCase() { override suspend fun run(requestValues: RequestValues) { val data = requestValues.data @@ -49,5 +49,5 @@ constructor(private val soundManager: SoundManager): val context: AppCompatActivity, val snackbarTargetView: ViewGroup, val showQuestItems: Boolean - ) : FlowUseCase.RequestValues + ) : UseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt index 5ae6bf77b..d121d676b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt @@ -20,7 +20,7 @@ import javax.inject.Inject class FeedPetUseCase @Inject constructor( private val inventoryRepository: InventoryRepository, -) : FlowUseCase() { +) : UseCase() { override suspend fun run(requestValues: FeedPetUseCase.RequestValues): FeedResponse? { val feedResponse = inventoryRepository.feedPet(requestValues.pet, requestValues.food) (requestValues.context as? SnackbarActivity)?.showSnackbar(content = feedResponse?.message) @@ -77,5 +77,5 @@ constructor( } class RequestValues(val pet: Pet, val food: Food, val context: Context) : - FlowUseCase.RequestValues + UseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt index b2cbdbb50..7b38dd5e4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt @@ -20,7 +20,7 @@ import javax.inject.Inject class HatchPetUseCase @Inject constructor( - private val inventoryRepository: InventoryRepository) : FlowUseCase() { + private val inventoryRepository: InventoryRepository) : UseCase() { override suspend fun run(requestValues: RequestValues): Items? { return inventoryRepository.hatchPet(requestValues.egg, requestValues.potion) { val petWrapper = View.inflate(requestValues.context, R.layout.pet_imageview, null) as? FrameLayout @@ -52,5 +52,5 @@ constructor( } } - class RequestValues(val potion: HatchingPotion, val egg: Egg, val context: Context) : FlowUseCase.RequestValues + class RequestValues(val potion: HatchingPotion, val egg: Egg, val context: Context) : UseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt index 2f0e7b2c6..00910fddb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt @@ -20,7 +20,7 @@ class LevelUpUseCase @Inject constructor( private val soundManager: SoundManager, private val checkClassSelectionUseCase: CheckClassSelectionUseCase -) : FlowUseCase() { +) : UseCase() { override suspend fun run(requestValues: RequestValues): Stats? { soundManager.loadAndPlayAudio(SoundManager.SoundLevelUp) @@ -99,7 +99,7 @@ constructor( val level: Int?, val activity: BaseActivity, val snackbarTargetView: ViewGroup - ) : FlowUseCase.RequestValues { + ) : UseCase.RequestValues { val newLevel: Int = level ?: 0 } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/NotifyUserUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/NotifyUserUseCase.kt index 658bcd66b..b63f9f7f7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/NotifyUserUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/NotifyUserUseCase.kt @@ -27,7 +27,7 @@ class NotifyUserUseCase @Inject constructor( private val levelUpUseCase: LevelUpUseCase, private val userRepository: UserRepository -) : FlowUseCase() { +) : UseCase() { override suspend fun run(requestValues: RequestValues): Stats? { if (requestValues.user == null) { @@ -57,7 +57,7 @@ constructor( val questDamage: Double?, val hasLeveledUp: Boolean?, val level: Int? - ) : FlowUseCase.RequestValues + ) : UseCase.RequestValues companion object { val formatter = NumberFormat.getInstance().apply { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt index 82222ccf0..0d5407580 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt @@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.interactors import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -abstract class FlowUseCase { +abstract class UseCase { protected abstract suspend fun run(requestValues: Q): T suspend fun callInteractor(requestValues: Q): T { return withContext(Dispatchers.Main) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index 7ad85dcec..aace8c7c4 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -16,6 +16,9 @@ import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.core.os.bundleOf import androidx.core.view.children import androidx.drawerlayout.widget.DrawerLayout @@ -240,7 +243,9 @@ open class MainActivity : BaseActivity(), SnackbarActivity { HabiticaTheme { AppHeaderView(viewModel.userViewModel) { showAsBottomSheet { onClose -> - GroupPlanMemberList(it, { + val group by viewModel.userViewModel.currentTeamPlanGroup.collectAsState(null) + val members by viewModel.userViewModel.currentTeamPlanMembers.observeAsState() + GroupPlanMemberList(members, group, { onClose() FullProfileActivity.open(it) }, { member -> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt index b8d9cd0e5..ee18c4334 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt @@ -18,6 +18,7 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment @@ -42,25 +43,35 @@ import androidx.lifecycle.asLiveData import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent +import com.habitrpg.android.habitica.data.SocialRepository import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.TaskDescriptionBuilder +import com.habitrpg.android.habitica.models.members.Member import com.habitrpg.android.habitica.ui.theme.HabiticaTheme import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel import com.habitrpg.android.habitica.ui.views.CompletedAt import com.habitrpg.android.habitica.ui.views.UserRow import com.habitrpg.shared.habitica.models.tasks.TaskType +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class TaskSummaryViewModel(val taskId: String) : BaseViewModel() { @Inject lateinit var taskRespository: TaskRepository + @Inject + lateinit var socialRepository: SocialRepository + val task = taskRespository.getTask(taskId).asLiveData() override fun inject(component: UserComponent) { component.inject(this) } + fun getMember(userID: String?): Flow { + return socialRepository.getMember(userID) + } + @Suppress("UNCHECKED_CAST") class Factory(private val taskID: String) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { @@ -230,8 +241,9 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) { modifier = titleModifier.padding(bottom = 4.dp) ) for (item in task?.group?.assignedUsersDetail ?: emptyList()) { + val member = viewModel.getMember(item.assignedUserID).collectAsState(null) UserRow( - item.assignedUsername ?: "", Modifier + item.assignedUsername ?: "", member.value, Modifier .padding(vertical = 4.dp) .background( colorResource( diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/theme/HabiticaTheme.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/theme/HabiticaTheme.kt index 1e18e3b43..9cc4259d1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/theme/HabiticaTheme.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/theme/HabiticaTheme.kt @@ -135,7 +135,7 @@ object HabiticaTheme { contentBackgroundOffset = Color(context.getThemeColor(R.attr.colorContentBackgroundOffset)), textPrimary = Color(context.getThemeColor(R.attr.textColorPrimary)), textSecondary = Color(context.getThemeColor(R.attr.textColorSecondary)), - textTertiary = Color(context.getThemeColor(R.attr.colorTertiary)), + textTertiary = Color(ContextCompat.getColor(context, R.color.text_ternary)), textDimmed = Color(ContextCompat.getColor(context, R.color.text_dimmed)), ) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt index 451484c8b..71b6d276a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.unit.sp import androidx.core.os.bundleOf import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.helpers.MainNavigationController -import com.habitrpg.android.habitica.models.members.Member import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel @@ -72,7 +71,7 @@ fun UserLevelText(user: User) { @Composable fun AppHeaderView( viewModel: MainUserViewModel, - onMemberRowClicked: (List) -> Unit + onMemberRowClicked: () -> Unit ) { val user by viewModel.user.observeAsState(null) val teamPlan by viewModel.currentTeamPlan.collectAsState(null) @@ -181,7 +180,7 @@ fun AppHeaderView( colorResource(R.color.window_background) ) .clickable { - teamPlanMembers?.let { onMemberRowClicked(it) } + onMemberRowClicked() } ) { for (member in teamPlanMembers?.filter { it.id != user?.id }?.take(6) ?: emptyList()) { @@ -205,11 +204,20 @@ fun AppHeaderView( ClassIcon(className = user?.stats?.habitClass, hasClass = user?.hasClass ?: false, modifier = Modifier.padding(4.dp)) user?.let { UserLevelText(it) } Spacer(Modifier.weight(1f)) - user?.hourglassCount?.toDouble() - ?.let { CurrencyText("hourglasses", it, modifier = Modifier.padding(end = 12.dp)) } + if (user?.isSubscribed == true) { + user?.hourglassCount?.toDouble() + ?.let { + CurrencyText( + "hourglasses", + it, + modifier = Modifier.padding(end = 12.dp).clickable { + MainNavigationController.navigate(R.id.subscriptionPurchaseActivity) + }) + } + } CurrencyText("gold", user?.stats?.gp ?: 0.0, modifier = Modifier.padding(end = 12.dp)) CurrencyText("gems", user?.gemCount?.toDouble() ?: 0.0, modifier = Modifier.clickable { - MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false))) + MainNavigationController.navigate(R.id.gemPurchaseActivity) }) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt index 9d3a399e6..a4424d872 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt @@ -74,21 +74,24 @@ private fun BottomSheetWrapper( val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) var isSheetOpened by remember { mutableStateOf(false) } + val radius = 20.dp ModalBottomSheetLayout( sheetBackgroundColor = Color.Transparent, sheetState = modalBottomSheetState, + sheetShape = RoundedCornerShape(topStart = radius, topEnd = radius), sheetContent = { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier + .padding(horizontal = 4.dp) .border( 2.dp, colorResource(R.color.window_background), - RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) + RoundedCornerShape(topStart = radius, topEnd = radius) ) .background( MaterialTheme.colors.background, - RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) + RoundedCornerShape(topStart = radius, topEnd = radius) ) .padding(vertical = 8.dp) ) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/CurrencyText.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/CurrencyText.kt index 69900db83..9c49aac8a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/CurrencyText.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/CurrencyText.kt @@ -23,7 +23,7 @@ fun CurrencyText( decimals: Int = 2, minForAbbrevation: Int = 0 ) { - Row(verticalAlignment = Alignment.CenterVertically) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { when (currency) { "gold" -> HabiticaIconsHelper.imageOfGold() "gems" -> HabiticaIconsHelper.imageOfGem() @@ -39,8 +39,7 @@ fun CurrencyText( else -> colorResource(R.color.text_primary) }, fontSize = 12.sp, - fontWeight = FontWeight.SemiBold, - modifier = modifier + fontWeight = FontWeight.SemiBold ) } } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/GroupPlanMemberList.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/GroupPlanMemberList.kt index 034e03f6b..95c70c75e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/GroupPlanMemberList.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/GroupPlanMemberList.kt @@ -14,18 +14,15 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.Text -import androidx.compose.material.TextButton 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.asImageBitmap -import androidx.compose.ui.platform.LocalContext 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.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -34,24 +31,52 @@ import androidx.compose.ui.unit.sp import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.models.auth.LocalAuthentication import com.habitrpg.android.habitica.models.members.Member +import com.habitrpg.android.habitica.models.social.Group import com.habitrpg.android.habitica.models.user.Authentication import com.habitrpg.android.habitica.models.user.Profile import com.habitrpg.android.habitica.models.user.Stats import com.habitrpg.android.habitica.ui.theme.HabiticaTheme -import com.habitrpg.common.habitica.extensions.getThemeColor import kotlin.random.Random @Composable fun GroupPlanMemberList( - members: List, + members: List?, + group: Group?, onMemberClicked: (String) -> Unit, onMoreClicked: (Member) -> Unit ) { LazyColumn { - for (member in members) { + item { + Text(stringResource(R.string.member_list), + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = HabiticaTheme.colors.textTertiary, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + .padding(bottom = 20.dp) + ) + } + for (member in members?.sortedWith(compareByDescending { + group?.isLeader( + it.id ?: "" + ) + }.thenByDescending { + group?.isManager( + it.id ?: "" + ) + }.thenBy { it.username }) ?: emptyList()) { item { + val role = if (group?.isLeader(member.id ?: "") == true) { + stringResource(R.string.owner) + } else if (group?.isManager(member.id ?: "") == true) { + stringResource(R.string.manager) + } else { + stringResource(R.string.member) + + } MemberItem( member, + role, onMemberClicked, onMoreClicked, modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) @@ -64,6 +89,7 @@ fun GroupPlanMemberList( @Composable fun MemberItem( member: Member, + role: String, onMemberClicked: (String) -> Unit, onMoreClicked: (Member) -> Unit, modifier: Modifier = Modifier @@ -77,23 +103,14 @@ fun MemberItem( member.id?.let { onMemberClicked(it) } } ) { - TextButton( - onClick = { onMoreClicked(member) }, modifier = Modifier - .size(32.dp) - .background( - Color(LocalContext.current.getThemeColor(R.attr.colorAccent)), - WobblyCircle - ) - .align(Alignment.TopEnd) - ) { - Image(painterResource(R.drawable.menu_messages), null) - } Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.padding(14.dp) + horizontalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.padding(8.dp) ) { - ComposableAvatarView(avatar = member, modifier = Modifier.size(94.dp, 98.dp)) - Column(verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier.height(100.dp)) { + ComposableAvatarView(avatar = member, modifier = Modifier + .padding(6.dp) + .size(94.dp, 98.dp)) + Column(verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier.height(104.dp).padding(end = 6.dp)) { Text( member.displayName, fontWeight = FontWeight.SemiBold, @@ -101,17 +118,16 @@ fun MemberItem( color = HabiticaTheme.colors.textPrimary ) Row( - horizontalArrangement = Arrangement.spacedBy(4.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically ) { Text( member.formattedUsername ?: "", - color = HabiticaTheme.colors.textSecondary + color = HabiticaTheme.colors.textTertiary ) Spacer( Modifier .weight(1.0f) - .background(Color.Red) ) ClassIcon( member.stats?.habitClass, @@ -126,14 +142,16 @@ fun MemberItem( barColor = HabiticaTheme.colors.contentBackgroundOffset, value = member.stats?.hp ?: 0.0, maxValue = (member.stats?.maxHealth ?: 0).toDouble(), - displayCompact = true + displayCompact = true, + barHeight = 5.dp ) LabeledBar( color = colorResource(R.color.xpColor), barColor = HabiticaTheme.colors.contentBackgroundOffset, value = member.stats?.exp ?: 0.0, maxValue = (member.stats?.toNextLevel ?: 0).toDouble(), - displayCompact = true + displayCompact = true, + barHeight = 5.dp ) if (member.hasClass) { LabeledBar( @@ -141,7 +159,8 @@ fun MemberItem( barColor = HabiticaTheme.colors.contentBackgroundOffset, value = member.stats?.mp ?: 0.0, maxValue = (member.stats?.maxMP ?: 0).toDouble(), - displayCompact = true + displayCompact = true, + barHeight = 5.dp ) } Row(horizontalArrangement = Arrangement.SpaceBetween) { @@ -149,11 +168,12 @@ fun MemberItem( stringResource(R.string.level_unabbreviated, member.stats?.lvl ?: 0), fontSize = 14.sp, fontWeight = FontWeight.Normal, - color = HabiticaTheme.colors.textPrimary + color = HabiticaTheme.colors.textTertiary ) + Spacer(Modifier.weight(1f)) Text( - "", fontWeight = FontWeight.SemiBold, fontSize = 14.sp, - color = HabiticaTheme.colors.textPrimary + role, fontWeight = FontWeight.SemiBold, fontSize = 14.sp, + color = HabiticaTheme.colors.textSecondary ) } } @@ -197,5 +217,5 @@ private class MemberProvider : PreviewParameterProvider { @Composable @Preview private fun Preview(@PreviewParameter(MemberProvider::class) member: Member) { - MemberItem(member = member, onMemberClicked = {}, onMoreClicked = {}) + MemberItem(member = member, role = "Manager", onMemberClicked = {}, onMoreClicked = {}) } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/LabeledBar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/LabeledBar.kt index cd7179d3f..3a91edaba 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/LabeledBar.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/LabeledBar.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.habitrpg.android.habitica.R @@ -44,7 +45,8 @@ fun LabeledBar( barColor: Color = HabiticaTheme.colors.windowBackground, value: Double, maxValue: Double, - displayCompact: Boolean, + displayCompact: Boolean = false, + barHeight: Dp = 8.dp, disabled: Boolean = false ) { val cleanedMaxVlaue = java.lang.Double.max(1.0, maxValue) @@ -72,7 +74,7 @@ fun LabeledBar( Modifier .fillMaxWidth() .clip(CircleShape) - .height(8.dp), + .height(barHeight), backgroundColor = barColor, color = color ) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/UserRow.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/UserRow.kt index 09e293326..164798779 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/UserRow.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/UserRow.kt @@ -1,27 +1,51 @@ package com.habitrpg.android.habitica.ui.views -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape 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.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.habitrpg.shared.habitica.models.Avatar @Composable fun UserRow( username: String, + avatar: Avatar?, modifier: Modifier = Modifier, extraContent: @Composable (() -> Unit)? = null, endContent: @Composable (() -> Unit)? = null, color: Color? = null ) { - Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier.fillMaxWidth()) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.fillMaxWidth()) { + Box(modifier = Modifier + .padding(end = 12.dp) + .clip(CircleShape) + .size(40.dp) + .padding(end = 12.dp, top = if (avatar?.currentMount?.isNotBlank() == true) 24.dp else 12.dp)) { + if (avatar != null) { + ComposableAvatarView( + avatar = avatar, + Modifier + .size(96.dp) + .requiredSize(96.dp) + ) + } + } + Column { Text( "@$username", @@ -33,6 +57,7 @@ fun UserRow( extraContent() } } + Spacer(Modifier.weight(1f)) if (endContent != null) { endContent() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt index f1fe3e916..23545919f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextButton @@ -122,7 +121,7 @@ fun StatsViewPreview() { levelValue = 10, equipmentValue = 5, buffValue = 4, - allocatedValue = 8, + allocatedValue = 20, canAllocate = true ) {} } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignSheet.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignSheet.kt index c8d2d6c58..645595bdd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignSheet.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/AssignSheet.kt @@ -94,6 +94,7 @@ fun AssignSheet( } UserRow( username = member.displayName, + avatar = member, color = colorResource(R.color.text_primary), extraContent = { Text( 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 0e0669568..18ca09270 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 @@ -46,7 +46,9 @@ fun AssignedView( .fillMaxWidth() for (assignable in assigned) { UserRow( - username = assignable.identifiableName, modifier = rowModifier, + username = assignable.identifiableName, + avatar = assignable.avatar, + modifier = rowModifier, color = color, extraContent = { completedAt[assignable.id]?.let { CompletedAt(completedAt = it) } diff --git a/version.properties b/version.properties index 1dc1c6bf1..18b1b60b7 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ NAME=4.1 -CODE=4781 \ No newline at end of file +CODE=4791 \ No newline at end of file