Skip to content

Commit

Permalink
Merge pull request #56 from spine-examples/improve-ux
Browse files Browse the repository at this point in the history
Increase window size and enhance UI
  • Loading branch information
MykytaPimonovTD authored Oct 31, 2024
2 parents e0e4d9f + 9ea21ae commit f845d10
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ package io.spine.internal.dependency

// https://github.com/spine-examples/Pingh
public object Pingh {
private const val version = "1.0.0-SNAPSHOT.15"
private const val version = "1.0.0-SNAPSHOT.16"
private const val group = "io.spine.examples.pingh"

public const val client: String = "$group:client:$version"
Expand Down
141 changes: 88 additions & 53 deletions desktop/src/main/kotlin/io/spine/examples/pingh/desktop/Login.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.testTag
Expand Down Expand Up @@ -139,6 +142,10 @@ private fun UsernameEnteringPage(
var username by remember { mutableStateOf("") }
var wasChanged by remember { mutableStateOf(false) }
val isError = remember { mutableStateOf(false) }
val requestUserCode = {
val name = Username::class.of(username)
flow.requestUserCode(name)
}
Column(
modifier = Modifier
.fillMaxSize()
Expand All @@ -154,15 +161,18 @@ private fun UsernameEnteringPage(
username = value
wasChanged = true
},
onEnterPressed = {
if (wasChanged && !isError.value) {
requestUserCode()
}
},
isError = isError
)
Spacer(Modifier.height(10.dp))
Spacer(Modifier.height(15.dp))
LoginButton(
enabled = wasChanged && !isError.value
) {
val name = Username::class.of(username)
flow.requestUserCode(name)
}
enabled = wasChanged && !isError.value,
onClick = requestUserCode
)
}
}

