Skip to content

Commit

Permalink
Merge pull request #138 from Automattic/hamorillo/127-skeleton-demo-app
Browse files Browse the repository at this point in the history
DemoApp - Add Skeleton button and Theme picker in Profile tab
  • Loading branch information
hamorillo authored Apr 29, 2024
2 parents 727ae3f + b82313e commit 6d53913
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 42 deletions.
160 changes: 118 additions & 42 deletions demo-app/src/main/java/com/gravatar/demoapp/ui/DemoGravatarApp.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.gravatar.demoapp.ui

import android.content.res.Configuration
import android.util.Log
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
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.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
Expand All @@ -23,7 +30,9 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
Expand All @@ -32,6 +41,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
Expand All @@ -54,11 +64,13 @@ import com.gravatar.demoapp.ui.model.SettingsState
import com.gravatar.services.ProfileService
import com.gravatar.services.Result
import com.gravatar.types.Email
import com.gravatar.ui.GravatarTheme
import com.gravatar.ui.components.LargeProfile
import com.gravatar.ui.components.LargeProfileSummary
import com.gravatar.ui.components.MiniProfileCard
import com.gravatar.ui.components.ProfileCard
import com.gravatar.ui.components.UserProfileState
import com.gravatar.ui.gravatarTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -149,37 +161,62 @@ private fun GravatarTabs(
}
}

private enum class ThemeOptions {
LIGHT,
DARK,
SYSTEM,
}

@Composable
private fun ProfileCards(profileState: UserProfileState?, error: String) {
val defaultModifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer)
.fillMaxWidth()
.padding(24.dp)
// Show the profile card if we got a result and there is no error and it's not loading
if (error.isEmpty()) {
profileState?.let {
ProfileCard(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
MiniProfileCard(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
LargeProfile(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
LargeProfileSummary(
it,
Modifier
.padding(8.dp)
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(start = 24.dp, end = 24.dp, top = 24.dp, bottom = 8.dp)
.fillMaxWidth(),
)
private fun ProfileCards(profileState: UserProfileState?, theme: ThemeOptions, error: String) {
val configuration = Configuration(LocalConfiguration.current).apply {
uiMode = when (theme) {
ThemeOptions.LIGHT -> Configuration.UI_MODE_NIGHT_NO
ThemeOptions.DARK -> Configuration.UI_MODE_NIGHT_YES
ThemeOptions.SYSTEM -> uiMode
}
} else {
if (error.isNotEmpty()) {
Text(text = error)
}
CompositionLocalProvider(
LocalConfiguration provides configuration,
) {
val defaultModifier = Modifier
.background(gravatarTheme.colorScheme.surfaceContainer)
.fillMaxWidth()
.padding(24.dp)

GravatarTheme {
Surface {
Column {
// Show the profile card if we got a result and there is no error and it's not loading
if (error.isEmpty()) {
profileState?.let {
ProfileCard(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
MiniProfileCard(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
LargeProfile(it, defaultModifier)
Spacer(modifier = Modifier.height(16.dp))
LargeProfileSummary(
it,
Modifier
.padding(8.dp)
.background(gravatarTheme.colorScheme.surfaceContainer)
.padding(start = 24.dp, end = 24.dp, top = 24.dp, bottom = 8.dp)
.fillMaxWidth(),
)
}
} else {
if (error.isNotEmpty()) {
Text(text = error)
}
}
}
}
}
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwable?) -> Unit) {
var email by remember { mutableStateOf(BuildConfig.DEMO_EMAIL, neverEqualPolicy()) }
Expand All @@ -188,6 +225,8 @@ private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwab
val profileService = ProfileService()
val keyboardController = LocalSoftwareKeyboardController.current
val scope = rememberCoroutineScope()
var themesExpanded by remember { mutableStateOf(false) }
var theme by remember { mutableStateOf(ThemeOptions.SYSTEM) }

Surface(modifier) {
Column(
Expand All @@ -204,28 +243,65 @@ private fun ProfileTab(modifier: Modifier = Modifier, onError: (String?, Throwab
.fillMaxWidth()
.padding(16.dp),
)
Button(
onClick = {
keyboardController?.hide()
scope.launch {
error = ""
profileState = UserProfileState.Loading
when (val result = profileService.fetch(Email(email))) {
is Result.Success -> {
result.value.entry.firstOrNull()?.let {
profileState = UserProfileState.Loaded(it)
Row(modifier = Modifier.padding(horizontal = 4.dp)) {
Button(
onClick = {
keyboardController?.hide()
scope.launch {
error = ""
profileState = UserProfileState.Loading
when (val result = profileService.fetch(Email(email))) {
is Result.Success -> {
result.value.entry.firstOrNull()?.let {
profileState = UserProfileState.Loaded(it)
}
}

is Result.Failure -> {
onError(result.error.name, null)
error = result.error.name
}
}
is Result.Failure -> {
onError(result.error.name, null)
error = result.error.name
}
}
},
) { Text(text = stringResource(R.string.button_get_profile)) }
Spacer(modifier = Modifier.width(4.dp))
Button(
enabled = profileState !is UserProfileState.Loading,
onClick = {
profileState = UserProfileState.Loading
},
) {
Text(text = stringResource(R.string.button_enable_loading_state))
}
Spacer(modifier = Modifier.width(4.dp))
ExposedDropdownMenuBox(
expanded = themesExpanded,
onExpandedChange = { themesExpanded = !themesExpanded },
modifier = Modifier.fillMaxWidth(),
) {
TextField(
readOnly = true,
value = theme.name,
onValueChange = { },
label = { Text(stringResource(R.string.theme_label)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = themesExpanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
)
ExposedDropdownMenu(expanded = themesExpanded, onDismissRequest = { themesExpanded = false }) {
ThemeOptions.entries.forEach { selectionOption ->
DropdownMenuItem(text = { Text(text = selectionOption.name) }, onClick = {
theme = selectionOption
themesExpanded = false
})
}
}
},
) { Text(text = stringResource(R.string.button_get_profile)) }
}
}
Spacer(modifier = Modifier.height(16.dp))
ProfileCards(profileState, error)
ProfileCards(profileState, theme, error)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions demo-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
<string name="tab_label_avatar">Avatar</string>
<string name="tab_label_avatar_update">Avatar Update</string>
<string name="button_get_profile">Get Profile</string>
<string name="button_enable_loading_state">Skeleton</string>
<string name="text_display_name">Display Name: %1$s</string>
<string name="text_url">Url: %1$s</string>
<string name="button_load_gravatar">Load Gravatar</string>
<string name="access_token_label">Access Token</string>
<string name="theme_label">Theme</string>
<string name="update_avatar_button_label">Update Avatar</string>
<string name="avatar_update_upload_success_toast">Upload success</string>
<string name="avatar_update_upload_failed_toast">Upload error: %1$s</string>
Expand Down

0 comments on commit 6d53913

Please sign in to comment.