Skip to content

Only display the input fields that are configured in the scope #631

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 7 commits into from
May 19, 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
1 change: 1 addition & 0 deletions demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ dependencies {
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.ucrop)
implementation(libs.composables.core)
implementation(project(":gravatar"))
implementation(project(":gravatar-ui"))
implementation(project(":gravatar-quickeditor"))
Expand Down
47 changes: 45 additions & 2 deletions demo-app/src/main/java/com/gravatar/demoapp/ui/AvatarUpdateTab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -24,6 +25,7 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -46,8 +48,10 @@ import androidx.compose.ui.unit.dp
import com.gravatar.demoapp.BuildConfig
import com.gravatar.demoapp.R
import com.gravatar.demoapp.ui.activity.QuickEditorTestActivity
import com.gravatar.demoapp.ui.components.AboutFieldsBottomSheet
import com.gravatar.demoapp.ui.components.GravatarEmailInput
import com.gravatar.demoapp.ui.components.GravatarPasswordInput
import com.gravatar.demoapp.ui.components.translatedValue
import com.gravatar.quickeditor.GravatarQuickEditor
import com.gravatar.quickeditor.ui.editor.AboutEditorConfiguration
import com.gravatar.quickeditor.ui.editor.AboutEditorResult
Expand Down Expand Up @@ -81,9 +85,13 @@ fun AvatarUpdateTab(modifier: Modifier = Modifier) {
var tokenVisible by remember { mutableStateOf(false) }
val context = LocalContext.current
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
var showAboutFieldsPicker by rememberSaveable { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
var cacheBuster: String? by remember { mutableStateOf(null) }
val scrollState: ScrollState = rememberScrollState()
var aboutFields: Set<AboutInputField> by rememberSaveable(stateSaver = AboutInputFieldSetSaver) {
mutableStateOf(AboutInputField.all)
}
var pickerContentLayout: AvatarPickerContentLayout by rememberSaveable(
stateSaver = AvatarPickerContentLayoutSaver,
) {
Expand Down Expand Up @@ -184,6 +192,27 @@ fun AvatarUpdateTab(modifier: Modifier = Modifier) {
modifier = Modifier.fillMaxWidth(),
)
}
AnimatedVisibility(
visible = editorScope == QuickEditorScope.AvatarAndAbout || editorScope == QuickEditorScope.About,
) {
TextField(
enabled = false,
value = aboutFields.joinToString(", ") { it.translatedValue(context) },
maxLines = 1,
singleLine = true,
onValueChange = { },
colors = TextFieldDefaults.colors(
disabledTextColor = MaterialTheme.colorScheme.onSurface,
disabledLabelColor = MaterialTheme.colorScheme.onSurface,
),
label = { Text("About fields") },
modifier = Modifier
.clickable {
showAboutFieldsPicker = true
}
.fillMaxWidth(),
)
}
Button(
onClick = {
keyboardController?.hide()
Expand Down Expand Up @@ -222,6 +251,15 @@ fun AvatarUpdateTab(modifier: Modifier = Modifier) {
}
}
}
if (showAboutFieldsPicker) {
AboutFieldsBottomSheet(
aboutFields = aboutFields,
onDismiss = { showAboutFieldsPicker = false },
onFieldsChanged = { fields ->
aboutFields = fields
},
)
}
if (showBottomSheet) {
val authenticationMethod = if (useToken) {
AuthenticationMethod.Bearer(userToken)
Expand Down Expand Up @@ -256,15 +294,15 @@ fun AvatarUpdateTab(modifier: Modifier = Modifier) {

QuickEditorScope.About -> QuickEditorScopeOption.aboutEditor(
config = AboutEditorConfiguration(
fields = AboutInputField.all,
fields = aboutFields,
),
)

QuickEditorScope.AvatarAndAbout -> QuickEditorScopeOption.avatarAndAbout(
config = AvatarPickerAndAboutEditorConfiguration(
contentLayout = pickerContentLayout,
initialPage = editorInitialPage,
fields = AboutInputField.all,
fields = aboutFields,
),
)
}
Expand Down Expand Up @@ -466,6 +504,11 @@ private fun InitialPageDropdown(
}
}

private val AboutInputFieldSetSaver = Saver<Set<AboutInputField>, List<AboutInputField>>(
save = { it.toList() },
restore = { it.toSet() },
)

private val AvatarPickerContentLayoutSaver: Saver<AvatarPickerContentLayout, String> = run {
val horizontalKey = "horizontal"
val verticalKey = "vertical"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.gravatar.demoapp.ui.components

import android.content.Context
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.composables.core.ModalBottomSheet
import com.composables.core.ModalSheetProperties
import com.composables.core.Scrim
import com.composables.core.Sheet
import com.composables.core.SheetDetent
import com.composables.core.rememberModalBottomSheetState
import com.composeunstyled.Thumb
import com.composeunstyled.ToggleSwitch
import com.gravatar.quickeditor.ui.editor.AboutInputField

@Composable
internal fun AboutFieldsBottomSheet(
aboutFields: Set<AboutInputField>,
onFieldsChanged: (Set<AboutInputField>) -> Unit,
onDismiss: () -> Unit = {},
) {
ModalBottomSheet(
state = rememberModalBottomSheetState(
initialDetent = SheetDetent.FullyExpanded,
),
onDismiss = onDismiss,
properties = ModalSheetProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true,
),
) {
Scrim(
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f),
)
Sheet(
modifier = Modifier
.imePadding()
.clip(RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp))
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
.widthIn(max = 640.dp)
.fillMaxWidth()
.padding(
WindowInsets.navigationBars
.only(WindowInsetsSides.Vertical)
.asPaddingValues(),
),
) {
Surface(
modifier = Modifier
.padding(24.dp)
.fillMaxWidth(),
tonalElevation = 1.dp,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
AboutInputField.all.forEach { field ->
Row(
modifier = Modifier
.padding(4.dp)
.fillMaxWidth(),
) {
val fieldsChecked = aboutFields.contains(field)

val animatedColor by animateColorAsState(
if (fieldsChecked) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.inversePrimary
},
)
Text(
text = field.translatedValue(LocalContext.current),
modifier = Modifier
.weight(1f),
)
ToggleSwitch(
toggled = fieldsChecked,
backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
shape = RoundedCornerShape(100),
contentPadding = PaddingValues(2.dp),
onToggled = { toggled ->
if (toggled) {
onFieldsChanged(aboutFields + field)
} else {
onFieldsChanged(aboutFields - field)
}
},
thumb = {
Thumb(
shape = CircleShape,
color = animatedColor,
modifier = Modifier.shadow(elevation = 4.dp, CircleShape),
)
},
)
}
}
}
}
}
}
}