Expand All @@ -184,7 +194,7 @@ private fun ApplicationInfo() {
Icon(
painter = Icons.pingh,
contentDescription = null,
modifier = Modifier.size(40.dp),
modifier = Modifier.size(50.dp),
tint = MaterialTheme.colorScheme.onSecondary
)
Spacer(Modifier.width(10.dp))
Expand All @@ -196,11 +206,11 @@ private fun ApplicationInfo() {
style = MaterialTheme.typography.displayLarge
)
}
Spacer(Modifier.height(10.dp))
Spacer(Modifier.height(20.dp))
Text(
text = "Pingh is a GitHub app that looks up mentions on behalf of the user. " +
"It requires authentication via GitHub.",
modifier = Modifier.width(180.dp),
modifier = Modifier.width(240.dp),
color = MaterialTheme.colorScheme.onSecondaryContainer,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium
Expand All @@ -213,12 +223,14 @@ private fun ApplicationInfo() {
*
* @param value The current value of the input field.
* @param onChange Called when input value is changed.
* @param onEnterPressed Called when this input is focused and the "Enter" key is pressed.
* @param isError Indicates if the input's current value is in error.
*/
@Composable
private fun UsernameInput(
value: String,
onChange: (String) -> Unit,
onEnterPressed: () -> Unit,
isError: MutableState<Boolean>
) {
val interactionSource = remember { MutableInteractionSource() }
Expand All @@ -236,8 +248,16 @@ private fun UsernameInput(
isError.value = !isValidUsername(changedValue)
},
modifier = Modifier
.width(180.dp)
.height(52.dp)
.width(240.dp)
.height(57.dp)
.onKeyEvent { event ->
if (event.key == Key.Enter) {
onEnterPressed()
true
} else {
false
}
}
.testTag("username-input"),
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSecondary
Expand Down Expand Up @@ -274,8 +294,8 @@ private fun InputContainer(
) {
Row(
modifier = Modifier
.width(180.dp)
.height(40.dp)
.width(240.dp)
.height(45.dp)
.border(border = border, shape = MaterialTheme.shapes.medium)
.background(
color = MaterialTheme.colorScheme.secondary,
Expand Down Expand Up @@ -305,9 +325,9 @@ private fun InputContainer(
private fun Label(color: Color) {
Box(
modifier = Modifier
.width(90.dp)
.height(10.dp)
.absoluteOffset(x = 10.dp, y = (-5).dp)
.width(110.dp)
.height(12.dp)
.absoluteOffset(x = 12.dp, y = (-6).dp)
) {
Text(
text = "GitHub username",
Expand Down Expand Up @@ -350,7 +370,7 @@ private fun ErrorMessage(isShown: Boolean) {
modifier = Modifier
.width(155.dp)
.height(30.dp)
.absoluteOffset(x = 15.dp, y = 44.dp),
.absoluteOffset(x = 15.dp, y = 49.dp),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall
)
Expand All @@ -372,10 +392,11 @@ private fun LoginButton(
Button(
onClick = onClick,
modifier = Modifier
.width(180.dp)
.height(40.dp)
.width(240.dp)
.height(45.dp)
.testTag("login-button"),
enabled = enabled,
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
Expand Down Expand Up @@ -420,12 +441,12 @@ private fun VerificationPage(
horizontalAlignment = Alignment.CenterHorizontally
) {
VerificationTitle()
Spacer(Modifier.height(15.dp))
Spacer(Modifier.height(25.dp))
UserCodeField(
userCode = userCode,
isExpired = isUserCodeExpired
)
Spacer(Modifier.height(10.dp))
Spacer(Modifier.height(25.dp))
if (isUserCodeExpired) {
Spacer(Modifier.height(5.dp))
CodeExpiredErrorMessage(flow)
Expand All @@ -434,7 +455,7 @@ private fun VerificationPage(
verificationUrl = verificationUrl,
expiresIn = expiresIn
)
Spacer(Modifier.height(20.dp))
Spacer(Modifier.height(25.dp))
SubmitButton(
flow = flow,
toMentionsPage = toMentionsPage
Expand All @@ -450,7 +471,7 @@ private fun VerificationPage(
private fun VerificationTitle() {
Text(
text = "Verify your login",
fontSize = 18.sp,
fontSize = 20.sp,
style = MaterialTheme.typography.displayLarge
)
}
Expand All @@ -471,15 +492,32 @@ private fun UserCodeField(
} else {
MaterialTheme.colorScheme.onSecondary
}
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
Row(
modifier = Modifier
.width(260.dp)
.height(46.dp)
.run {
if (isExpired) {
this
} else {
border(
width = 1.dp,
color = MaterialTheme.colorScheme.onBackground,
shape = MaterialTheme.shapes.medium
)
}
}
.padding(horizontal = 15.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
SelectionContainer {
Text(
text = userCode.value,
modifier = Modifier.width(200.dp),
color = color,
fontSize = 28.sp,
fontSize = 30.sp,
textAlign = if (isExpired) TextAlign.Center else TextAlign.Start,
letterSpacing = 3.sp,
style = MaterialTheme.typography.displayLarge
)
Expand All @@ -500,20 +538,16 @@ private fun CopyToClipboardIcon(
userCode: UserCode
) {
val clipboardManager = LocalClipboardManager.current
Box(
modifier = Modifier.offset(x = 103.dp)
) {
IconButton(
icon = Icons.copy,
onClick = {
clipboardManager.setText(AnnotatedString(userCode.value))
},
modifier = Modifier.size(30.dp),
colors = IconButtonDefaults.iconButtonColors(
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
IconButton(
icon = Icons.copy,
onClick = {
clipboardManager.setText(AnnotatedString(userCode.value))
},
modifier = Modifier.size(30.dp),
colors = IconButtonDefaults.iconButtonColors(
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
}
)
}

/**
Expand Down Expand Up @@ -543,17 +577,16 @@ private fun VerificationText(
expiresIn: Duration
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
modifier = Modifier.width(260.dp)
) {
Text(
text = "Enter this code at",
color = MaterialTheme.colorScheme.onSecondaryContainer,
style = MaterialTheme.typography.bodyLarge
)
Spacer(Modifier.height(3.dp))
Spacer(Modifier.height(5.dp))
VerificationUrlButton(verificationUrl)
Spacer(Modifier.height(3.dp))
Spacer(Modifier.height(12.dp))
Text(
text = "The code is valid for ${toMinutes(expiresIn)} minutes.",
color = MaterialTheme.colorScheme.onSecondaryContainer,
Expand Down Expand Up @@ -615,23 +648,24 @@ private fun SubmitButton(
}
Box(
modifier = Modifier
.width(210.dp)
.height(32.dp),
.width(260.dp)
.height(46.dp),
contentAlignment = Alignment.TopCenter
) {
Button(
onClick = onClick,
modifier = Modifier.fillMaxSize()
.testTag("submit-button"),
enabled = enabled,
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Text(
text = "I have entered the code",
style = MaterialTheme.typography.displayMedium
style = MaterialTheme.typography.displaySmall
)
}
if (!enabled) {
Expand Down Expand Up @@ -659,8 +693,8 @@ private fun NoResponseErrorMessage(flow: VerifyLogin) {
clickablePartOfText = "start over",
onClick = flow::requestNewUserCode,
modifier = Modifier
.width(180.dp)
.offset(y = 40.dp)
.width(240.dp)
.offset(y = 50.dp)
.testTag("no-response-message")
)
}
Expand Down Expand Up @@ -737,11 +771,11 @@ private fun FailedPage(flow: LoginFailed) {
) {
Text(
text = flow.errorMessage.value,
modifier = Modifier.width(210.dp),
modifier = Modifier.width(240.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge
)
Spacer(Modifier.height(20.dp))
Spacer(Modifier.height(25.dp))
RestartButton(flow)
}
}
Expand All @@ -756,8 +790,9 @@ private fun RestartButton(flow: LoginFailed) {
Button(
onClick = flow::restartLogin,
modifier = Modifier
.width(210.dp)
.height(32.dp),
.width(240.dp)
.height(40.dp),
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
Expand Down
Loading

0 comments on commit f845d10

Please sign in to comment.