Skip to content

Make the top views of the QE be reused by the scopes #615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions gravatar-quickeditor/api/gravatar-quickeditor.api
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ public final class com/gravatar/quickeditor/ui/editor/AvatarPickerContentLayout$
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/gravatar/quickeditor/ui/editor/ComposableSingletons$QuickEditorKt {
public static final field INSTANCE Lcom/gravatar/quickeditor/ui/editor/ComposableSingletons$QuickEditorKt;
public static field lambda-1 Lkotlin/jvm/functions/Function2;
public fun <init> ()V
public final fun getLambda-1$gravatar_quickeditor_release ()Lkotlin/jvm/functions/Function2;
}

public abstract class com/gravatar/quickeditor/ui/editor/GravatarQuickEditorDismissReason {
public static final field $stable I
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,15 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gravatar.extensions.defaultProfile
import com.gravatar.quickeditor.R
import com.gravatar.quickeditor.ui.components.AlertBanner
import com.gravatar.quickeditor.ui.components.AvatarDeletionConfirmationDialog
import com.gravatar.quickeditor.ui.components.AvatarOption
import com.gravatar.quickeditor.ui.components.AvatarsSection
import com.gravatar.quickeditor.ui.components.CtaSection
import com.gravatar.quickeditor.ui.components.DownloadManagerDisabledAlertDialog
import com.gravatar.quickeditor.ui.components.EmailLabel
import com.gravatar.quickeditor.ui.components.FailedAvatarUploadAlertDialog
import com.gravatar.quickeditor.ui.components.PermissionRationaleDialog
import com.gravatar.quickeditor.ui.components.ProfileCard
import com.gravatar.quickeditor.ui.components.QEPageDefault
import com.gravatar.quickeditor.ui.cropperlauncher.CropperLauncher
import com.gravatar.quickeditor.ui.cropperlauncher.UCropCropperLauncher
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
Expand All @@ -75,7 +71,6 @@ import com.gravatar.quickeditor.ui.withPermission
import com.gravatar.restapi.models.Avatar
import com.gravatar.types.Email
import com.gravatar.ui.GravatarTheme
import com.gravatar.ui.components.ComponentState
import com.yalantis.ucrop.UCrop
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -89,8 +84,8 @@ internal fun AvatarPicker(
handleExpiredSession: Boolean,
onAvatarSelected: () -> Unit,
onSessionExpired: () -> Unit,
onDoneClicked: () -> Unit,
onAltTextTapped: (email: String, avatarId: String) -> Unit,
onRefresh: () -> Unit,
viewModel: AvatarPickerViewModel = viewModel(
factory = AvatarPickerViewModelFactory(gravatarQuickEditorParams, handleExpiredSession),
),
Expand Down Expand Up @@ -130,23 +125,23 @@ internal fun AvatarPicker(
}
}

GravatarTheme {
QEPageDefault(
onDoneClicked = onDoneClicked,
content = {
Box(modifier = Modifier.wrapContentSize()) {
AvatarPicker(
uiState = uiState,
onEvent = viewModel::onEvent,
)
QESnackbarHost(
modifier = Modifier
.align(Alignment.BottomStart),
hostState = snackState,
)
}
},
)
Surface {
Box(modifier = Modifier.wrapContentSize()) {
AvatarPicker(
uiState = uiState,
onEvent = { event ->
viewModel.onEvent(event)
if (event is AvatarPickerEvent.Refresh) {
onRefresh()
}
},
)
QESnackbarHost(
modifier = Modifier
.align(Alignment.BottomStart),
hostState = snackState,
)
}
}
}

Expand Down Expand Up @@ -199,31 +194,19 @@ internal fun AvatarPicker(uiState: AvatarPickerUiState, onEvent: (AvatarPickerEv
),
) {
Column {
EmailLabel(
email = uiState.email,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
)
AnimatedVisibility(visible = uiState.nonSelectedAvatarAlertVisible) {
AlertBanner(
message = stringResource(id = R.string.gravatar_qe_alert_banner_no_avatar_selected),
onClose = { onEvent(AvatarPickerEvent.AvatarDeleteAlertDismissed) },
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 24.dp),
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
)
}
ProfileCard(
profile = uiState.profile,
email = uiState.email,
avatarCacheBuster = uiState.avatarCacheBuster.toString(),
modifier = Modifier.padding(horizontal = 16.dp),
)
Box(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
) {
val sectionModifier = Modifier.padding(top = 24.dp, bottom = 10.dp)
val sectionModifier = Modifier.padding(top = 8.dp, bottom = 10.dp)
when {
uiState.isLoading -> Box(
modifier = sectionModifier
Expand Down Expand Up @@ -490,13 +473,6 @@ private fun AvatarPickerPreview() {
AvatarPicker(
uiState = AvatarPickerUiState(
email = Email("[email protected]"),
profile = ComponentState.Loaded(
defaultProfile(
hash = "tetet",
displayName = "Henry Wallace",
location = "London, UK",
),
),
emailAvatars = EmailAvatars(
avatars = listOf(
Avatar {
Expand All @@ -523,7 +499,6 @@ private fun AvatarPickerLoadingPreview() {
AvatarPicker(
uiState = AvatarPickerUiState(
email = Email("[email protected]"),
profile = ComponentState.Loading,
isLoading = true,
avatarPickerContentLayout = AvatarPickerContentLayout.Horizontal,
emailAvatars = null,
Expand All @@ -540,7 +515,6 @@ private fun AvatarPickerErrorPreview() {
AvatarPicker(
uiState = AvatarPickerUiState(
email = Email("[email protected]"),
profile = null,
isLoading = false,
emailAvatars = null,
error = SectionError.ServerError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ package com.gravatar.quickeditor.ui.avatarpicker
import android.net.Uri
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
import com.gravatar.restapi.models.Avatar
import com.gravatar.restapi.models.Profile
import com.gravatar.services.ErrorType
import com.gravatar.types.Email
import com.gravatar.ui.components.ComponentState

internal data class AvatarPickerUiState(
val email: Email,
val avatarPickerContentLayout: AvatarPickerContentLayout,
val isLoading: Boolean = false,
val error: SectionError? = null,
val profile: ComponentState<Profile>? = null,
val emailAvatars: EmailAvatars? = null,
val selectingAvatarId: String? = null,
val uploadingAvatar: Uri? = null,
Expand All @@ -22,7 +19,6 @@ internal data class AvatarPickerUiState(
val failedUploadDialog: AvatarUploadFailure? = null,
val downloadManagerDisabled: Boolean = false,
val nonSelectedAvatarAlertVisible: Boolean = false,
val avatarCacheBuster: Long? = null,
) {
val avatarsSectionUiState: AvatarsSectionUiState? = emailAvatars?.mapToUiModel()?.let {
AvatarsSectionUiState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@ import com.gravatar.quickeditor.data.FileUtils
import com.gravatar.quickeditor.data.ImageDownloader
import com.gravatar.quickeditor.data.models.QuickEditorError
import com.gravatar.quickeditor.data.repository.AvatarRepository
import com.gravatar.quickeditor.data.repository.ProfileRepository
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
import com.gravatar.quickeditor.ui.editor.GravatarQuickEditorParams
import com.gravatar.quickeditor.ui.time.Clock
import com.gravatar.quickeditor.ui.time.SystemClock
import com.gravatar.restapi.models.Avatar
import com.gravatar.services.ErrorType
import com.gravatar.services.GravatarResult
import com.gravatar.types.Email
import com.gravatar.ui.components.ComponentState
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -39,18 +35,15 @@ internal class AvatarPickerViewModel(
private val email: Email,
private val handleExpiredSession: Boolean,
private val avatarPickerContentLayout: AvatarPickerContentLayout,
private val profileRepository: ProfileRepository,
private val avatarRepository: AvatarRepository,
private val imageDownloader: ImageDownloader,
private val fileUtils: FileUtils,
private val clock: Clock,
) : ViewModel() {
private val _uiState =
MutableStateFlow(
AvatarPickerUiState(
email = email,
avatarPickerContentLayout = avatarPickerContentLayout,
avatarCacheBuster = clock.getTimeMillis(),
),
)
val uiState: StateFlow<AvatarPickerUiState> = _uiState.asStateFlow()
Expand Down Expand Up @@ -186,9 +179,6 @@ internal class AvatarPickerViewModel(

private fun refresh() {
fetchAvatars()
if (uiState.value.profile !is ComponentState.Loaded) {
fetchProfile()
}
}

private fun selectAvatar(avatar: Avatar) {
Expand All @@ -203,7 +193,6 @@ internal class AvatarPickerViewModel(
_uiState.update { currentState ->
currentState.copy(
selectingAvatarId = null,
avatarCacheBuster = clock.getTimeMillis(),
)
}
_actions.send(AvatarPickerAction.AvatarSelected)
Expand Down Expand Up @@ -248,11 +237,6 @@ internal class AvatarPickerViewModel(
currentState.copy(
uploadingAvatar = null,
scrollToIndex = null,
avatarCacheBuster = if (avatar.selected == true) {
clock.getTimeMillis()
} else {
currentState.avatarCacheBuster
},
)
}
}
Expand All @@ -273,25 +257,6 @@ internal class AvatarPickerViewModel(
}
}

private fun fetchProfile() {
viewModelScope.launch {
_uiState.update { currentState -> currentState.copy(profile = ComponentState.Loading) }
when (val result = profileRepository.getProfile(email)) {
is GravatarResult.Success -> {
_uiState.update { currentState ->
currentState.copy(profile = ComponentState.Loaded(result.value))
}
}

is GravatarResult.Failure -> {
_uiState.update { currentState ->
currentState.copy(profile = null)
}
}
}
}
}

private fun fetchAvatars() {
viewModelScope.launch {
fetchAvatars(
Expand Down Expand Up @@ -395,15 +360,6 @@ internal class AvatarPickerViewModel(

private suspend fun notifyAvatarDeletedSuccessfully(isSelectedAvatar: Boolean) {
if (isSelectedAvatar) _actions.send(AvatarPickerAction.AvatarSelected)
_uiState.update { currentState ->
currentState.copy(
avatarCacheBuster = if (isSelectedAvatar) {
clock.getTimeMillis()
} else {
currentState.avatarCacheBuster
},
)
}
}

private fun hideNonSelectedAvatarAlert() {
Expand Down Expand Up @@ -474,11 +430,9 @@ internal class AvatarPickerViewModelFactory(
handleExpiredSession = handleExpiredSession,
email = gravatarQuickEditorParams.email,
avatarPickerContentLayout = gravatarQuickEditorParams.avatarPickerContentLayout,
profileRepository = QuickEditorContainer.getInstance().profileRepository,
avatarRepository = QuickEditorContainer.getInstance().avatarRepository,
imageDownloader = QuickEditorContainer.getInstance().imageDownloader,
fileUtils = QuickEditorContainer.getInstance().fileUtils,
clock = SystemClock(),
) as T
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.gravatar.quickeditor.ui.alttext.AltTextPage
import com.gravatar.quickeditor.ui.avatarpicker.AvatarPicker
import com.gravatar.quickeditor.ui.navigation.EditorNavDestinations
import com.gravatar.quickeditor.ui.navigation.QuickEditorPage
import com.gravatar.quickeditor.ui.oauth.OAuthPage
Expand Down Expand Up @@ -156,7 +155,7 @@ private fun NavGraphBuilder.addAvatarPickerGraph(
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start) + shrinkVertically()
},
) {
AvatarPicker(
QuickEditor(
gravatarQuickEditorParams = gravatarQuickEditorParams,
handleExpiredSession = handleExpiredSession,
onAvatarSelected = onAvatarSelected,
Expand Down
Loading