internal fun AboutInputField.translatedValue(context: Context): String {
return when (this) {
AboutInputField.DisplayName -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_display_name
AboutInputField.AboutMe -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_about_me
AboutInputField.Pronouns -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_pronouns
AboutInputField.Pronunciation -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_pronunciation
AboutInputField.Location -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_location
AboutInputField.JobTitle -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_job_title
AboutInputField.Company -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_company
else -> com.gravatar.quickeditor.R.string.gravatar_qe_about_field_label_display_name
}.let { context.getString(it) }
}

@Preview
@Composable
private fun AboutFieldsBottomSheetPreview() {
AboutFieldsBottomSheet(
aboutFields = setOf(
AboutInputField.DisplayName,
AboutInputField.AboutMe,
AboutInputField.Pronouns,
AboutInputField.Pronunciation,
AboutInputField.Location,
AboutInputField.JobTitle,
AboutInputField.Company,
),
onFieldsChanged = {},
)
}
2 changes: 2 additions & 0 deletions gravatar-quickeditor/api/gravatar-quickeditor.api
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ public final class com/gravatar/quickeditor/ui/abouteditor/components/Composable
public final class com/gravatar/quickeditor/ui/abouteditor/components/ComposableSingletons$AboutSectionKt {
public static final field INSTANCE Lcom/gravatar/quickeditor/ui/abouteditor/components/ComposableSingletons$AboutSectionKt;
public static field lambda-1 Lkotlin/jvm/functions/Function2;
public static field lambda-2 Lkotlin/jvm/functions/Function2;
public fun <init> ()V
public final fun getLambda-1$gravatar_quickeditor_release ()Lkotlin/jvm/functions/Function2;
public final fun getLambda-2$gravatar_quickeditor_release ()Lkotlin/jvm/functions/Function2;
}

