重构登录注册页面UI

- 提取ActionButton组件以复用
- 提取TextInputField组件以复用
- 更新Profile页面布局

- 更新ChangePasswordScreen页面布局
- 更新EmailSignup页面布局
- 更新UserAuth页面布局
- 更新Signup页面布局
- 更新Login页面布局
This commit is contained in:
2024-09-05 23:30:37 +08:00
parent fda6fe4dcf
commit 37dcd19227
28 changed files with 325 additions and 186 deletions

View File

@@ -1,12 +1,30 @@
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
import com.aiosman.riderpro.ui.composables.TextInputField
import kotlinx.coroutines.launch
/**
@@ -37,60 +55,93 @@ fun ChangePasswordScreen() {
var errorMessage by remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
Scaffold { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
verticalArrangement = Arrangement.Center
var oldPasswordError by remember { mutableStateOf<String?>(null) }
var confirmPasswordError by remember { mutableStateOf<String?>(null) }
var passwordError by remember { mutableStateOf<String?>(null) }
fun validate(): Boolean {
oldPasswordError =
if (currentPassword.isEmpty()) "Please enter your current password" else null
passwordError = when {
newPassword.length < 8 -> "Password must be at least 8 characters long"
!newPassword.any { it.isDigit() } -> "Password must contain at least one digit"
!newPassword.any { it.isUpperCase() } -> "Password must contain at least one uppercase letter"
!newPassword.any { it.isLowerCase() } -> "Password must contain at least one lowercase letter"
else -> null
}
confirmPasswordError =
if (newPassword != confirmPassword) "Passwords do not match" else null
return passwordError == null && confirmPasswordError == null && oldPasswordError == null
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally
) {
StatusBarSpacer()
Box(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
) {
TextField(
value = currentPassword,
NoticeScreenHeader(
title = "Change password",
moreIcon = false
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
) {
Spacer(modifier = Modifier.height(80.dp))
TextInputField(
text = currentPassword,
onValueChange = { currentPassword = it },
label = { Text("Current Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
password = true,
label = "Current password",
hint = "Enter your current password",
error = oldPasswordError
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = newPassword,
Spacer(modifier = Modifier.height(24.dp))
TextInputField(
text = newPassword,
onValueChange = { newPassword = it },
label = { Text("New Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
password = true,
label = "New password",
hint = "Enter your new password",
error = passwordError
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = confirmPassword,
Spacer(modifier = Modifier.height(24.dp))
TextInputField(
text = confirmPassword,
onValueChange = { confirmPassword = it },
label = { Text("Confirm New Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
password = true,
label = "Confirm new password",
hint = "Enter your new password again",
error = confirmPasswordError
)
Spacer(modifier = Modifier.height(16.dp))
if (errorMessage.isNotEmpty()) {
Text(
text = errorMessage,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(bottom = 16.dp)
)
}
Button(
onClick = {
if (newPassword == confirmPassword) {
scope.launch {
Spacer(modifier = Modifier.height(80.dp))
ActionButton(
modifier = Modifier
.width(345.dp)
.height(48.dp),
text = "LET'S RIDE".uppercase(),
backgroundImage = R.mipmap.rider_pro_signup_red_bg
) {
if (validate()) {
scope.launch {
try {
viewModel.changePassword(currentPassword, newPassword)
navController.popBackStack()
} catch (e: Exception) {
errorMessage = e.message ?: "An error occurred"
}
} else {
errorMessage = "Passwords do not match"
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Change Password")
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
package com.aiosman.riderpro.ui.composables
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
@Composable
fun ActionButton(
modifier: Modifier,
text: String,
color: Color = Color.White,
@DrawableRes backgroundImage: Int,
leading: @Composable (() -> Unit)? = null,
click: () -> Unit = {}
) {
Box(
modifier = modifier.noRippleClickable {
click()
}
) {
Image(
painter = painterResource(id = backgroundImage),
contentDescription = "Rider Pro",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
Row(
modifier = Modifier.align(Alignment.Center),
verticalAlignment = Alignment.CenterVertically
) {
leading?.invoke()
Text(text, fontSize = 16.sp, color = color, fontWeight = FontWeight.W400)
}
}
}

View File

@@ -0,0 +1,125 @@
package com.aiosman.riderpro.ui.composables
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
@Composable
fun TextInputField(
modifier: Modifier = Modifier,
text: String,
onValueChange: (String) -> Unit,
password: Boolean = false,
label: String? = null,
hint: String? = null,
error: String? = null
) {
var showPassword by remember { mutableStateOf(!password) }
var isFocused by remember { mutableStateOf(false) }
Column(modifier = modifier) {
label?.let {
Text(it, color = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.height(16.dp))
}
Box(
contentAlignment = Alignment.CenterStart
) {
Row {
BasicTextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier
.weight(1f)
.onFocusChanged { focusState ->
isFocused = focusState.isFocused
},
textStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.W500,
color = Color(0xff333333)
),
keyboardOptions = KeyboardOptions(
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text
),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
singleLine = true,
)
if (password) {
Image(
painter = painterResource(id = R.drawable.rider_pro_eye),
contentDescription = "Password",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = ColorFilter.tint(Color.Black)
)
}
}
if (text.isEmpty()) {
hint?.let {
Text(it, color = Color(0xFFCCCCCC), fontWeight = FontWeight.W600)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(if (isFocused) Color(0xff333333) else Color(0xFFF5F5F5))
)
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.height(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (error != null) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_input_error),
contentDescription = "Error",
modifier = Modifier.size(8.dp)
)
Spacer(modifier = Modifier.size(4.dp))
Text(error, color = Color(0xFFE53935), fontSize = 12.sp)
}
}
}
}

View File

@@ -95,8 +95,12 @@ fun ProfilePage() {
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
LazyColumn(
modifier = Modifier
.fillMaxSize().background(Color(0xFFF5F5F5)).padding(bottom = with(LocalDensity.current) {
val da = WindowInsets.navigationBars.getBottom(this).toDp() + 48.dp
.fillMaxSize()
.background(Color(0xFFF5F5F5))
.padding(bottom = with(LocalDensity.current) {
val da = WindowInsets.navigationBars
.getBottom(this)
.toDp() + 48.dp
da
})
) {
@@ -118,7 +122,7 @@ fun ProfilePage() {
contentScale = ContentScale.Crop
)
} else {
Image(
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
modifier = Modifier
.fillMaxWidth()
@@ -145,44 +149,60 @@ fun ProfilePage() {
},
tint = Color.White
)
MaterialTheme(shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(16.dp))) {
MaterialTheme(
shapes = MaterialTheme.shapes.copy(
extraSmall = RoundedCornerShape(
16.dp
)
)
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(250.dp).background(Color.White)
modifier = Modifier
.width(250.dp)
.background(Color.White)
) {
Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) {
Box(
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
model.logout()
navController.navigate(NavigationRoute.Login.route) {
popUpTo(NavigationRoute.Index.route) {
inclusive = true
}
}
}
}) {
Row {
Text("Logout", fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_logout),
contentDescription = "",
modifier = Modifier.size(24.dp).noRippleClickable {
scope.launch {
model.logout()
navController.navigate(NavigationRoute.Login.route) {
popUpTo(NavigationRoute.Index.route) {
inclusive = true
}
}
}
}
modifier = Modifier.size(24.dp)
)
}
}
Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) {
Box(
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
}
}) {
Row {
Text("Change password",fontWeight = FontWeight.W500)
Text("Change password", fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_change_password),
contentDescription = "",
modifier = Modifier.size(24.dp).noRippleClickable {
scope.launch {
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
}
}
modifier = Modifier.size(24.dp)
)
}
}
@@ -444,7 +464,9 @@ fun CommunicationOperatorGroup(
contentDescription = ""
)
Text(
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(R.string.follow_upper),
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(
R.string.follow_upper
),
fontSize = 16.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
@@ -565,7 +587,9 @@ fun TimeGroup(time: String = "2024.06.08 12:23") {
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier.height(16.dp).width(14.dp),
modifier = Modifier
.height(16.dp)
.width(14.dp),
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
contentDescription = ""
)
@@ -654,7 +678,7 @@ fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) {
imageUrl,
modifier = Modifier
.fillMaxSize()
.aspectRatio(3f/2f)
.aspectRatio(3f / 2f)
.padding(16.dp)
.noRippleClickable {
PostViewModel.preTransit(momentEntity)

View File

@@ -33,7 +33,9 @@ import com.aiosman.riderpro.data.ServiceException
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.TextInputField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

View File

@@ -1,6 +1,5 @@
package com.aiosman.riderpro.ui.login
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -18,18 +17,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavOptions
import androidx.navigation.navOptions
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.composables.ActionButton
@Composable
fun LoginPage() {
@@ -106,33 +101,3 @@ fun LoginPage() {
}
}
@Composable
fun ActionButton(
modifier: Modifier,
text: String,
color: Color = Color.White,
@DrawableRes backgroundImage: Int,
leading: @Composable (() -> Unit)? = null,
click: () -> Unit = {}
) {
Box(
modifier = modifier.noRippleClickable {
click()
}
) {
Image(
painter = painterResource(id = backgroundImage),
contentDescription = "Rider Pro",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
Row(
modifier = Modifier.align(Alignment.Center),
verticalAlignment = Alignment.CenterVertically
) {
leading?.invoke()
Text(text, fontSize = 16.sp, color = color, fontWeight = FontWeight.W400)
}
}
}

View File

@@ -15,25 +15,18 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.credentials.Credential
import androidx.credentials.CredentialManager
import androidx.credentials.CustomCredential
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.Messaging
@@ -42,6 +35,7 @@ import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.ServiceException
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.utils.GoogleLogin
import kotlinx.coroutines.Dispatchers

View File

@@ -2,7 +2,6 @@ package com.aiosman.riderpro.ui.login
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -14,8 +13,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -31,13 +28,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.credentials.CredentialManager
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.LocalNavController
@@ -48,7 +39,9 @@ import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.ServiceException
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.TextInputField
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.utils.GoogleLogin
import kotlinx.coroutines.launch
@@ -227,66 +220,3 @@ fun UserAuthScreen() {
}
@Composable
fun TextInputField(
modifier: Modifier = Modifier,
text: String,
onValueChange: (String) -> Unit,
password: Boolean = false,
label: String? = null,
hint: String? = null
) {
var showPassword by remember { mutableStateOf(!password) }
Column(modifier = modifier) {
label?.let {
Text(it, color = Color(0xFFCCCCCC))
Spacer(modifier = Modifier.height(16.dp))
}
Box(
contentAlignment = Alignment.CenterStart
) {
Row {
BasicTextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier.weight(1f),
textStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.W500
),
keyboardOptions = KeyboardOptions(
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text
),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
singleLine = true
)
if (password) {
Image(
painter = painterResource(id = R.drawable.rider_pro_eye),
contentDescription = "Password",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.Black)
)
}
}
if (text.isEmpty()) {
hint?.let {
Text(it, color = Color(0xFFCCCCCC), fontWeight = FontWeight.W600)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(Color(0xFFF5F5F5))
)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB