Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 2e202a32 authored by Wolf-Martell Montwé's avatar Wolf-Martell Montwé Committed by GitHub
Browse files

Merge pull request #8644 from wmontwe/add-account-avatar

Add account avatar
parents 1f3a3d0a b51d94c8
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
plugins {
    id(ThunderbirdPlugins.Library.androidCompose)
}

android {
    namespace = "app.k9mail.feature.account.avatar"
    resourcePrefix = "account_avatar_"
}

dependencies {
    implementation(projects.core.ui.compose.designsystem)
    implementation(projects.core.common)

    testImplementation(projects.core.ui.compose.testing)
}
+30 −0
Original line number Diff line number Diff line
package app.k9mail.feature.account.avatar.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes

@Composable
@Preview(showBackground = true)
internal fun AvatarPreview() {
    PreviewWithThemes {
        Avatar(
            color = Color(0xFFe57373),
            name = "example",
            selected = false,
        )
    }
}

@Composable
@Preview(showBackground = true)
internal fun AvatarSelectedPreview() {
    PreviewWithThemes {
        Avatar(
            color = Color(0xFFe57373),
            name = "example",
            selected = true,
        )
    }
}
+92 −0
Original line number Diff line number Diff line
package app.k9mail.feature.account.avatar.ui

import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.theme2.MainTheme

val selectedAvatarSize = 40.dp

@Composable
fun Avatar(
    color: Color,
    name: String,
    selected: Boolean,
    modifier: Modifier = Modifier,
    onClick: (() -> Unit)? = null,
) {
    val avatarSize by animateDpAsState(
        targetValue = if (selected) selectedAvatarSize else MainTheme.sizes.iconAvatar,
        label = "Avatar size",
    )

    Box(
        modifier = modifier
            .clip(CircleShape)
            .clickable(enabled = onClick != null && !selected, onClick = { onClick?.invoke() }),
        contentAlignment = Alignment.Center,
    ) {
        AvatarOutline(
            color = color,
            modifier = Modifier.size(avatarSize),
        ) {
            AvatarPlaceholder(
                displayName = name,
            )
            // TODO: Add image loading
        }
    }
}

@Composable
private fun AvatarOutline(
    color: Color,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    Surface(
        modifier = modifier
            .clip(CircleShape)
            .border(2.dp, color, CircleShape)
            .padding(2.dp),
        color = color.copy(alpha = 0.3f),
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .border(2.dp, MainTheme.colors.surfaceContainerLowest, CircleShape),
            contentAlignment = Alignment.Center,
        ) {
            content()
        }
    }
}

@Composable
private fun AvatarPlaceholder(
    displayName: String,
    modifier: Modifier = Modifier,
) {
    TextTitleMedium(
        text = extractNameInitials(displayName).uppercase(),
        modifier = modifier,
    )
}

private fun extractNameInitials(displayName: String): String {
    return displayName.take(2)
}
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@ dependencies {
    implementation(projects.core.ui.theme.api)
    implementation(projects.core.ui.compose.designsystem)

    implementation(projects.feature.account.avatar)

    implementation(projects.legacy.account)
    implementation(projects.legacy.mailstore)
    implementation(projects.legacy.message)
+7 −59
Original line number Diff line number Diff line
package app.k9mail.feature.navigation.drawer.ui.account

import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.theme2.ColorRoles
import app.k9mail.core.ui.compose.theme2.MainTheme
import app.k9mail.core.ui.compose.theme2.toColorRoles
import app.k9mail.feature.account.avatar.ui.Avatar
import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount
import app.k9mail.feature.navigation.drawer.ui.common.labelForCount

val selectedAvatarSize = 40.dp

@Composable
internal fun AccountAvatar(
    account: DisplayAccount,
@@ -36,44 +27,16 @@ internal fun AccountAvatar(
    val accountColor = calculateAccountColor(account.color)
    val accountColorRoles = accountColor.toColorRoles(context)

    val avatarSize by animateDpAsState(
        targetValue = if (selected) selectedAvatarSize else MainTheme.sizes.iconAvatar,
        label = "Avatar size",
    )

    Box(
        modifier = modifier,
        contentAlignment = Alignment.BottomEnd,
    ) {
        val clickableModifier = if (onClick != null && !selected) Modifier.clickable { onClick(account) } else Modifier

        Box(
            modifier = Modifier
                .size(MainTheme.sizes.iconAvatar),
            contentAlignment = Alignment.Center,
        ) {
            Surface(
                modifier = Modifier
                    .size(avatarSize)
                    .clip(CircleShape)
                    .border(2.dp, accountColor, CircleShape)
                    .padding(2.dp)
                    .then(clickableModifier),
                color = accountColor.copy(alpha = 0.3f),
            ) {
                Box(
                    contentAlignment = Alignment.Center,
                    modifier = Modifier
                        .border(2.dp, MainTheme.colors.surfaceContainerLowest, CircleShape),
                ) {
                    Placeholder(
                        displayName = account.name,
        Avatar(
            color = accountColor,
            name = account.name,
            onClick = onClick?.let { { onClick(account) } },
            selected = selected,
        )
                    // TODO: Add image loading
                }
            }
        }

        UnreadBadge(
            unreadCount = account.unreadMessageCount,
            accountColorRoles = accountColorRoles,
@@ -81,17 +44,6 @@ internal fun AccountAvatar(
    }
}

@Composable
private fun Placeholder(
    displayName: String,
    modifier: Modifier = Modifier,
) {
    TextTitleMedium(
        text = extractNameInitials(displayName).uppercase(),
        modifier = modifier,
    )
}

@Composable
private fun UnreadBadge(
    unreadCount: Int,
@@ -120,7 +72,3 @@ private fun UnreadBadge(
        }
    }
}

private fun extractNameInitials(displayName: String): String {
    return displayName.take(2)
}
Loading