public final class com/gravatar/quickeditor/ui/abouteditor/components/ComposableSingletons$DiscardChangesAlertDialogKt {
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.
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 @@ -34,6 +34,7 @@ import com.gravatar.quickeditor.R
import com.gravatar.quickeditor.ui.abouteditor.components.AboutSection
import com.gravatar.quickeditor.ui.abouteditor.components.DiscardChangesAlertDialog
import com.gravatar.quickeditor.ui.components.QEButton
import com.gravatar.quickeditor.ui.editor.AboutInputField
import com.gravatar.quickeditor.ui.editor.GravatarQuickEditorParams
import com.gravatar.quickeditor.ui.extensions.QESnackbarHost
import com.gravatar.quickeditor.ui.extensions.SnackbarType
Expand Down Expand Up @@ -130,7 +131,7 @@ internal fun AboutEditor(
@Composable
internal fun AboutEditor(
uiState: AboutEditorUiState,
onValueChange: (AboutInputField) -> Unit,
onValueChange: (AboutEditorField) -> Unit,
onSaveClick: () -> Unit,
) {
Surface {
Expand Down Expand Up @@ -163,7 +164,7 @@ internal fun AboutEditor(
formEnabled = uiState.formEnabled,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
.padding(16.dp),
onValueChange = onValueChange,
)
}
Expand All @@ -189,17 +190,36 @@ internal fun AboutEditorLoadedPreview() {
Box(modifier = Modifier.padding(10.dp)) {
AboutEditor(
uiState = AboutEditorUiState(
aboutFields = AboutFields(
personal = PersonalFields(
aboutMe = AboutInputField.Personal.AboutMe(value = "My description"),
displayName = AboutInputField.Personal.DisplayName(value = "John Doe"),
location = AboutInputField.Personal.Location(value = "San Francisco, CA"),
pronunciation = AboutInputField.Personal.Pronunciation(value = "John Doe"),
pronouns = AboutInputField.Personal.Pronouns(value = "he/him"),
aboutFields = setOf(
AboutEditorField(
type = AboutInputField.DisplayName,
value = "John Doe",
maxLines = 1,
),
professional = ProfessionalFields(
company = AboutInputField.Professional.Company(value = "Automattic"),
jobTitle = AboutInputField.Professional.JobTitle(value = "Software Engineer"),
AboutEditorField(
type = AboutInputField.AboutMe,
value = "My description",
maxLines = 3,
),
AboutEditorField(
type = AboutInputField.Pronunciation,
value = "John Doe",
),
AboutEditorField(
type = AboutInputField.Pronouns,
value = "he/him",
),
AboutEditorField(
type = AboutInputField.Location,
value = "San Francisco, CA",
),
AboutEditorField(
type = AboutInputField.Company,
value = "Automattic",
),
AboutEditorField(
type = AboutInputField.JobTitle,
value = "Software Engineer",
),
),
),
Expand All @@ -217,7 +237,7 @@ internal fun AboutEditorLoadingPreview() {
Box(modifier = Modifier.padding(10.dp)) {
AboutEditor(
uiState = AboutEditorUiState(
aboutFields = AboutFields.EMPTY,
aboutFields = emptySet(),
isLoading = true,
),
onValueChange = { },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.gravatar.quickeditor.ui.abouteditor

internal sealed class AboutEditorEvent {
data class OnAboutFieldUpdated(
val aboutField: AboutInputField,
val aboutField: AboutEditorField,
) : AboutEditorEvent()

data object OnSaveClicked : AboutEditorEvent()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.gravatar.quickeditor.ui.abouteditor

import com.gravatar.quickeditor.ui.editor.AboutInputField

internal data class AboutEditorField(
val type: AboutInputField,
val value: String,
val maxLines: Int = 1,
)
Loading