Skip to content

Commit

Permalink
[MOB-1670] �아바타 컴포넌트 구현 (#43)
Browse files Browse the repository at this point in the history
* outlineBorder 추가

* outlineBorder 1px 보정 추가

* integration 이미지 추가

* 아바타 컴포넌트 추가

* url 오버로딩 추가

* preview top padding 추가

* status size 변수명 수정

* outlineBorder 속성 Image로 이동

* 아바타 사이즈 requiredSize로 변경

* avatarSize -> size 변수명 수정

* outlineBorderWidth 2 -> 4 수정

* IntegrationBadge 작은 사이즈에서 circle로 변경

* isOnline, doNotDisturb 편의 함수 추가
  • Loading branch information
sodp5 authored Aug 7, 2024
1 parent 165a044 commit befa7d8
Show file tree
Hide file tree
Showing 38 changed files with 364 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package io.channel.bezier.compose.component.avatar

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
import io.channel.bezier.BezierTheme
import io.channel.bezier.compose.R
import io.channel.bezier.compose.component.avatar.properties.BezierAvatarBadge
import io.channel.bezier.compose.component.avatar.properties.BezierAvatarSize
import io.channel.bezier.compose.component.badge.chat.BezierChatBadge
import io.channel.bezier.compose.component.badge.status.BezierStatusBadge
import io.channel.bezier.extension.outlineBorder
import io.channel.bezier.extension.thenIf
import io.channel.bezier.shape.SmoothRoundedCornerShape

internal const val AvatarRadiusFraction = 42

@Composable
fun BezierAvatar(
url: String,
size: BezierAvatarSize,
isOnline: Boolean,
doNotDisturb: Boolean,
modifier: Modifier = Modifier,
showBorder: Boolean = false,
errorPainter: Painter = painterResource(id = R.drawable.unknown),
) {
BezierAvatar(
url = url,
size = size,
modifier = modifier,
showBorder = showBorder,
badge = BezierAvatarBadge.Status(isOnline = isOnline, doNotDisturb = doNotDisturb),
errorPainter = errorPainter,
)
}

@Composable
fun BezierAvatar(
url: String,
size: BezierAvatarSize,
modifier: Modifier = Modifier,
showBorder: Boolean = false,
badge: BezierAvatarBadge = BezierAvatarBadge.None,
errorPainter: Painter = painterResource(id = R.drawable.unknown),
) {
val painter = rememberAsyncImagePainter(
model = url,
error = errorPainter,
)

BezierAvatar(
painter = painter,
size = size,
modifier = modifier,
showBorder = showBorder,
badge = badge,
)
}

@Composable
fun BezierAvatar(
painter: Painter,
size: BezierAvatarSize,
isOnline: Boolean,
doNotDisturb: Boolean,
modifier: Modifier = Modifier,
showBorder: Boolean = false,
) {
BezierAvatar(
painter = painter,
size = size,
modifier = modifier,
showBorder = showBorder,
badge = BezierAvatarBadge.Status(isOnline = isOnline, doNotDisturb = doNotDisturb),
)
}

@Composable
fun BezierAvatar(
painter: Painter,
size: BezierAvatarSize,
modifier: Modifier = Modifier,
showBorder: Boolean = false,
badge: BezierAvatarBadge = BezierAvatarBadge.None,
) {
val avatarShape = SmoothRoundedCornerShape(AvatarRadiusFraction)

Box(
modifier = modifier.requiredSize(size.size),
) {
Image(
modifier = Modifier
.fillMaxSize()
.thenIf(showBorder) {
outlineBorder(
width = size.outlineBorderWidth,
color = BezierTheme.colorSchemes.surfaceNormal.color,
shape = avatarShape,
)
}
.clip(avatarShape),
painter = painter,
contentDescription = null,
)

Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.offset(x = size.badgeOffset, y = size.badgeOffset),
) {
when (badge) {
BezierAvatarBadge.None -> Unit
BezierAvatarBadge.Chat -> BezierChatBadge(size = size.chatBadgeSize)
is BezierAvatarBadge.Status -> BezierStatusBadge(
isOnline = badge.isOnline,
doNotDisturb = badge.doNotDisturb,
size = size.statusBadgeSize,
)

is BezierAvatarBadge.Integration -> IntegrationBadge(
integration = badge,
size = size,
)
}
}
}
}

@Composable
private fun IntegrationBadge(
integration: BezierAvatarBadge.Integration,
size: BezierAvatarSize,
) {
val shape = integration.createShape(size)

Image(
modifier = Modifier
.size(size.size)
.outlineBorder(
width = 2.dp,
color = BezierTheme.colorSchemes.surfaceNormal.color,
shape = shape,
)
.clip(shape),
painter = integration.painter(),
contentDescription = integration.name,
)
}

