Skip to content

Commit

Permalink
Merge pull request #379 from Cjsghkd/feature/#113
Browse files Browse the repository at this point in the history
[Feature/#113] Apply profile card card screen design roughly
  • Loading branch information
takahirom authored Aug 14, 2024
2 parents 53c57ee + dba8311 commit 5be67aa
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.github.droidkaigi.confsched.designsystem.theme

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color

sealed interface ProfileCardScreenTheme {
data object Default : ProfileCardScreenTheme {
override val primaryColor = Color(0xFFB4FF79)
override val containerColor = Color(0xFFC0FF8E)
}

data object Orange : ProfileCardScreenTheme {
override val primaryColor = Color(0xFFFEB258)
override val containerColor = Color(0xFFFFBB69)
}

data object Yellow : ProfileCardScreenTheme {
override val primaryColor = Color(0xFFFCF65F)
override val containerColor = Color(0xFFFFFA77)
}

data object Pink : ProfileCardScreenTheme {
override val primaryColor = Color(0xFF6FD7F8)
override val containerColor = Color(0xFFFFA0C9)
}

data object Blue : ProfileCardScreenTheme {
override val primaryColor = Color(0xFFB4FF79)
override val containerColor = Color(0xFF93E5FF)
}

data object White : ProfileCardScreenTheme {
override val primaryColor = Color(0xFFF9F9F9)
override val containerColor = Color.White
}

val primaryColor: Color
val containerColor: Color

companion object {
fun ofOrNull(profileCardTheme: String): ProfileCardScreenTheme? {
return when (profileCardTheme) {
"Iguana" -> Default
"Hedgehog" -> Orange
"Giraffe" -> Yellow
"Flamingo" -> Pink
"Jellyfish" -> Blue
else -> White
}
}
}
}

@Suppress("CompositionLocalAllowlist")
val LocalProfileCardScreenTheme: ProvidableCompositionLocal<ProfileCardScreenTheme> =
staticCompositionLocalOf<ProfileCardScreenTheme> {
error("No RoomTheme provided")
}

@Composable
fun ProvideProfileCardScreenTheme(profileCardTheme: String, content: @Composable () -> Unit) {
val profileCardTheme = ProfileCardScreenTheme.ofOrNull(profileCardTheme) ?: ProfileCardScreenTheme.Default
CompositionLocalProvider(LocalProfileCardScreenTheme provides profileCardTheme) {
content()
}
}
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
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sample">!!Please remove this resource when you add string resource!!</string>
<string name="profile_card">プロフィールカード</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sample">!!Please remove this resource when you add string resource!!</string>
<string name="profile_card">Profile Card</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package io.github.droidkaigi.confsched.profilecard

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import conference_app_2024.feature.profilecard.generated.resources.icon_qr
import io.github.droidkaigi.confsched.designsystem.theme.LocalProfileCardScreenTheme
import io.github.droidkaigi.confsched.designsystem.theme.ProvideProfileCardScreenTheme
import io.github.droidkaigi.confsched.ui.rememberAsyncImagePainter
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.painterResource

@Composable
internal fun FlipCard(
uiState: ProfileCardUiState.Card,
modifier: Modifier = Modifier,
isCreated: Boolean = false,
) {
var isFlipped by remember { mutableStateOf(false) }
var isCreated by rememberSaveable { mutableStateOf(isCreated) }
var initialRotation by remember { mutableStateOf(0f) }
val rotation = animateFloatAsState(
targetValue = if (isFlipped) 180f else initialRotation,
animationSpec = tween(
durationMillis = 400,
easing = FastOutSlowInEasing,
),
)
val targetRotation = animateFloatAsState(
targetValue = 30f,
animationSpec = tween(
durationMillis = 400,
easing = FastOutSlowInEasing,
),
).value
val targetRotation2 = animateFloatAsState(
targetValue = 0f,
animationSpec = tween(
durationMillis = 400,
easing = FastOutSlowInEasing,
),
).value

LaunchedEffect(Unit) {
if (isCreated) {
initialRotation = targetRotation
delay(400)
initialRotation = targetRotation2
isCreated = false
}
}

ProvideProfileCardScreenTheme(uiState.theme.toString()) {
Card(
modifier = modifier
.size(width = 300.dp, height = 380.dp)
.clickable { isFlipped = !isFlipped }
.graphicsLayer {
rotationY = rotation.value
cameraDistance = 12f * density
},
colors = CardDefaults.cardColors(containerColor = LocalProfileCardScreenTheme.current.containerColor),
elevation = CardDefaults.cardElevation(10.dp),
) {
if (isFlipped) { // Back
Column(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
rotationY = 180f
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Image(
painter = painterResource(ProfileCardRes.drawable.icon_qr),
contentDescription = null,
modifier = Modifier.size(160.dp),
)
}
} else { // Front
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Image(
painter = rememberAsyncImagePainter(uiState.image ?: ""),
contentDescription = null,
modifier = Modifier
.clip(CircleShape)
.size(120.dp),
)
Spacer(Modifier.height(12.dp))
Text(
text = uiState.occupation ?: "",
style = MaterialTheme.typography.titleMedium,
color = Color.Black,
)
Spacer(Modifier.height(2.dp))
Text(
text = uiState.nickname,
style = MaterialTheme.typography.headlineSmall,
color = Color.Black,
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package io.github.droidkaigi.confsched.profilecard

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
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
Expand All @@ -20,18 +31,26 @@ import androidx.compose.runtime.remember
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.platform.LocalLayoutDirection
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import conference_app_2024.feature.profilecard.generated.resources.icon_share
import conference_app_2024.feature.profilecard.generated.resources.profile_card
import io.github.droidkaigi.confsched.compose.EventEmitter
import io.github.droidkaigi.confsched.compose.rememberEventEmitter
import io.github.droidkaigi.confsched.designsystem.theme.LocalProfileCardScreenTheme
import io.github.droidkaigi.confsched.designsystem.theme.ProvideProfileCardScreenTheme
import io.github.droidkaigi.confsched.model.ProfileCard
import io.github.droidkaigi.confsched.model.ProfileCardTheme
import io.github.droidkaigi.confsched.ui.SnackbarMessageEffect
import io.github.droidkaigi.confsched.ui.UserMessageStateHolder
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource

const val profileCardScreenRoute = "profilecard"

Expand Down Expand Up @@ -155,7 +174,11 @@ internal fun ProfileCardScreen(
onClickEdit = {
eventEmitter.tryEmit(CardScreenEvent.Edit)
},
onClickShareProfileCard = {
eventEmitter.tryEmit(CardScreenEvent.Share)
},
contentPadding = padding,
isCreated = true,
)
}
}
Expand Down Expand Up @@ -235,27 +258,64 @@ internal fun EditScreen(
internal fun CardScreen(
uiState: ProfileCardUiState.Card,
onClickEdit: () -> Unit,
onClickShareProfileCard: () -> Unit,
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(),
isCreated: Boolean = false,
contentPadding: PaddingValues = PaddingValues(16.dp),
) {
Column(
modifier = modifier
.testTag(ProfileCardCardScreenTestTag)
.padding(contentPadding),
) {
Text("ProfileCard")
Text(uiState.nickname)
if (uiState.occupation != null) {
Text(uiState.occupation)
}
if (uiState.link != null) {
Text(uiState.link)
}
Button(
onClickEdit,
modifier = Modifier.testTag(ProfileCardEditButtonTestTag),
ProvideProfileCardScreenTheme(uiState.theme.toString()) {
Column(
modifier = modifier
.fillMaxSize()
.background(LocalProfileCardScreenTheme.current.primaryColor)
.testTag(ProfileCardCardScreenTestTag)
.padding(contentPadding),
) {
Text("Edit")
Text(
text = stringResource(ProfileCardRes.string.profile_card),
style = MaterialTheme.typography.headlineSmall,
color = Color.Black,
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 16.dp),
)
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
FlipCard(
uiState = uiState,
isCreated = isCreated,
)
Spacer(Modifier.height(32.dp))
Button(
onClick = { onClickShareProfileCard() },
colors = ButtonDefaults.buttonColors(containerColor = Color.White),
contentPadding = PaddingValues(vertical = 10.dp),
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
) {
Icon(
painter = painterResource(ProfileCardRes.drawable.icon_share),
contentDescription = "Share",
tint = Color.Black,
modifier = Modifier.size(18.dp),
)
Spacer(Modifier.width(8.dp))
Text(
text = "共有する",
style = MaterialTheme.typography.labelLarge,
color = Color.Black,
)
}
Spacer(Modifier.height(9.dp))
Text(
text = "編集する",
style = MaterialTheme.typography.labelLarge,
color = Color.Black,
modifier = Modifier
.clickable { onClickEdit() }
.testTag(ProfileCardEditButtonTestTag),
)
}
}
}
}

0 comments on commit 5be67aa

Please sign in to comment.