Skip to content

Commit 773bad8

Browse files
authored
Merge pull request #168 from Automattic/hamorillo/167-generic-state
Refactor `UserProfileState` to a generic state
2 parents 832d295 + c5e5273 commit 773bad8

18 files changed

Lines changed: 113 additions & 107 deletions

File tree

demo-app/src/main/java/com/gravatar/demoapp/ui/DemoGravatarApp.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import com.gravatar.AvatarQueryOptions
5656
import com.gravatar.AvatarUrl
5757
import com.gravatar.DefaultAvatarOption
5858
import com.gravatar.ImageRating
59+
import com.gravatar.api.models.Profile
5960
import com.gravatar.demoapp.BuildConfig
6061
import com.gravatar.demoapp.R
6162
import com.gravatar.demoapp.theme.GravatarDemoAppTheme
@@ -68,11 +69,11 @@ import com.gravatar.services.ProfileService
6869
import com.gravatar.services.Result
6970
import com.gravatar.types.Email
7071
import com.gravatar.ui.GravatarTheme
72+
import com.gravatar.ui.components.ComponentState
7173
import com.gravatar.ui.components.LargeProfile
7274
import com.gravatar.ui.components.LargeProfileSummary
7375
import com.gravatar.ui.components.Profile
7476
import com.gravatar.ui.components.ProfileSummary
75-
import com.gravatar.ui.components.UserProfileState
7677
import com.gravatar.ui.gravatarTheme
7778
import kotlinx.coroutines.CoroutineScope
7879
import kotlinx.coroutines.launch
@@ -171,7 +172,7 @@ private enum class ThemeOptions {
171172
}
172173