@Composable
@Preview(widthDp = 700, heightDp = 1100)
private fun BezierAvatarPreview() {
val badges = listOf(
BezierAvatarBadge.None,
BezierAvatarBadge.Status(isOnline = true, doNotDisturb = false),
BezierAvatarBadge.Status(isOnline = true, doNotDisturb = true),
BezierAvatarBadge.Status(isOnline = false, doNotDisturb = true),
BezierAvatarBadge.Status(isOnline = false, doNotDisturb = false),
BezierAvatarBadge.Chat,
BezierAvatarBadge.Integration.Kakao,
)

BezierTheme {
Column(
modifier = Modifier
.fillMaxSize()
.padding(start = 20.dp, top = 20.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
BezierAvatarSize.entries.forEach { size ->
BezierAvatar(
painter = painterResource(id = R.drawable.unknown),
size = size,
showBorder = false,
)
}
}

badges.forEach { badge ->
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
BezierAvatarSize.entries.forEach { size ->
BezierAvatar(
painter = painterResource(id = R.drawable.unknown),
size = size,
showBorder = true,
badge = badge,
)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.channel.bezier.compose.component.avatar.properties

import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import io.channel.bezier.compose.R
import io.channel.bezier.compose.component.avatar.AvatarRadiusFraction
import io.channel.bezier.shape.SmoothRoundedCornerShape

@Stable
sealed interface BezierAvatarBadge {
data object None : BezierAvatarBadge

data class Status(val isOnline: Boolean, val doNotDisturb: Boolean) : BezierAvatarBadge

data object Chat : BezierAvatarBadge

enum class Integration(
internal val painter: @Composable () -> Painter
) : BezierAvatarBadge {
Kakao(painter = { painterResource(id = R.drawable.image_kakao) }),
Line(painter = { painterResource(id = R.drawable.image_line) }),
NaverTalk(painter = { painterResource(id = R.drawable.image_talktalk) }),
Instagram(painter = { painterResource(id = R.drawable.image_instagram) }),
PhoneNumber(painter = { painterResource(id = R.drawable.image_avatar_call) }),
Email(painter = { painterResource(id = R.drawable.image_email) });

fun createShape(size: BezierAvatarSize): Shape {
return when (size) {
BezierAvatarSize.Size16,
BezierAvatarSize.Size20,
BezierAvatarSize.Size24,
BezierAvatarSize.Size30,
BezierAvatarSize.Size36 -> CircleShape
BezierAvatarSize.Size42,
BezierAvatarSize.Size48,
BezierAvatarSize.Size72,
BezierAvatarSize.Size90,
BezierAvatarSize.Size120 -> SmoothRoundedCornerShape(AvatarRadiusFraction)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package io.channel.bezier.compose.component.avatar.properties

import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.channel.bezier.compose.component.badge.chat.properties.BezierChatBadgeSize
import io.channel.bezier.compose.component.badge.status.properties.BezierStatusBadgeSize

/**
* 정의되지 않은 값에 대한 fallback 입니다.
*/
private val DefaultIntegrationBadgeSize = 16.dp

enum class BezierAvatarSize(
internal val size: Dp,
internal val outlineBorderWidth: Dp,
internal val statusBadgeSize: BezierStatusBadgeSize,
internal val chatBadgeSize: BezierChatBadgeSize,
internal val integrationBadgeSize: Dp,
internal val badgeOffset: Dp,
) {
Size16(
size = 16.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = 4.dp,
),
Size20(
size = 20.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = 4.dp,
),
Size24(
size = 24.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = 16.dp,
badgeOffset = 4.dp,
),
Size30(
size = 30.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = 4.dp,
),
Size36(
size = 36.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = 3.dp,
),
Size42(
size = 42.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = 20.dp,
badgeOffset = 2.dp,
),
Size48(
size = 48.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Medium,
chatBadgeSize = BezierChatBadgeSize.Medium,
integrationBadgeSize = 20.dp,
badgeOffset = 2.dp,
),
Size72(
size = 72.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Large,
chatBadgeSize = BezierChatBadgeSize.Large,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = (-2).dp,
),
Size90(
size = 90.dp,
outlineBorderWidth = 2.dp,
statusBadgeSize = BezierStatusBadgeSize.Large,
chatBadgeSize = BezierChatBadgeSize.Large,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = (-2).dp,
),
Size120(
size = 120.dp,
outlineBorderWidth = 4.dp,
statusBadgeSize = BezierStatusBadgeSize.Large,
chatBadgeSize = BezierChatBadgeSize.Large,
integrationBadgeSize = DefaultIntegrationBadgeSize,
badgeOffset = (-2).dp,
),
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-hdpi/image_email.png
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.
Binary file added bezier/src/main/res/drawable-hdpi/image_kakao.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-hdpi/image_line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-hdpi/image_talktalk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-hdpi/image_text.png
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.
Binary file added bezier/src/main/res/drawable-mdpi/image_email.png
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.
Binary file added bezier/src/main/res/drawable-mdpi/image_kakao.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-mdpi/image_line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-mdpi/image_talktalk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-mdpi/image_text.png
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.
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.
Binary file added bezier/src/main/res/drawable-xhdpi/image_kakao.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-xhdpi/image_line.png
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.
Binary file added bezier/src/main/res/drawable-xhdpi/image_text.png
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.
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.
Binary file added bezier/src/main/res/drawable-xxhdpi/image_kakao.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bezier/src/main/res/drawable-xxxhdpi/image_kakao.png

0 comments on commit befa7d8

Please sign in to comment.