From 8cd5b88e1be8c31559509d2adf5fdec340219c03 Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 17:09:08 +0100 Subject: [PATCH 1/9] Implement updated legend settings with leaderboard toggle and shoutout edits --- .../core/serialization/json/NostrJson.kt | 6 + .../android/premium/api/PremiumApiImpl.kt | 3 +- .../api/model/MembershipStatusResponse.kt | 1 + .../model/UpdatePrimalLegendProfileRequest.kt | 8 +- .../premium/domain/PremiumMembership.kt | 2 + .../premium/legend/LegendaryCustomization.kt | 2 + .../LegendaryProfileCustomizationContract.kt | 16 +- .../LegendaryProfileCustomizationScreen.kt | 339 ++++++++++++++---- .../LegendaryProfileCustomizationViewModel.kt | 12 +- .../premium/repository/PremiumRepository.kt | 11 +- .../profile/domain/PrimalLegendProfile.kt | 1 + app/src/main/res/values/strings.xml | 9 +- 12 files changed, 312 insertions(+), 98 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/core/serialization/json/NostrJson.kt b/app/src/main/kotlin/net/primal/android/core/serialization/json/NostrJson.kt index e6c5fa257..18efbdef9 100644 --- a/app/src/main/kotlin/net/primal/android/core/serialization/json/NostrJson.kt +++ b/app/src/main/kotlin/net/primal/android/core/serialization/json/NostrJson.kt @@ -17,6 +17,12 @@ val NostrJson = Json { coerceInputValues = true } +val NostrJsonImplicitNulls = Json { + ignoreUnknownKeys = true + coerceInputValues = true + explicitNulls = false +} + val NostrJsonEncodeDefaults = Json { ignoreUnknownKeys = true encodeDefaults = true diff --git a/app/src/main/kotlin/net/primal/android/premium/api/PremiumApiImpl.kt b/app/src/main/kotlin/net/primal/android/premium/api/PremiumApiImpl.kt index 3882d793e..3d14395da 100644 --- a/app/src/main/kotlin/net/primal/android/premium/api/PremiumApiImpl.kt +++ b/app/src/main/kotlin/net/primal/android/premium/api/PremiumApiImpl.kt @@ -3,6 +3,7 @@ package net.primal.android.premium.api import javax.inject.Inject import net.primal.android.core.serialization.json.NostrJson import net.primal.android.core.serialization.json.NostrJsonEncodeDefaults +import net.primal.android.core.serialization.json.NostrJsonImplicitNulls import net.primal.android.core.serialization.json.decodeFromStringOrNull import net.primal.android.networking.di.PrimalCacheApiClient import net.primal.android.networking.di.PrimalWalletApiClient @@ -213,7 +214,7 @@ class PremiumApiImpl @Inject constructor( AppSpecificDataRequest( eventFromUser = nostrNotary.signAppSpecificDataNostrEvent( userId = userId, - content = NostrJson.encodeToString(updateProfileRequest), + content = NostrJsonImplicitNulls.encodeToString(updateProfileRequest), ), ), ), diff --git a/app/src/main/kotlin/net/primal/android/premium/api/model/MembershipStatusResponse.kt b/app/src/main/kotlin/net/primal/android/premium/api/model/MembershipStatusResponse.kt index 2b95d939a..1aaf02939 100644 --- a/app/src/main/kotlin/net/primal/android/premium/api/model/MembershipStatusResponse.kt +++ b/app/src/main/kotlin/net/primal/android/premium/api/model/MembershipStatusResponse.kt @@ -19,4 +19,5 @@ data class MembershipStatusResponse( @SerialName("recurring") val recurring: Boolean = false, @SerialName("renews_on") val renewsOn: Long? = null, @SerialName("origin") val origin: String? = null, + @SerialName("edited_shoutout") val editedShoutout: String? = null, ) diff --git a/app/src/main/kotlin/net/primal/android/premium/api/model/UpdatePrimalLegendProfileRequest.kt b/app/src/main/kotlin/net/primal/android/premium/api/model/UpdatePrimalLegendProfileRequest.kt index f7ec8ad56..8d6afa416 100644 --- a/app/src/main/kotlin/net/primal/android/premium/api/model/UpdatePrimalLegendProfileRequest.kt +++ b/app/src/main/kotlin/net/primal/android/premium/api/model/UpdatePrimalLegendProfileRequest.kt @@ -5,7 +5,9 @@ import kotlinx.serialization.Serializable @Serializable data class UpdatePrimalLegendProfileRequest( - @SerialName("style") val styleId: String, - @SerialName("custom_badge") val customBadge: Boolean, - @SerialName("avatar_glow") val avatarGlow: Boolean, + @SerialName("style") val styleId: String?, + @SerialName("custom_badge") val customBadge: Boolean?, + @SerialName("avatar_glow") val avatarGlow: Boolean?, + @SerialName("in_leaderboard") val inLeaderboard: Boolean?, + @SerialName("edited_shoutout") val editedShoutout: String?, ) diff --git a/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt b/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt index d3ebd20c4..f878c0fad 100644 --- a/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt +++ b/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt @@ -1,6 +1,7 @@ package net.primal.android.premium.domain import kotlinx.datetime.Clock +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable @@ -19,6 +20,7 @@ data class PremiumMembership( val recurring: Boolean = false, val renewsOn: Long? = null, val origin: String? = null, + val editedShoutout: String? = null, ) { fun isExpired() = expiresOn != null && Clock.System.now().epochSeconds > expiresOn } diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/LegendaryCustomization.kt b/app/src/main/kotlin/net/primal/android/premium/legend/LegendaryCustomization.kt index fa1e037de..37b203517 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/LegendaryCustomization.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/LegendaryCustomization.kt @@ -8,6 +8,7 @@ data class LegendaryCustomization( val legendaryStyle: LegendaryStyle? = null, val legendSince: Long? = null, val currentShoutout: String? = null, + val inLeaderboard: Boolean? = null, ) fun PrimalLegendProfile.asLegendaryCustomization() = @@ -17,4 +18,5 @@ fun PrimalLegendProfile.asLegendaryCustomization() = legendaryStyle = LegendaryStyle.valueById(id = styleId), legendSince = legendSince, currentShoutout = currentShoutout, + inLeaderboard = inLeaderboard, ) diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt index f7922d7e5..794f196f2 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt @@ -12,17 +12,17 @@ interface LegendaryProfileCustomizationContract { val membership: PremiumMembership? = null, val avatarLegendaryCustomization: LegendaryCustomization? = null, val applyingChanges: Boolean = false, - ) + ) { + fun computeShoutout() = membership?.editedShoutout ?: avatarLegendaryCustomization?.currentShoutout ?: "" + } sealed class UiEvent { data class ApplyCustomization( - val customBadge: Boolean, - val avatarGlow: Boolean, - val style: LegendaryStyle, + val customBadge: Boolean? = null, + val avatarGlow: Boolean? = null, + val inLeaderboard: Boolean? = null, + val style: LegendaryStyle? = null, + val editedShoutout: String? = null, ) : UiEvent() } - - sealed class SideEffect { - data object CustomizationSaved : SideEffect() - } } diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt index 188fa7c12..9bf6bc3fb 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt @@ -1,37 +1,50 @@ package net.primal.android.premium.legend.custimization +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow 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.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -44,27 +57,21 @@ import net.primal.android.core.compose.PrimalDivider import net.primal.android.core.compose.PrimalSwitch import net.primal.android.core.compose.PrimalTopAppBar import net.primal.android.core.compose.UniversalAvatarThumbnail -import net.primal.android.core.compose.button.PrimalFilledButton import net.primal.android.core.compose.icons.PrimalIcons import net.primal.android.core.compose.icons.primaliconpack.ArrowBack import net.primal.android.core.compose.icons.primaliconpack.LegendaryProfileNoCustomization import net.primal.android.premium.legend.LegendaryCustomization import net.primal.android.premium.legend.LegendaryStyle +import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.ui.PremiumBadge import net.primal.android.theme.AppTheme +const val SHOUTOUT_CHAR_LIMIT = 140 + @Composable fun LegendaryProfileCustomizationScreen(viewModel: LegendaryProfileCustomizationViewModel, onClose: () -> Unit) { val uiState by viewModel.state.collectAsState() - LaunchedEffect(viewModel, onClose) { - viewModel.effect.collect { - when (it) { - LegendaryProfileCustomizationContract.SideEffect.CustomizationSaved -> onClose() - } - } - } - LegendaryProfileCustomizationScreen( state = uiState, eventPublisher = viewModel::setEvent, @@ -76,7 +83,7 @@ fun LegendaryProfileCustomizationScreen(viewModel: LegendaryProfileCustomization @Composable fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, - eventPublisher: (LegendaryProfileCustomizationContract.UiEvent) -> Unit, + eventPublisher: (UiEvent) -> Unit, onClose: () -> Unit, ) { val snackbarHostState = remember { SnackbarHostState() } @@ -89,8 +96,39 @@ fun LegendaryProfileCustomizationScreen( var selectedStyle by remember(state.avatarLegendaryCustomization?.legendaryStyle) { mutableStateOf(state.avatarLegendaryCustomization?.legendaryStyle) } + var inLeaderboard by remember(state.avatarLegendaryCustomization?.inLeaderboard) { + mutableStateOf(state.avatarLegendaryCustomization?.inLeaderboard) + } + var shoutout by remember(state.membership?.editedShoutout, state.avatarLegendaryCustomization?.currentShoutout) { + mutableStateOf(state.computeShoutout()) + } + + var editsInReview by remember(state.membership?.editedShoutout) { + mutableStateOf(state.membership?.editedShoutout != null) + } + + var editMode by remember { mutableStateOf(false) } + val textFieldState = + rememberSaveable( + state.membership?.editedShoutout, + state.avatarLegendaryCustomization?.currentShoutout, + saver = TextFieldState.Saver, + ) { + TextFieldState(initialText = state.computeShoutout()) + } + + fun cancelEditMode() { + editMode = false + shoutout = state.computeShoutout() + textFieldState.edit { replace(0, length, shoutout) } + } + + BackHandler(enabled = editMode) { + cancelEditMode() + } Scaffold( + modifier = Modifier.imePadding(), topBar = { PrimalTopAppBar( title = stringResource(id = R.string.premium_legend_profile_customization), @@ -99,18 +137,17 @@ fun LegendaryProfileCustomizationScreen( ) }, bottomBar = { - BottomBarButton( - text = stringResource(id = R.string.premium_legend_profile_apply_button), - onClick = { - eventPublisher( - LegendaryProfileCustomizationContract.UiEvent.ApplyCustomization( - customBadge = customBadge ?: false, - avatarGlow = avatarGlow ?: false, - style = selectedStyle ?: LegendaryStyle.NO_CUSTOMIZATION, - ), - ) - }, - ) + if (editMode) { + BottomBarButtons( + onSendClick = { + editMode = false + editsInReview = true + eventPublisher(UiEvent.ApplyCustomization(editedShoutout = shoutout)) + }, + onCancelClick = { cancelEditMode() }, + shoutoutLength = shoutout.length, + ) + } }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) @@ -168,7 +205,10 @@ fun LegendaryProfileCustomizationScreen( .fillMaxWidth() .padding(horizontal = 32.dp, vertical = 8.dp), activeLegendaryStyle = selectedStyle ?: LegendaryStyle.NO_CUSTOMIZATION, - onStyleChanged = { selectedStyle = it }, + onStyleChanged = { + selectedStyle = it + eventPublisher(UiEvent.ApplyCustomization(style = it)) + }, ) SwitchSettings( @@ -176,14 +216,43 @@ fun LegendaryProfileCustomizationScreen( .fillMaxWidth() .padding(horizontal = 16.dp), avatarRing = avatarGlow == true, - onAvatarRingChanged = { avatarGlow = it }, + onAvatarRingChanged = { + avatarGlow = it + eventPublisher(UiEvent.ApplyCustomization(avatarGlow = it)) + }, customBadge = customBadge == true, - onCustomBadgeChanged = { customBadge = it }, + onCustomBadgeChanged = { + customBadge = it + eventPublisher(UiEvent.ApplyCustomization(customBadge = it)) + }, + appearInLeaderboard = inLeaderboard == true, + onAppearInLeaderboardChanged = { + inLeaderboard = it + eventPublisher(UiEvent.ApplyCustomization(inLeaderboard = it)) + }, ) + Spacer(modifier = Modifier.height(24.dp)) + LegendCardShoutout( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + onShoutoutChanged = { shoutout = it }, + editMode = editMode, + onEditModeChange = { editMode = it }, + textFieldState = textFieldState, + ) + if (editsInReview) { + EditsInReviewBadge(modifier = Modifier.padding(bottom = 16.dp)) + } + Text( color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, - text = stringResource(id = R.string.premium_legend_profile_customization_notice), + text = if (editsInReview) { + stringResource(id = R.string.premium_legend_profile_customization_notice_in_review) + } else { + stringResource(id = R.string.premium_legend_profile_customization_notice) + }, style = AppTheme.typography.bodySmall, fontSize = 13.sp, textAlign = TextAlign.Center, @@ -193,6 +262,90 @@ fun LegendaryProfileCustomizationScreen( } } +@Composable +private fun LegendCardShoutout( + modifier: Modifier = Modifier, + textFieldState: TextFieldState, + onShoutoutChanged: (String) -> Unit, + editMode: Boolean, + onEditModeChange: (Boolean) -> Unit, +) { + Text( + text = stringResource(id = R.string.premium_legend_profile_customization_card_shoutout).uppercase(), + style = AppTheme.typography.bodyMedium, + fontSize = 14.sp, + ) + LaunchedEffect(textFieldState) { + snapshotFlow { textFieldState.text }.collect { + if (it.length > SHOUTOUT_CHAR_LIMIT) { + textFieldState.setTextAndPlaceCursorAtEnd(it.take(SHOUTOUT_CHAR_LIMIT).toString()) + } else { + onShoutoutChanged(it.toString()) + } + } + } + + val bottomPadding = if (editMode) 28.dp else 12.dp + + BasicTextField( + modifier = modifier + .clip(AppTheme.shapes.medium) + .background(AppTheme.extraColorScheme.surfaceVariantAlt1), + readOnly = !editMode, + state = textFieldState, + textStyle = AppTheme.typography.bodyMedium.copy( + color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, + textAlign = TextAlign.Center, + ), + decorator = { innerTextField -> + Box( + modifier = modifier + .padding(horizontal = 16.dp) + .padding(top = 28.dp, bottom = bottomPadding) + .clip(AppTheme.shapes.medium) + .background(AppTheme.extraColorScheme.surfaceVariantAlt1), + contentAlignment = Alignment.Center, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically), + ) { + innerTextField() + + AnimatedVisibility(visible = !editMode) { + TextButton( + onClick = { onEditModeChange(true) }, + ) { + Text( + text = stringResource(id = R.string.premium_legend_profile_customization_suggest_edits), + color = AppTheme.colorScheme.secondary, + style = AppTheme.typography.bodySmall, + fontSize = 14.sp, + ) + } + } + } + } + }, + ) +} + +@Composable +private fun EditsInReviewBadge(modifier: Modifier = Modifier) { + Text( + modifier = modifier + .background(AppTheme.extraColorScheme.surfaceVariantAlt2) + .padding(vertical = 4.dp), + text = stringResource(id = R.string.premium_legend_profile_customization_edits_under_review).uppercase(), + color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, + style = AppTheme.typography.bodyMedium, + fontWeight = FontWeight.ExtraBold, + textAlign = TextAlign.Center, + fontSize = 13.sp, + ) + +} + @Composable private fun LegendaryColorPicker( modifier: Modifier, @@ -322,6 +475,8 @@ private fun SwitchSettings( onCustomBadgeChanged: (Boolean) -> Unit, avatarRing: Boolean, onAvatarRingChanged: (Boolean) -> Unit, + appearInLeaderboard: Boolean, + onAppearInLeaderboardChanged: (Boolean) -> Unit, ) { Column( modifier = modifier.background( @@ -329,64 +484,102 @@ private fun SwitchSettings( shape = AppTheme.shapes.large, ), ) { - Row( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier.weight(1f), - text = stringResource(R.string.premium_legend_profile_custom_badge), - fontSize = 16.sp, - style = AppTheme.typography.bodyMedium, - ) - PrimalSwitch( - checked = customBadge, - onCheckedChange = onCustomBadgeChanged, - ) - } + SwitchSettingsSingleRow( + text = stringResource(R.string.premium_legend_profile_custom_badge), + checked = customBadge, + onCheckedChange = onCustomBadgeChanged, + ) PrimalDivider() - Row( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier.weight(1f), - text = stringResource(R.string.premium_legend_profile_avatar_ring), - fontSize = 16.sp, - style = AppTheme.typography.bodyMedium, - ) - PrimalSwitch( - checked = avatarRing, - onCheckedChange = onAvatarRingChanged, - ) - } + SwitchSettingsSingleRow( + text = stringResource(R.string.premium_legend_profile_avatar_ring), + checked = avatarRing, + onCheckedChange = onAvatarRingChanged, + ) + + PrimalDivider() + + SwitchSettingsSingleRow( + text = stringResource(R.string.premium_legend_profile_appear_in_leaderboard), + checked = appearInLeaderboard, + onCheckedChange = onAppearInLeaderboardChanged, + ) + } } @Composable -private fun BottomBarButton( - text: String, +private fun SwitchSettingsSingleRow( modifier: Modifier = Modifier, - onClick: () -> Unit, + text: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, ) { - Box( - modifier = modifier - .navigationBarsPadding() - .padding(bottom = 36.dp) - .padding(horizontal = 36.dp), + Row( + modifier = modifier.padding(horizontal = 16.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, ) { - PrimalFilledButton( - modifier = Modifier.fillMaxWidth(), - onClick = onClick, + Text( + modifier = Modifier.weight(1f), + text = text, + fontSize = 16.sp, + style = AppTheme.typography.bodyMedium, + ) + PrimalSwitch( + checked = checked, + onCheckedChange = onCheckedChange, + ) + } +} + +@Composable +private fun BottomBarButtons( + onSendClick: () -> Unit, + onCancelClick: () -> Unit, + shoutoutLength: Int, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp, vertical = 4.dp) + .background(AppTheme.colorScheme.background), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = "$shoutoutLength/$SHOUTOUT_CHAR_LIMIT", + color = AppTheme.extraColorScheme.onSurfaceVariantAlt4, + style = AppTheme.typography.bodySmall, + ) + + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, ) { - Text( - modifier = Modifier.padding(vertical = 8.dp), - text = text, - style = AppTheme.typography.bodyLarge, - fontWeight = FontWeight.SemiBold, - ) + Button( + onClick = onCancelClick, + colors = ButtonDefaults.buttonColors( + containerColor = AppTheme.extraColorScheme.surfaceVariantAlt1, + ), + contentPadding = PaddingValues(horizontal = 20.dp, vertical = 2.dp), + ) { + Text( + text = stringResource(id = R.string.premium_legend_profile_customization_cancel), + style = AppTheme.typography.bodyMedium, + color = AppTheme.colorScheme.onPrimary, + ) + } + Button( + onClick = onSendClick, + contentPadding = PaddingValues(horizontal = 20.dp, vertical = 2.dp), + ) { + Text( + text = stringResource(id = R.string.premium_legend_profile_customization_send), + style = AppTheme.typography.bodyMedium, + color = Color.White, + ) + } } } } diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt index c1f1f2ec0..4eebb69a9 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt @@ -4,16 +4,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.getAndUpdate -import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import net.primal.android.networking.sockets.errors.WssException import net.primal.android.premium.legend.asLegendaryCustomization -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.SideEffect import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiState import net.primal.android.premium.repository.PremiumRepository @@ -37,10 +34,6 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( private val events: MutableSharedFlow = MutableSharedFlow() fun setEvent(event: UiEvent) = viewModelScope.launch { events.emit(event) } - private val _effect: Channel = Channel() - val effect = _effect.receiveAsFlow() - private fun setEffect(effect: SideEffect) = viewModelScope.launch { _effect.send(effect) } - init { observeActiveAccount() observeProfile() @@ -65,12 +58,13 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( try { premiumRepository.updateLegendProfile( userId = activeAccountStore.activeUserId(), - styleId = event.style.id, + styleId = event.style?.id, avatarGlow = event.avatarGlow, customBadge = event.customBadge, + inLeaderboard = event.inLeaderboard, + editedShoutout = event.editedShoutout, ) userRepository.fetchAndUpdateUserAccount(userId = activeAccountStore.activeUserId()) - setEffect(SideEffect.CustomizationSaved) } catch (error: WssException) { Timber.e(error) } finally { diff --git a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt index 95d620461..08fb64e87 100644 --- a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt +++ b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt @@ -99,9 +99,11 @@ class PremiumRepository @Inject constructor( suspend fun updateLegendProfile( userId: String, - styleId: String, - customBadge: Boolean, - avatarGlow: Boolean, + styleId: String?, + customBadge: Boolean?, + avatarGlow: Boolean?, + inLeaderboard: Boolean?, + editedShoutout: String?, ) = withContext(dispatchers.io()) { premiumApi.updateLegendProfile( userId = userId, @@ -109,6 +111,8 @@ class PremiumRepository @Inject constructor( styleId = styleId, customBadge = customBadge, avatarGlow = avatarGlow, + inLeaderboard = inLeaderboard, + editedShoutout = editedShoutout, ), ) } @@ -134,6 +138,7 @@ class PremiumRepository @Inject constructor( cohort2 = this.cohort2, recurring = this.recurring, origin = this.origin, + editedShoutout = this.editedShoutout, ) } } diff --git a/app/src/main/kotlin/net/primal/android/profile/domain/PrimalLegendProfile.kt b/app/src/main/kotlin/net/primal/android/profile/domain/PrimalLegendProfile.kt index e15a65761..ff4967dad 100644 --- a/app/src/main/kotlin/net/primal/android/profile/domain/PrimalLegendProfile.kt +++ b/app/src/main/kotlin/net/primal/android/profile/domain/PrimalLegendProfile.kt @@ -9,5 +9,6 @@ data class PrimalLegendProfile( @SerialName("custom_badge") val customBadge: Boolean, @SerialName("avatar_glow") val avatarGlow: Boolean, @SerialName("legend_since") val legendSince: Long? = null, + @SerialName("in_leaderboard") val inLeaderboard: Boolean? = null, @SerialName("current_shoutout") val currentShoutout: String? = null, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5cd3c8376..da1c68cb4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -317,9 +317,16 @@ Become a Legend Now Legendary Profile - Don’t want to stand out?\nIf you disable the custom badge and avatar ring,\nyour profile will look like any other profile on Primal. + Legend Card Shoutout + Legend cards contain a personalized shoutout from\nPrimal to all our legends. + Your edits have been sent to primal for review. The\nupdated content should be live soon. + suggest edits + Edits under review + Cancel + Send Custom badge Avatar ring + Appear in leaderboard Apply Legend since From 7a916c44b1eaa53819ca887396bf65318eb65600 Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 17:14:03 +0100 Subject: [PATCH 2/9] Auto-format code and fix detekt issue --- .../android/premium/domain/PremiumMembership.kt | 1 - .../LegendaryProfileCustomizationScreen.kt | 2 -- .../LegendaryProfileCustomizationViewModel.kt | 13 ++++++++----- .../premium/repository/PremiumRepository.kt | 14 ++------------ 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt b/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt index f878c0fad..d54ba7254 100644 --- a/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt +++ b/app/src/main/kotlin/net/primal/android/premium/domain/PremiumMembership.kt @@ -1,7 +1,6 @@ package net.primal.android.premium.domain import kotlinx.datetime.Clock -import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt index 9bf6bc3fb..831513456 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt @@ -343,7 +343,6 @@ private fun EditsInReviewBadge(modifier: Modifier = Modifier) { textAlign = TextAlign.Center, fontSize = 13.sp, ) - } @Composable @@ -505,7 +504,6 @@ private fun SwitchSettings( checked = appearInLeaderboard, onCheckedChange = onAppearInLeaderboardChanged, ) - } } diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt index 4eebb69a9..ec55997f1 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.launch import net.primal.android.networking.sockets.errors.WssException +import net.primal.android.premium.api.model.UpdatePrimalLegendProfileRequest import net.primal.android.premium.legend.asLegendaryCustomization import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiState @@ -58,11 +59,13 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( try { premiumRepository.updateLegendProfile( userId = activeAccountStore.activeUserId(), - styleId = event.style?.id, - avatarGlow = event.avatarGlow, - customBadge = event.customBadge, - inLeaderboard = event.inLeaderboard, - editedShoutout = event.editedShoutout, + updateProfileRequest = UpdatePrimalLegendProfileRequest( + styleId = event.style?.id, + avatarGlow = event.avatarGlow, + customBadge = event.customBadge, + inLeaderboard = event.inLeaderboard, + editedShoutout = event.editedShoutout, + ), ) userRepository.fetchAndUpdateUserAccount(userId = activeAccountStore.activeUserId()) } catch (error: WssException) { diff --git a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt index 08fb64e87..1bf1e003d 100644 --- a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt +++ b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt @@ -99,21 +99,11 @@ class PremiumRepository @Inject constructor( suspend fun updateLegendProfile( userId: String, - styleId: String?, - customBadge: Boolean?, - avatarGlow: Boolean?, - inLeaderboard: Boolean?, - editedShoutout: String?, + updateProfileRequest: UpdatePrimalLegendProfileRequest, ) = withContext(dispatchers.io()) { premiumApi.updateLegendProfile( userId = userId, - updateProfileRequest = UpdatePrimalLegendProfileRequest( - styleId = styleId, - customBadge = customBadge, - avatarGlow = avatarGlow, - inLeaderboard = inLeaderboard, - editedShoutout = editedShoutout, - ), + updateProfileRequest = updateProfileRequest, ) } From 50a0f3838aea4d5be9ce2154b091dbf0a1a60414 Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 17:14:35 +0100 Subject: [PATCH 3/9] Update detekt baseline --- app/detekt-baseline.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index ced953660..069a93fb5 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -1,5 +1,6 @@ + ComplexCondition:NostrResources.kt$isNote() || isNoteUri() || isNEventUri() || isNEvent() CyclomaticComplexMethod:ArticleDetailsScreen.kt$@OptIn(ExperimentalLayoutApi::class) @Composable private fun ArticleContentWithComments( state: ArticleDetailsContract.UiState, detailsEventPublisher: (UiEvent) -> Unit, articleParts: List<ArticlePartRender>, listState: LazyListState = rememberLazyListState(), paddingValues: PaddingValues, onArticleCommentClick: (naddr: String) -> Unit, onArticleHashtagClick: (hashtag: String) -> Unit, onHighlightClick: (String) -> Unit, onZapOptionsClick: () -> Unit, noteCallbacks: NoteCallbacks, onGoToWallet: () -> Unit, onPostAction: ((FeedPostAction) -> Unit)? = null, onPostLongPressAction: ((FeedPostAction) -> Unit)? = null, onUiError: ((UiError) -> Unit)? = null, ) @@ -48,13 +49,12 @@ LongMethod:FeedNoteCard.kt$@Composable private fun FeedNote( data: FeedPostUi, fullWidthContent: Boolean, avatarSizeDp: Dp, avatarPaddingValues: PaddingValues, notePaddingValues: PaddingValues, enableTweetsMode: Boolean, headerSingleLine: Boolean, showReplyTo: Boolean, forceContentIndent: Boolean, expanded: Boolean, textSelectable: Boolean, showNoteStatCounts: Boolean, noteCallbacks: NoteCallbacks, onPostAction: ((FeedPostAction) -> Unit)? = null, onPostLongClickAction: ((FeedPostAction) -> Unit)? = null, contentFooter: @Composable () -> Unit = {}, ) LongMethod:FeedNoteCard.kt$@ExperimentalMaterial3Api @Composable private fun FeedNoteCard( data: FeedPostUi, state: NoteContract.UiState, eventPublisher: (UiEvent) -> Unit, modifier: Modifier = Modifier, shape: Shape = CardDefaults.shape, colors: CardColors = noteCardColors(), cardPadding: PaddingValues = PaddingValues(all = 0.dp), enableTweetsMode: Boolean = false, headerSingleLine: Boolean = true, fullWidthContent: Boolean = false, forceContentIndent: Boolean = false, drawLineAboveAvatar: Boolean = false, drawLineBelowAvatar: Boolean = false, expanded: Boolean = false, textSelectable: Boolean = false, showReplyTo: Boolean = true, noteOptionsMenuEnabled: Boolean = true, showNoteStatCounts: Boolean = true, noteCallbacks: NoteCallbacks = NoteCallbacks(), onGoToWallet: (() -> Unit)? = null, contentFooter: @Composable () -> Unit = {}, ) LongMethod:HomeFeedScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeFeedScreen( state: HomeFeedContract.UiState, onTopLevelDestinationChanged: (PrimalTopLevelDestination) -> Unit, onDrawerScreenClick: (DrawerScreenDestination) -> Unit, onDrawerQrCodeClick: () -> Unit, onSearchClick: () -> Unit, noteCallbacks: NoteCallbacks, onGoToWallet: () -> Unit, onNewPostClick: (content: TextFieldValue?) -> Unit, eventPublisher: (UiEvent) -> Unit, ) - LongMethod:LegendaryProfileCustomizationScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, eventPublisher: (LegendaryProfileCustomizationContract.UiEvent) -> Unit, onClose: () -> Unit, ) + LongMethod:LegendaryProfileCustomizationScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, eventPublisher: (UiEvent) -> Unit, onClose: () -> Unit, ) LongMethod:MessageConversationListScreen.kt$@Composable private fun ConversationListItem( conversation: MessageConversationUi, onConversationClick: (String) -> Unit, onProfileClick: (profileId: String) -> Unit, ) LongMethod:MessageConversationListScreen.kt$@Composable private fun MessagesTabs( relation: ConversationRelation, onFollowsTabClick: () -> Unit, onOtherTabClick: () -> Unit, onMarkAllRead: () -> Unit, ) LongMethod:MultipleUserPicker.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun MultipleUserPicker( modifier: Modifier = Modifier, sheetTitle: String, placeholderText: String, onDismissRequest: () -> Unit, onUsersSelected: (Set<UserProfileItemUi>) -> Unit, sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), startingSelectedUsers: Set<UserProfileItemUi>, ) LongMethod:NostrUserText.kt$@Composable fun NostrUserText( displayName: String, internetIdentifier: String?, modifier: Modifier = Modifier, displayNameColor: Color = AppTheme.colorScheme.onSurface, fontSize: TextUnit = TextUnit.Unspecified, style: TextStyle = LocalTextStyle.current, overflow: TextOverflow = TextOverflow.Ellipsis, maxLines: Int = 1, internetIdentifierBadgeSize: Dp = 14.dp, internetIdentifierBadgeAlign: PlaceholderVerticalAlign = PlaceholderVerticalAlign.Center, legendaryCustomization: LegendaryCustomization? = null, annotatedStringPrefixBuilder: (AnnotatedString.Builder.() -> Unit)? = null, annotatedStringSuffixBuilder: (AnnotatedString.Builder.() -> Unit)? = null, ) LongMethod:NoteActionsRow.kt$@Composable fun FeedNoteActionsRow( modifier: Modifier, eventStats: EventStatsUi, isBookmarked: Boolean, highlightedNote: Boolean = false, showBookmark: Boolean = false, showCounts: Boolean = true, onPostAction: ((FeedPostAction) -> Unit)? = null, onPostLongPressAction: ((FeedPostAction) -> Unit)? = null, ) - LongMethod:NoteAttachments.kt$@ExperimentalFoundationApi @Composable fun NoteAttachments( modifier: Modifier = Modifier, attachments: List<NoteAttachmentUi>, blossoms: List<String>, onUrlClick: ((mediaUrl: String) -> Unit)? = null, onMediaClick: ((MediaClickEvent) -> Unit)? = null, ) LongMethod:NoteContent.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun NoteContent( modifier: Modifier = Modifier, data: NoteContentUi, expanded: Boolean, noteCallbacks: NoteCallbacks, maxLines: Int = Int.MAX_VALUE, overflow: TextOverflow = TextOverflow.Clip, enableTweetsMode: Boolean = false, textSelectable: Boolean = false, referencedEventsHaveBorder: Boolean = false, highlightColor: Color = AppTheme.colorScheme.secondary, contentColor: Color = AppTheme.colorScheme.onSurface, referencedEventsContainerColor: Color = AppTheme.extraColorScheme.surfaceVariantAlt1, onClick: ((offset: Offset) -> Unit)? = null, onUrlClick: ((url: String) -> Unit)? = null, ) LongMethod:NoteDropdownMenu.kt$@Composable fun NoteDropdownMenuIcon( modifier: Modifier, noteId: String, noteContent: String, noteRawData: String, authorId: String, isBookmarked: Boolean, enabled: Boolean = true, onBookmarkClick: (() -> Unit)? = null, onMuteUserClick: (() -> Unit)? = null, onReportContentClick: (() -> Unit)? = null, ) LongMethod:NoteEditorScreen.kt$@ExperimentalMaterial3Api @Composable private fun NoteEditorBox( state: NoteEditorContract.UiState, eventPublisher: (UiEvent) -> Unit, modifier: Modifier = Modifier, contentPadding: PaddingValues, ) From 53d029317166db2449a875d3aaeed990af68490c Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 17:15:40 +0100 Subject: [PATCH 4/9] Auto-format code --- .../premium/repository/PremiumRepository.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt index 1bf1e003d..3ae08fcf3 100644 --- a/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt +++ b/app/src/main/kotlin/net/primal/android/premium/repository/PremiumRepository.kt @@ -97,15 +97,13 @@ class PremiumRepository @Inject constructor( premiumApi.getOrdersHistory(userId = userId) } - suspend fun updateLegendProfile( - userId: String, - updateProfileRequest: UpdatePrimalLegendProfileRequest, - ) = withContext(dispatchers.io()) { - premiumApi.updateLegendProfile( - userId = userId, - updateProfileRequest = updateProfileRequest, - ) - } + suspend fun updateLegendProfile(userId: String, updateProfileRequest: UpdatePrimalLegendProfileRequest) = + withContext(dispatchers.io()) { + premiumApi.updateLegendProfile( + userId = userId, + updateProfileRequest = updateProfileRequest, + ) + } suspend fun fetchRecoveryContactsList(userId: String) = withContext(dispatchers.io()) { From 8a5b46330f50a6982313a7d8de52793376217b06 Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 20:19:55 +0100 Subject: [PATCH 5/9] Tidy up LegendaryProfileCustomizationScreen and fix some paddings --- .../LegendaryProfileCustomizationContract.kt | 4 +- .../LegendaryProfileCustomizationScreen.kt | 290 +++++++++--------- .../LegendaryProfileCustomizationViewModel.kt | 20 +- 3 files changed, 169 insertions(+), 145 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt index 794f196f2..3f47aeb41 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt @@ -10,10 +10,10 @@ interface LegendaryProfileCustomizationContract { data class UiState( val avatarCdnImage: CdnImage? = null, val membership: PremiumMembership? = null, - val avatarLegendaryCustomization: LegendaryCustomization? = null, + val avatarLegendaryCustomization: LegendaryCustomization = LegendaryCustomization(), val applyingChanges: Boolean = false, ) { - fun computeShoutout() = membership?.editedShoutout ?: avatarLegendaryCustomization?.currentShoutout ?: "" + fun computeShoutout() = membership?.editedShoutout ?: avatarLegendaryCustomization.currentShoutout ?: "" } sealed class UiEvent { diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt index 831513456..841b016f4 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import net.primal.android.R +import net.primal.android.attachments.domain.CdnImage import net.primal.android.core.compose.NostrUserText import net.primal.android.core.compose.PrimalDivider import net.primal.android.core.compose.PrimalSwitch @@ -60,13 +61,14 @@ import net.primal.android.core.compose.UniversalAvatarThumbnail import net.primal.android.core.compose.icons.PrimalIcons import net.primal.android.core.compose.icons.primaliconpack.ArrowBack import net.primal.android.core.compose.icons.primaliconpack.LegendaryProfileNoCustomization +import net.primal.android.premium.domain.PremiumMembership import net.primal.android.premium.legend.LegendaryCustomization import net.primal.android.premium.legend.LegendaryStyle import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.ui.PremiumBadge import net.primal.android.theme.AppTheme -const val SHOUTOUT_CHAR_LIMIT = 140 +private const val SHOUTOUT_CHAR_LIMIT = 140 @Composable fun LegendaryProfileCustomizationScreen(viewModel: LegendaryProfileCustomizationViewModel, onClose: () -> Unit) { @@ -81,25 +83,13 @@ fun LegendaryProfileCustomizationScreen(viewModel: LegendaryProfileCustomization @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LegendaryProfileCustomizationScreen( +private fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, eventPublisher: (UiEvent) -> Unit, onClose: () -> Unit, ) { val snackbarHostState = remember { SnackbarHostState() } - var customBadge by remember(state.avatarLegendaryCustomization?.customBadge) { - mutableStateOf(state.avatarLegendaryCustomization?.customBadge) - } - var avatarGlow by remember(state.avatarLegendaryCustomization?.avatarGlow) { - mutableStateOf(state.avatarLegendaryCustomization?.avatarGlow) - } - var selectedStyle by remember(state.avatarLegendaryCustomization?.legendaryStyle) { - mutableStateOf(state.avatarLegendaryCustomization?.legendaryStyle) - } - var inLeaderboard by remember(state.avatarLegendaryCustomization?.inLeaderboard) { - mutableStateOf(state.avatarLegendaryCustomization?.inLeaderboard) - } - var shoutout by remember(state.membership?.editedShoutout, state.avatarLegendaryCustomization?.currentShoutout) { + var shoutout by remember(state.membership?.editedShoutout, state.avatarLegendaryCustomization.currentShoutout) { mutableStateOf(state.computeShoutout()) } @@ -108,14 +98,13 @@ fun LegendaryProfileCustomizationScreen( } var editMode by remember { mutableStateOf(false) } - val textFieldState = - rememberSaveable( - state.membership?.editedShoutout, - state.avatarLegendaryCustomization?.currentShoutout, - saver = TextFieldState.Saver, - ) { - TextFieldState(initialText = state.computeShoutout()) - } + val textFieldState = rememberSaveable( + state.membership?.editedShoutout, + state.avatarLegendaryCustomization.currentShoutout, + saver = TextFieldState.Saver, + ) { + TextFieldState(initialText = state.computeShoutout()) + } fun cancelEditMode() { editMode = false @@ -163,101 +152,98 @@ fun LegendaryProfileCustomizationScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterVertically), ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - UniversalAvatarThumbnail( - avatarCdnImage = state.avatarCdnImage, - avatarSize = 80.dp, - legendaryCustomization = LegendaryCustomization( - avatarGlow = avatarGlow == true, - customBadge = customBadge == true, - legendaryStyle = selectedStyle, - ), - ) - Spacer(modifier = Modifier.height(16.dp)) - val primalName = state.membership?.premiumName ?: "" - NostrUserText( - modifier = Modifier.padding(start = 8.dp), - displayName = primalName, - internetIdentifier = "$primalName@primal.net", - internetIdentifierBadgeSize = 24.dp, - legendaryCustomization = LegendaryCustomization( - customBadge = customBadge == true, - legendaryStyle = selectedStyle, - ), - fontSize = 20.sp, - ) - } + ProfileSummary( + modifier = Modifier.padding(top = 36.dp), + avatarCdnImage = state.avatarCdnImage, + avatarGlow = state.avatarLegendaryCustomization.avatarGlow, + customBadge = state.avatarLegendaryCustomization.customBadge, + selectedStyle = state.avatarLegendaryCustomization.legendaryStyle, + membership = state.membership, + ) - if (state.membership != null) { - PremiumBadge( - firstCohort = state.membership.cohort1, - secondCohort = state.membership.cohort2, - membershipExpired = state.membership.isExpired(), - legendaryStyle = selectedStyle ?: LegendaryStyle.NO_CUSTOMIZATION, - ) + PrimalDivider(modifier = Modifier.padding(top = 16.dp)) - PrimalDivider(modifier = Modifier.padding(top = 16.dp)) + LegendaryColorPicker( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp, vertical = 8.dp), + activeLegendaryStyle = state.avatarLegendaryCustomization.legendaryStyle + ?: LegendaryStyle.NO_CUSTOMIZATION, + onStyleChanged = { eventPublisher(UiEvent.ApplyCustomization(style = it)) }, + ) - LegendaryColorPicker( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 32.dp, vertical = 8.dp), - activeLegendaryStyle = selectedStyle ?: LegendaryStyle.NO_CUSTOMIZATION, - onStyleChanged = { - selectedStyle = it - eventPublisher(UiEvent.ApplyCustomization(style = it)) - }, - ) + SwitchSettings( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + avatarRing = state.avatarLegendaryCustomization.avatarGlow == true, + onAvatarRingChanged = { eventPublisher(UiEvent.ApplyCustomization(avatarGlow = it)) }, + customBadge = state.avatarLegendaryCustomization.customBadge == true, + onCustomBadgeChanged = { eventPublisher(UiEvent.ApplyCustomization(customBadge = it)) }, + appearInLeaderboard = state.avatarLegendaryCustomization.inLeaderboard == true, + onAppearInLeaderboardChanged = { eventPublisher(UiEvent.ApplyCustomization(inLeaderboard = it)) }, + ) - SwitchSettings( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - avatarRing = avatarGlow == true, - onAvatarRingChanged = { - avatarGlow = it - eventPublisher(UiEvent.ApplyCustomization(avatarGlow = it)) - }, - customBadge = customBadge == true, - onCustomBadgeChanged = { - customBadge = it - eventPublisher(UiEvent.ApplyCustomization(customBadge = it)) - }, - appearInLeaderboard = inLeaderboard == true, - onAppearInLeaderboardChanged = { - inLeaderboard = it - eventPublisher(UiEvent.ApplyCustomization(inLeaderboard = it)) - }, - ) + Spacer(modifier = Modifier.height(16.dp)) + LegendCardShoutout( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + onShoutoutChanged = { shoutout = it }, + editMode = editMode, + onEditModeChange = { editMode = it }, + textFieldState = textFieldState, + editsInReview = editsInReview, + ) - Spacer(modifier = Modifier.height(24.dp)) - LegendCardShoutout( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - onShoutoutChanged = { shoutout = it }, - editMode = editMode, - onEditModeChange = { editMode = it }, - textFieldState = textFieldState, - ) - if (editsInReview) { - EditsInReviewBadge(modifier = Modifier.padding(bottom = 16.dp)) - } + BottomNotice(editsInReview = editsInReview) + } + } +} - Text( - color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, - text = if (editsInReview) { - stringResource(id = R.string.premium_legend_profile_customization_notice_in_review) - } else { - stringResource(id = R.string.premium_legend_profile_customization_notice) - }, - style = AppTheme.typography.bodySmall, - fontSize = 13.sp, - textAlign = TextAlign.Center, - ) - } +@Composable +private fun ProfileSummary( + modifier: Modifier = Modifier, + avatarCdnImage: CdnImage?, + avatarGlow: Boolean?, + customBadge: Boolean?, + selectedStyle: LegendaryStyle?, + membership: PremiumMembership?, +) { + val primalName = membership?.premiumName ?: "" + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + UniversalAvatarThumbnail( + avatarCdnImage = avatarCdnImage, + avatarSize = 80.dp, + legendaryCustomization = LegendaryCustomization( + avatarGlow = avatarGlow == true, + customBadge = customBadge == true, + legendaryStyle = selectedStyle, + ), + ) + NostrUserText( + modifier = Modifier.padding(start = 8.dp), + displayName = primalName, + internetIdentifier = "$primalName@primal.net", + internetIdentifierBadgeSize = 24.dp, + legendaryCustomization = LegendaryCustomization( + customBadge = customBadge == true, + legendaryStyle = selectedStyle, + ), + fontSize = 20.sp, + ) + + membership?.let { + PremiumBadge( + firstCohort = membership.cohort1, + secondCohort = membership.cohort2, + membershipExpired = membership.isExpired(), + legendaryStyle = selectedStyle ?: LegendaryStyle.NO_CUSTOMIZATION, + ) } } } @@ -266,15 +252,12 @@ fun LegendaryProfileCustomizationScreen( private fun LegendCardShoutout( modifier: Modifier = Modifier, textFieldState: TextFieldState, - onShoutoutChanged: (String) -> Unit, editMode: Boolean, + editsInReview: Boolean, + onShoutoutChanged: (String) -> Unit, onEditModeChange: (Boolean) -> Unit, ) { - Text( - text = stringResource(id = R.string.premium_legend_profile_customization_card_shoutout).uppercase(), - style = AppTheme.typography.bodyMedium, - fontSize = 14.sp, - ) + val bottomPadding = if (editMode) 28.dp else 12.dp LaunchedEffect(textFieldState) { snapshotFlow { textFieldState.text }.collect { if (it.length > SHOUTOUT_CHAR_LIMIT) { @@ -285,28 +268,35 @@ private fun LegendCardShoutout( } } - val bottomPadding = if (editMode) 28.dp else 12.dp + Text( + fontWeight = FontWeight.Medium, + text = stringResource(id = R.string.premium_legend_profile_customization_card_shoutout).uppercase(), + style = AppTheme.typography.bodyMedium, + fontSize = 14.sp, + ) - BasicTextField( - modifier = modifier - .clip(AppTheme.shapes.medium) - .background(AppTheme.extraColorScheme.surfaceVariantAlt1), - readOnly = !editMode, - state = textFieldState, - textStyle = AppTheme.typography.bodyMedium.copy( - color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, - textAlign = TextAlign.Center, - ), - decorator = { innerTextField -> - Box( - modifier = modifier - .padding(horizontal = 16.dp) - .padding(top = 28.dp, bottom = bottomPadding) - .clip(AppTheme.shapes.medium) - .background(AppTheme.extraColorScheme.surfaceVariantAlt1), - contentAlignment = Alignment.Center, - ) { + Column( + modifier = Modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically), + ) { + BasicTextField( + modifier = modifier + .clip(AppTheme.shapes.medium) + .background(AppTheme.extraColorScheme.surfaceVariantAlt1), + readOnly = !editMode, + state = textFieldState, + textStyle = AppTheme.typography.bodyMedium.copy( + color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, + textAlign = TextAlign.Center, + ), + decorator = { innerTextField -> Column( + modifier = modifier + .padding(horizontal = 16.dp) + .padding(top = 28.dp, bottom = bottomPadding) + .clip(AppTheme.shapes.medium) + .background(AppTheme.extraColorScheme.surfaceVariantAlt1), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically), ) { @@ -325,9 +315,12 @@ private fun LegendCardShoutout( } } } - } - }, - ) + }, + ) + if (editsInReview) { + EditsInReviewBadge(modifier = Modifier.padding(bottom = 16.dp)) + } + } } @Composable @@ -581,3 +574,18 @@ private fun BottomBarButtons( } } } + +@Composable +private fun BottomNotice(editsInReview: Boolean) { + Text( + color = AppTheme.extraColorScheme.onSurfaceVariantAlt2, + text = if (editsInReview) { + stringResource(id = R.string.premium_legend_profile_customization_notice_in_review) + } else { + stringResource(id = R.string.premium_legend_profile_customization_notice) + }, + style = AppTheme.typography.bodySmall, + fontSize = 13.sp, + textAlign = TextAlign.Center, + ) +} diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt index ec55997f1..1dd4c45ee 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.launch import net.primal.android.networking.sockets.errors.WssException import net.primal.android.premium.api.model.UpdatePrimalLegendProfileRequest +import net.primal.android.premium.legend.LegendaryCustomization import net.primal.android.premium.legend.asLegendaryCustomization import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiState @@ -55,6 +56,7 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( private fun applyCustomization(event: UiEvent.ApplyCustomization) { viewModelScope.launch { setState { copy(applyingChanges = true) } + event.optimisticallyUpdateCustomization() try { premiumRepository.updateLegendProfile( @@ -67,10 +69,10 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( editedShoutout = event.editedShoutout, ), ) - userRepository.fetchAndUpdateUserAccount(userId = activeAccountStore.activeUserId()) } catch (error: WssException) { Timber.e(error) } finally { + runCatching { userRepository.fetchAndUpdateUserAccount(userId = activeAccountStore.activeUserId()) } setState { copy(applyingChanges = false) } } } @@ -94,7 +96,7 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( setState { copy( avatarLegendaryCustomization = it.metadata?.primalPremiumInfo - ?.legendProfile?.asLegendaryCustomization(), + ?.legendProfile?.asLegendaryCustomization() ?: LegendaryCustomization(), ) } } @@ -106,4 +108,18 @@ class LegendaryProfileCustomizationViewModel @Inject constructor( profileRepository.requestProfileUpdate(profileId = activeAccountStore.activeUserId()) } } + + private fun UiEvent.ApplyCustomization.optimisticallyUpdateCustomization() { + val data = this + setState { + copy( + avatarLegendaryCustomization = avatarLegendaryCustomization.copy( + avatarGlow = data.avatarGlow ?: avatarLegendaryCustomization.avatarGlow, + customBadge = data.customBadge ?: avatarLegendaryCustomization.customBadge, + legendaryStyle = data.style ?: avatarLegendaryCustomization.legendaryStyle, + inLeaderboard = data.inLeaderboard ?: avatarLegendaryCustomization.inLeaderboard, + ), + ) + } + } } From caff51aca3340bc5bd1ce91f79057cf97c432d4c Mon Sep 17 00:00:00 2001 From: Marko Kocic Date: Fri, 31 Jan 2025 20:21:28 +0100 Subject: [PATCH 6/9] Update detekt baseline --- app/detekt-baseline.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index 069a93fb5..9bff6a380 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -49,7 +49,7 @@ LongMethod:FeedNoteCard.kt$@Composable private fun FeedNote( data: FeedPostUi, fullWidthContent: Boolean, avatarSizeDp: Dp, avatarPaddingValues: PaddingValues, notePaddingValues: PaddingValues, enableTweetsMode: Boolean, headerSingleLine: Boolean, showReplyTo: Boolean, forceContentIndent: Boolean, expanded: Boolean, textSelectable: Boolean, showNoteStatCounts: Boolean, noteCallbacks: NoteCallbacks, onPostAction: ((FeedPostAction) -> Unit)? = null, onPostLongClickAction: ((FeedPostAction) -> Unit)? = null, contentFooter: @Composable () -> Unit = {}, ) LongMethod:FeedNoteCard.kt$@ExperimentalMaterial3Api @Composable private fun FeedNoteCard( data: FeedPostUi, state: NoteContract.UiState, eventPublisher: (UiEvent) -> Unit, modifier: Modifier = Modifier, shape: Shape = CardDefaults.shape, colors: CardColors = noteCardColors(), cardPadding: PaddingValues = PaddingValues(all = 0.dp), enableTweetsMode: Boolean = false, headerSingleLine: Boolean = true, fullWidthContent: Boolean = false, forceContentIndent: Boolean = false, drawLineAboveAvatar: Boolean = false, drawLineBelowAvatar: Boolean = false, expanded: Boolean = false, textSelectable: Boolean = false, showReplyTo: Boolean = true, noteOptionsMenuEnabled: Boolean = true, showNoteStatCounts: Boolean = true, noteCallbacks: NoteCallbacks = NoteCallbacks(), onGoToWallet: (() -> Unit)? = null, contentFooter: @Composable () -> Unit = {}, ) LongMethod:HomeFeedScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeFeedScreen( state: HomeFeedContract.UiState, onTopLevelDestinationChanged: (PrimalTopLevelDestination) -> Unit, onDrawerScreenClick: (DrawerScreenDestination) -> Unit, onDrawerQrCodeClick: () -> Unit, onSearchClick: () -> Unit, noteCallbacks: NoteCallbacks, onGoToWallet: () -> Unit, onNewPostClick: (content: TextFieldValue?) -> Unit, eventPublisher: (UiEvent) -> Unit, ) - LongMethod:LegendaryProfileCustomizationScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, eventPublisher: (UiEvent) -> Unit, onClose: () -> Unit, ) + LongMethod:LegendaryProfileCustomizationScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable private fun LegendaryProfileCustomizationScreen( state: LegendaryProfileCustomizationContract.UiState, eventPublisher: (UiEvent) -> Unit, onClose: () -> Unit, ) LongMethod:MessageConversationListScreen.kt$@Composable private fun ConversationListItem( conversation: MessageConversationUi, onConversationClick: (String) -> Unit, onProfileClick: (profileId: String) -> Unit, ) LongMethod:MessageConversationListScreen.kt$@Composable private fun MessagesTabs( relation: ConversationRelation, onFollowsTabClick: () -> Unit, onOtherTabClick: () -> Unit, onMarkAllRead: () -> Unit, ) LongMethod:MultipleUserPicker.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun MultipleUserPicker( modifier: Modifier = Modifier, sheetTitle: String, placeholderText: String, onDismissRequest: () -> Unit, onUsersSelected: (Set<UserProfileItemUi>) -> Unit, sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), startingSelectedUsers: Set<UserProfileItemUi>, ) From e8f88489fef961dccbce59380b9782626d40687b Mon Sep 17 00:00:00 2001 From: Aleksandar Ilic Date: Fri, 31 Jan 2025 22:26:41 +0100 Subject: [PATCH 7/9] Fixed wrong imports during conflict resolution --- .../custimization/LegendaryProfileCustomizationScreen.kt | 4 ++-- .../custimization/LegendaryProfileCustomizationViewModel.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt index 841b016f4..aefff37d0 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt @@ -62,9 +62,9 @@ import net.primal.android.core.compose.icons.PrimalIcons import net.primal.android.core.compose.icons.primaliconpack.ArrowBack import net.primal.android.core.compose.icons.primaliconpack.LegendaryProfileNoCustomization import net.primal.android.premium.domain.PremiumMembership -import net.primal.android.premium.legend.LegendaryCustomization -import net.primal.android.premium.legend.LegendaryStyle import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent +import net.primal.android.premium.legend.domain.LegendaryCustomization +import net.primal.android.premium.legend.domain.LegendaryStyle import net.primal.android.premium.ui.PremiumBadge import net.primal.android.theme.AppTheme diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt index 1dd4c45ee..ffcf8718d 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.launch import net.primal.android.networking.sockets.errors.WssException import net.primal.android.premium.api.model.UpdatePrimalLegendProfileRequest -import net.primal.android.premium.legend.LegendaryCustomization -import net.primal.android.premium.legend.asLegendaryCustomization import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiState +import net.primal.android.premium.legend.domain.LegendaryCustomization +import net.primal.android.premium.legend.domain.asLegendaryCustomization import net.primal.android.premium.repository.PremiumRepository import net.primal.android.profile.repository.ProfileRepository import net.primal.android.user.accounts.active.ActiveAccountStore From 0cadf11d3339d0418802dd35ddaae594ef3cf805 Mon Sep 17 00:00:00 2001 From: Aleksandar Ilic Date: Fri, 31 Jan 2025 22:30:02 +0100 Subject: [PATCH 8/9] Fixed typo in package name --- .../net/primal/android/navigation/PrimalAppNavigation.kt | 4 ++-- .../LegendaryProfileCustomizationContract.kt | 2 +- .../LegendaryProfileCustomizationScreen.kt | 4 ++-- .../LegendaryProfileCustomizationViewModel.kt | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) rename app/src/main/kotlin/net/primal/android/premium/legend/{custimization => customization}/LegendaryProfileCustomizationContract.kt (94%) rename app/src/main/kotlin/net/primal/android/premium/legend/{custimization => customization}/LegendaryProfileCustomizationScreen.kt (99%) rename app/src/main/kotlin/net/primal/android/premium/legend/{custimization => customization}/LegendaryProfileCustomizationViewModel.kt (96%) diff --git a/app/src/main/kotlin/net/primal/android/navigation/PrimalAppNavigation.kt b/app/src/main/kotlin/net/primal/android/navigation/PrimalAppNavigation.kt index 25f52e278..eace55926 100644 --- a/app/src/main/kotlin/net/primal/android/navigation/PrimalAppNavigation.kt +++ b/app/src/main/kotlin/net/primal/android/navigation/PrimalAppNavigation.kt @@ -95,8 +95,8 @@ import net.primal.android.premium.legend.become.PremiumBecomeLegendScreen import net.primal.android.premium.legend.become.PremiumBecomeLegendViewModel import net.primal.android.premium.legend.card.LegendCardScreen import net.primal.android.premium.legend.card.LegendCardViewModel -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationScreen -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationViewModel +import net.primal.android.premium.legend.customization.LegendaryProfileCustomizationScreen +import net.primal.android.premium.legend.customization.LegendaryProfileCustomizationViewModel import net.primal.android.premium.legend.leaderboard.LegendLeaderboardScreen import net.primal.android.premium.legend.leaderboard.LegendLeaderboardViewModel import net.primal.android.premium.manage.PremiumManageContract diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationContract.kt similarity index 94% rename from app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt rename to app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationContract.kt index 6b4fe2e49..15f241e46 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationContract.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationContract.kt @@ -1,4 +1,4 @@ -package net.primal.android.premium.legend.custimization +package net.primal.android.premium.legend.customization import net.primal.android.attachments.domain.CdnImage import net.primal.android.premium.domain.PremiumMembership diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt similarity index 99% rename from app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt rename to app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt index aefff37d0..da9ea128e 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt @@ -1,4 +1,4 @@ -package net.primal.android.premium.legend.custimization +package net.primal.android.premium.legend.customization import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility @@ -62,7 +62,7 @@ import net.primal.android.core.compose.icons.PrimalIcons import net.primal.android.core.compose.icons.primaliconpack.ArrowBack import net.primal.android.core.compose.icons.primaliconpack.LegendaryProfileNoCustomization import net.primal.android.premium.domain.PremiumMembership -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent +import net.primal.android.premium.legend.customization.LegendaryProfileCustomizationContract.UiEvent import net.primal.android.premium.legend.domain.LegendaryCustomization import net.primal.android.premium.legend.domain.LegendaryStyle import net.primal.android.premium.ui.PremiumBadge diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationViewModel.kt similarity index 96% rename from app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt rename to app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationViewModel.kt index ffcf8718d..d917a2d30 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/custimization/LegendaryProfileCustomizationViewModel.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationViewModel.kt @@ -1,4 +1,4 @@ -package net.primal.android.premium.legend.custimization +package net.primal.android.premium.legend.customization import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -11,8 +11,8 @@ import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.launch import net.primal.android.networking.sockets.errors.WssException import net.primal.android.premium.api.model.UpdatePrimalLegendProfileRequest -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiEvent -import net.primal.android.premium.legend.custimization.LegendaryProfileCustomizationContract.UiState +import net.primal.android.premium.legend.customization.LegendaryProfileCustomizationContract.UiEvent +import net.primal.android.premium.legend.customization.LegendaryProfileCustomizationContract.UiState import net.primal.android.premium.legend.domain.LegendaryCustomization import net.primal.android.premium.legend.domain.asLegendaryCustomization import net.primal.android.premium.repository.PremiumRepository From 2bc5c6c02f618d093cae654b8aceba3ad10eec41 Mon Sep 17 00:00:00 2001 From: Aleksandar Ilic Date: Fri, 31 Jan 2025 22:33:19 +0100 Subject: [PATCH 9/9] Simplify expression --- .../customization/LegendaryProfileCustomizationScreen.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt index da9ea128e..516191d4c 100644 --- a/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt +++ b/app/src/main/kotlin/net/primal/android/premium/legend/customization/LegendaryProfileCustomizationScreen.kt @@ -176,15 +176,16 @@ private fun LegendaryProfileCustomizationScreen( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - avatarRing = state.avatarLegendaryCustomization.avatarGlow == true, + avatarRing = state.avatarLegendaryCustomization.avatarGlow, onAvatarRingChanged = { eventPublisher(UiEvent.ApplyCustomization(avatarGlow = it)) }, - customBadge = state.avatarLegendaryCustomization.customBadge == true, + customBadge = state.avatarLegendaryCustomization.customBadge, onCustomBadgeChanged = { eventPublisher(UiEvent.ApplyCustomization(customBadge = it)) }, appearInLeaderboard = state.avatarLegendaryCustomization.inLeaderboard == true, onAppearInLeaderboardChanged = { eventPublisher(UiEvent.ApplyCustomization(inLeaderboard = it)) }, ) Spacer(modifier = Modifier.height(16.dp)) + LegendCardShoutout( modifier = Modifier .fillMaxWidth()