173174
@Composable
174-
private fun ProfileComponents(profileState: UserProfileState?, theme: ThemeOptions, error: String) {
175+
private fun ProfileComponents(profileState: ComponentState<Profile>?, theme: ThemeOptions, error: String) {
175176
val configuration = Configuration(LocalConfiguration.current).apply {
176177
uiMode = when (theme) {
177178
ThemeOptions.LIGHT -> Configuration.UI_MODE_NIGHT_NO
@@ -223,7 +224,7 @@ private fun ProfileComponents(profileState: UserProfileState?, theme: ThemeOptio
223224
@Composable
224225
private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwable?) -> Unit) {
225226
var email by remember { mutableStateOf(BuildConfig.DEMO_EMAIL, neverEqualPolicy()) }
226-
var profileState: UserProfileState? by remember { mutableStateOf(null, neverEqualPolicy()) }
227+
var profileState: ComponentState<Profile>? by remember { mutableStateOf(null, neverEqualPolicy()) }
227228
var error by remember { mutableStateOf("") }
228229
val profileService = ProfileService()
229230
val keyboardController = LocalSoftwareKeyboardController.current
@@ -252,18 +253,18 @@ private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwab
252253
keyboardController?.hide()
253254
scope.launch {
254255
error = ""
255-
profileState = UserProfileState.Loading
256+
profileState = ComponentState.Loading
256257
when (val result = profileService.fetch(Email(email))) {
257258
is Result.Success -> {
258259
result.value.let {
259-
profileState = UserProfileState.Loaded(it)
260+
profileState = ComponentState.Loaded(it)
260261
}
261262
}
262263

263264
is Result.Failure -> {
264265
when (result.error) {
265266
ErrorType.NOT_FOUND -> {
266-
profileState = UserProfileState.Empty
267+
profileState = ComponentState.Empty
267268
}
268269

269270
else -> {
@@ -278,9 +279,9 @@ private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwab
278279
) { Text(text = stringResource(R.string.button_get_profile)) }
279280
Spacer(modifier = Modifier.width(4.dp))
280281
Button(
281-
enabled = profileState !is UserProfileState.Loading,
282+
enabled = profileState !is ComponentState.Loading,
282283
onClick = {
283-
profileState = UserProfileState.Loading
284+
profileState = ComponentState.Loading
284285
},
285286
) {
286287
Text(text = stringResource(R.string.button_enable_loading_state))
@@ -313,9 +314,9 @@ private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwab
313314
}
314315
Spacer(modifier = Modifier.height(16.dp))
315316
profileState?.let { state ->
316-
if (state is UserProfileState.Loaded) {
317+
if (state is ComponentState.Loaded) {
317318
ExpandableSection(title = stringResource(R.string.raw_profile_title)) {
318-
Text(text = state.userProfile.prettyPrint())
319+
Text(text = state.loadedValue.prettyPrint())
319320
}
320321
Spacer(modifier = Modifier.height(16.dp))
321322
}

gravatar-ui/src/main/java/com/gravatar/ui/components/UserProfileState.kt renamed to gravatar-ui/src/main/java/com/gravatar/ui/components/ComponentState.kt

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,39 @@ import com.gravatar.api.models.Profile
1414
import com.gravatar.api.models.VerifiedAccount
1515
import com.gravatar.extensions.emptyProfile
1616
import com.gravatar.ui.GravatarTheme
17-
import com.gravatar.ui.components.UserProfileState.Loaded
18-
import com.gravatar.ui.components.UserProfileState.Loading
17+
import com.gravatar.ui.components.ComponentState.Loaded
18+
import com.gravatar.ui.components.ComponentState.Loading
1919
import kotlinx.coroutines.delay
2020
import java.net.URI
2121

2222
/**
23-
* [UserProfileState] represents the state of a user profile loading.
23+
* [ComponentState] represents the state of a user profile loading.
2424
* It can be in a [Loading] state or a [Loaded] state.
2525
*/
26-
public sealed class UserProfileState {
26+
public sealed class ComponentState<out T> {
2727
/**
28-
* [Loading] represents the state where the user profile is still loading.
28+
* [Loading] represents the state where the data is still loading.
2929
*/
30-
public data object Loading : UserProfileState()
30+
public data object Loading : ComponentState<Nothing>()
3131

3232
/**
33-
* [Loaded] represents the state where the user profile has been loaded.
33+
* [Loaded] represents the state where the user data has been loaded.
3434
*
35-
* @property userProfile The user's profile information
35+
* @param T the type of the information to load
36+
* @property loadedValue The loaded information
3637
*/
37-
public data class Loaded(val userProfile: Profile) : UserProfileState()
38+
public data class Loaded<T>(val loadedValue: T) : ComponentState<T>()
3839

3940
/**
40-
* [Empty] represents the state where the user profile is empty, so it can be claimed.
41+
* [Empty] represents the state where the data is empty
4142
*/
42-
public data object Empty : UserProfileState()
43+
public data object Empty : ComponentState<Nothing>()
4344
}
4445

4546
@Preview
4647
@Composable
47-
public fun LoadingToLoadedStatePreview(composable: @Composable (state: UserProfileState) -> Unit = {}) {
48-
var state: UserProfileState by remember { mutableStateOf(Loading) }
48+
public fun LoadingToLoadedStatePreview(composable: @Composable (state: ComponentState<Profile>) -> Unit = {}) {
49+
var state: ComponentState<Profile> by remember { mutableStateOf(Loading) }
4950
LaunchedEffect(key1 = state) {
5051
delay(5000)
5152
state = Loaded(

gravatar-ui/src/main/java/com/gravatar/ui/components/LargeProfile.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import java.net.URI
3535
*/
3636
@Composable
3737
public fun LargeProfile(profile: Profile, modifier: Modifier = Modifier) {
38-
LargeProfile(state = UserProfileState.Loaded(profile), modifier = modifier)
38+
LargeProfile(state = ComponentState.Loaded(profile), modifier = modifier)
3939
}
4040

4141
/**
@@ -46,7 +46,7 @@ public fun LargeProfile(profile: Profile, modifier: Modifier = Modifier) {
4646
* @param modifier Composable modifier
4747
*/
4848
@Composable
49-
public fun LargeProfile(state: UserProfileState, modifier: Modifier = Modifier) {
49+
public fun LargeProfile(state: ComponentState<Profile>, modifier: Modifier = Modifier) {
5050
GravatarTheme {
5151
EmptyProfileClickableContainer(state) {
5252
Column(
@@ -125,7 +125,7 @@ public fun DisplayNamePreview() {
125125
private fun ProfileEmptyPreview() {
126126
GravatarTheme {
127127
Surface(Modifier.fillMaxWidth()) {
128-
LargeProfile(UserProfileState.Empty)
128+
LargeProfile(ComponentState.Empty)
129129
}
130130
}
131131
}

gravatar-ui/src/main/java/com/gravatar/ui/components/LargeProfileSummary.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import java.net.URI
3333
*/
3434
@Composable
3535
public fun LargeProfileSummary(profile: Profile, modifier: Modifier = Modifier) {
36-
LargeProfileSummary(UserProfileState.Loaded(profile), modifier)
36+
LargeProfileSummary(ComponentState.Loaded(profile), modifier)
3737
}
3838

3939
/**
@@ -44,7 +44,7 @@ public fun LargeProfileSummary(profile: Profile, modifier: Modifier = Modifier)
4444
* @param modifier Composable modifier
4545
*/
4646
@Composable
47-
public fun LargeProfileSummary(state: UserProfileState, modifier: Modifier = Modifier) {
47+
public fun LargeProfileSummary(state: ComponentState<Profile>, modifier: Modifier = Modifier) {
4848
GravatarTheme {
4949
EmptyProfileClickableContainer(state) {
5050
Column(
@@ -122,7 +122,7 @@ public fun LargeProfileLoadingPreview() {
122122
private fun ProfileEmptyPreview() {
123123
GravatarTheme {
124124
Surface(Modifier.fillMaxWidth()) {
125-
LargeProfileSummary(UserProfileState.Empty)
125+
LargeProfileSummary(ComponentState.Empty)
126126
}
127127
}
128128
}

gravatar-ui/src/main/java/com/gravatar/ui/components/Profile.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import java.net.URI
3939
*/
4040
@Composable
4141
public fun Profile(profile: Profile, modifier: Modifier = Modifier) {
42-
Profile(state = UserProfileState.Loaded(profile), modifier = modifier)
42+
Profile(state = ComponentState.Loaded(profile), modifier = modifier)
4343
}
4444

4545
/**
@@ -50,7 +50,7 @@ public fun Profile(profile: Profile, modifier: Modifier = Modifier) {
5050
* @param modifier Composable modifier
5151
*/
5252
@Composable
53-
public fun Profile(state: UserProfileState, modifier: Modifier = Modifier) {
53+
public fun Profile(state: ComponentState<Profile>, modifier: Modifier = Modifier) {
5454
GravatarTheme {
5555
EmptyProfileClickableContainer(state) {
5656
Column(
@@ -138,7 +138,7 @@ private fun ProfileLoadingPreview() {
138138
private fun ProfileEmptyPreview() {
139139
GravatarTheme {
140140
Surface(Modifier.fillMaxWidth()) {
141-
Profile(UserProfileState.Empty)
141+
Profile(ComponentState.Empty)
142142
}
143143
}
144144
}

gravatar-ui/src/main/java/com/gravatar/ui/components/ProfileComponentsUtils.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import androidx.compose.runtime.Composable
66
import androidx.compose.ui.Modifier
77
import androidx.compose.ui.platform.LocalUriHandler
88
import com.gravatar.GravatarConstants
9+
import com.gravatar.api.models.Profile
910

1011
@Composable
11-
internal fun EmptyProfileClickableContainer(userProfileState: UserProfileState?, content: @Composable () -> Unit) {
12-
if (userProfileState is UserProfileState.Empty) {
12+
internal fun EmptyProfileClickableContainer(
13+
userProfileState: ComponentState<Profile>?,
14+
content: @Composable () -> Unit,
15+
) {
16+
if (userProfileState is ComponentState.Empty) {
1317
Box(Modifier.emptyProfileClick(userProfileState)) {
1418
content()
1519
}
@@ -19,8 +23,8 @@ internal fun EmptyProfileClickableContainer(userProfileState: UserProfileState?,
1923
}
2024

2125
@Composable
22-
private fun Modifier.emptyProfileClick(userProfileState: UserProfileState?): Modifier {
23-
return if (userProfileState is UserProfileState.Empty) {
26+
private fun Modifier.emptyProfileClick(userProfileState: ComponentState<Profile>?): Modifier {
27+
return if (userProfileState is ComponentState.Empty) {
2428
val uriHandler = LocalUriHandler.current
2529
this.clickable {
2630
uriHandler.openUri(GravatarConstants.GRAVATAR_SIGN_IN_URL)

gravatar-ui/src/main/java/com/gravatar/ui/components/ProfileSummary.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import com.gravatar.ui.components.atomic.ViewProfileButton
3434
*/
3535
@Composable
3636
public fun ProfileSummary(profile: Profile, modifier: Modifier = Modifier) {
37-
ProfileSummary(state = UserProfileState.Loaded(profile), modifier = modifier)
37+
ProfileSummary(state = ComponentState.Loaded(profile), modifier = modifier)
3838
}
3939

4040
/**
@@ -45,7 +45,7 @@ public fun ProfileSummary(profile: Profile, modifier: Modifier = Modifier) {
4545
* @param modifier Composable modifier
4646
*/
4747
@Composable
48-
public fun ProfileSummary(state: UserProfileState, modifier: Modifier = Modifier) {
48+
public fun ProfileSummary(state: ComponentState<Profile>, modifier: Modifier = Modifier) {
4949
GravatarTheme {
5050
EmptyProfileClickableContainer(state) {
5151
Row(modifier = modifier) {
@@ -60,17 +60,17 @@ public fun ProfileSummary(state: UserProfileState, modifier: Modifier = Modifier
6060
textStyle = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
6161
)
6262
when (state) {
63-
is UserProfileState.Loaded -> {
64-
if (state.userProfile.location.isNotBlank()) {
63+
is ComponentState.Loaded -> {
64+
if (state.loadedValue.location.isNotBlank()) {
6565
Location(state)
6666
}
6767
}
6868

69-
UserProfileState.Loading -> {
69+
ComponentState.Loading -> {
7070
Location(state, modifier.width(120.dp))
7171
}
7272

73-
UserProfileState.Empty -> {
73+
ComponentState.Empty -> {
7474
Location(state)
7575
}
7676
}
@@ -109,7 +109,7 @@ private fun ProfileSummaryLoadingPreview() {
109109
private fun ProfileSummaryEmptyPreview() {
110110
GravatarTheme {
111111
Surface(Modifier.fillMaxWidth()) {
112-
ProfileSummary(UserProfileState.Empty)
112+
ProfileSummary(ComponentState.Empty)
113113
}
114114
}
115115
}

gravatar-ui/src/main/java/com/gravatar/ui/components/atomic/AboutMe.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import com.gravatar.api.models.Profile
2020
import com.gravatar.extensions.emptyProfile
2121
import com.gravatar.ui.R
2222
import com.gravatar.ui.TextSkeletonEffect
23+
import com.gravatar.ui.components.ComponentState
2324
import com.gravatar.ui.components.LoadingToLoadedStatePreview
24-
import com.gravatar.ui.components.UserProfileState
2525

2626
/**
2727
* [AboutMe] is a composable that displays a user's about me description.
@@ -53,23 +53,23 @@ public fun AboutMe(
5353
*/
5454
@Composable
5555
public fun AboutMe(
56-
state: UserProfileState,
56+
state: ComponentState<Profile>,
5757
modifier: Modifier = Modifier,
5858
textStyle: TextStyle = MaterialTheme.typography.bodyMedium,
5959
content: @Composable ((String, Modifier) -> Unit) = { userInfo, contentModifier ->
6060
AboutMeDefaultContent(userInfo, textStyle, contentModifier)
6161
},
6262
) {
6363
when (state) {
64-
is UserProfileState.Loading -> {
64+
is ComponentState.Loading -> {
6565
TextSkeletonEffect(textStyle = textStyle)
6666
}
6767

68-
is UserProfileState.Loaded -> {
69-
AboutMe(state.userProfile, modifier, textStyle, content)
68+
is ComponentState.Loaded -> {
69+
AboutMe(state.loadedValue, modifier, textStyle, content)
7070
}
7171

72-
UserProfileState.Empty -> {
72+
ComponentState.Empty -> {
7373
DashedBorder(modifier) {
7474
content.invoke(
7575
stringResource(id = R.string.empty_state_about_me),
@@ -125,7 +125,7 @@ private fun AboutMePreview() {
125125
@Preview
126126
@Composable
127127
private fun AboutMeEmptyState() {
128-
AboutMe(UserProfileState.Empty)
128+
AboutMe(ComponentState.Empty)
129129
}
130130

131131
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)

gravatar-ui/src/main/java/com/gravatar/ui/components/atomic/Avatar.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import com.gravatar.api.models.Profile
1515
import com.gravatar.extensions.avatarUrl
1616
import com.gravatar.extensions.emptyProfile
1717
import com.gravatar.ui.R
18+
import com.gravatar.ui.components.ComponentState
1819
import com.gravatar.ui.components.LoadingToLoadedStatePreview
19-
import com.gravatar.ui.components.UserProfileState
2020
import com.gravatar.ui.components.isNightModeEnabled
2121
import com.gravatar.ui.skeletonEffect
2222

@@ -67,30 +67,30 @@ private fun Avatar(model: Any?, size: Dp, modifier: Modifier) {
6767
*/
6868
@Composable
6969
public fun Avatar(
70-
state: UserProfileState,
70+
state: ComponentState<Profile>,
7171
size: Dp,
7272
modifier: Modifier = Modifier,
7373
avatarQueryOptions: AvatarQueryOptions? = null,
7474
) {
7575
when (state) {
76-
is UserProfileState.Loading -> {
76+
is ComponentState.Loading -> {
7777
Box(
7878
modifier = modifier
7979
.size(size)
8080
.skeletonEffect(),
8181
)
8282
}
8383

84-
is UserProfileState.Loaded -> {
84+
is ComponentState.Loaded -> {
8585
Avatar(
86-
profile = state.userProfile,
86+
profile = state.loadedValue,
8787
size = size,
8888
modifier = modifier,
8989
avatarQueryOptions = avatarQueryOptions,
9090
)
9191
}
9292

93-
UserProfileState.Empty -> Avatar(
93+
ComponentState.Empty -> Avatar(
9494
model = if (isNightModeEnabled()) {
9595
R.drawable.empty_profile_avatar_dark
9696
} else {
@@ -118,5 +118,5 @@ private fun AvatarStatePreview() {
118118
@Preview
119119
@Composable
120120
private fun AvatarEmptyPreview() {
121-
Avatar(UserProfileState.Empty, 256.dp)
121+
Avatar(ComponentState.Empty, 256.dp)
122122
}

0 commit comments

Comments
 (0)