Browse Source

Continue refinement of RoomList (and remove avatar library)

feature/bma/flipper
ganfra 2 years ago
parent
commit
f55bb16bfa
  1. 1
      features/roomlist/build.gradle.kts
  2. 161
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt
  3. 15
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt
  4. 157
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt
  5. 48
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt
  6. 16
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt
  7. 25
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt
  8. 1
      gradle/libs.versions.toml
  9. 1
      libraries/avatar/.gitignore
  10. 12
      libraries/avatar/build.gradle.kts
  11. 0
      libraries/avatar/consumer-rules.pro
  12. 21
      libraries/avatar/proguard-rules.pro
  13. 24
      libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt
  14. 4
      libraries/avatar/src/main/AndroidManifest.xml
  15. 33
      libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt
  16. 10
      libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt
  17. 41
      libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt
  18. 17
      libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt
  19. 24
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt
  20. 4
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt
  21. 11
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt
  22. 1
      settings.gradle.kts

1
features/roomlist/build.gradle.kts

@ -13,6 +13,7 @@ dependencies { @@ -13,6 +13,7 @@ dependencies {
implementation(libs.mavericks.compose)
implementation(libs.timber)
implementation(libs.datetime)
implementation(libs.accompanist.placeholder)
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")

161
features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt

@ -2,39 +2,30 @@ @@ -2,39 +2,30 @@
package io.element.android.x.features.roomlist
import Avatar
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExitToApp
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
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.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.core.data.LogCompositions
import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.roomlist.components.RoomItem
import io.element.android.x.features.roomlist.components.RoomListTopBar
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListViewState
import io.element.android.x.features.roomlist.model.stubbedRoomSummaries
import io.element.android.x.matrix.core.RoomId
@Composable
@ -87,125 +78,29 @@ fun RoomListContent( @@ -87,125 +78,29 @@ fun RoomListContent(
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RoomListTopBar(
matrixUser: MatrixUser?,
onLogoutClicked: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
) {
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
if (matrixUser == null) return
MediumTopAppBar(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection),
title = {
Text(
fontWeight = FontWeight.Bold,
text = "All Chats"
)
},
navigationIcon = {
IconButton(onClick = {}) {
Avatar(matrixUser.avatarData)
}
},
actions = {
IconButton(
onClick = onLogoutClicked
) {
Icon(Icons.Default.ExitToApp, contentDescription = "logout")
}
},
scrollBehavior = scrollBehavior,
)
}
@Preview
@Composable
private fun RoomItem(
modifier: Modifier = Modifier,
room: RoomListRoomSummary,
onClick: (RoomId) -> Unit
) {
if (room.isPlaceholder) {
return
}
Column(
modifier = modifier
.fillMaxWidth()
.height(72.dp)
.clickable(
onClick = { onClick(room.roomId) },
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() }
),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Avatar(room.avatarData)
Column(
modifier = Modifier
.padding(12.dp)
.weight(1f)
) {
Text(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
text = room.name,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = room.lastMessage?.toString().orEmpty(),
color = MaterialTheme.colorScheme.secondary,
fontSize = 15.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Column(
) {
Text(
fontSize = 12.sp,
text = room.timestamp ?: "",
color = MaterialTheme.colorScheme.secondary,
)
Spacer(modifier.size(4.dp))
val unreadIndicatorColor =
if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent
Box(
modifier = Modifier
.size(12.dp)
.clip(CircleShape)
.background(unreadIndicatorColor)
.align(Alignment.End),
)
}
}
private fun PreviewableRoomListContent() {
ElementXTheme(darkTheme = false) {
RoomListContent(
roomSummaries = stubbedRoomSummaries(),
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
onLogoutClicked = {}
)
}
}
@Preview
@Composable
private fun PreviewableRoomListContent() {
val roomSummaries = listOf(
RoomListRoomSummary(
name = "Room",
hasUnread = true,
timestamp = "14:18",
lastMessage = "A message",
avatarData = AvatarData("R"),
id = "roomId"
private fun PreviewableDarkRoomListContent() {
ElementXTheme(darkTheme = true) {
RoomListContent(
roomSummaries = stubbedRoomSummaries(),
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
onLogoutClicked = {}
)
)
RoomListContent(
roomSummaries = roomSummaries,
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
onLogoutClicked = {}
)
}
}
}

15
features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
package io.element.android.x.features.roomlist
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.Success
import io.element.android.x.core.data.parallelMap
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListViewState
@ -47,7 +47,7 @@ class RoomListViewModel(initialState: RoomListViewState) : @@ -47,7 +47,7 @@ class RoomListViewModel(initialState: RoomListViewState) :
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull()
val avatarData =
loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl, 32)
loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl, AvatarSize.SMALL)
MatrixUser(
username = userDisplayName ?: client.userId().value,
avatarUrl = userAvatarUrl,
@ -73,10 +73,7 @@ class RoomListViewModel(initialState: RoomListViewState) : @@ -73,10 +73,7 @@ class RoomListViewModel(initialState: RoomListViewState) :
): List<RoomListRoomSummary> {
return roomSummaries.parallelMap { roomSummary ->
when (roomSummary) {
is RoomSummary.Empty -> RoomListRoomSummary(
id = roomSummary.identifier,
isPlaceholder = true
)
is RoomSummary.Empty -> RoomListRoomSummary.placeholder(roomSummary.identifier)
is RoomSummary.Filled -> {
val avatarData = loadAvatarData(
client,
@ -100,17 +97,17 @@ class RoomListViewModel(initialState: RoomListViewState) : @@ -100,17 +97,17 @@ class RoomListViewModel(initialState: RoomListViewState) :
client: MatrixClient,
name: String,
url: String?,
size: Long = 48
size: AvatarSize = AvatarSize.MEDIUM
): AvatarData {
val mediaContent = url?.let {
val mediaSource = mediaSourceFromUrl(it)
client.loadMediaThumbnailForSource(mediaSource, size, size)
client.loadMediaThumbnailForSource(mediaSource, size.value.toLong(), size.value.toLong())
}
return mediaContent?.fold(
{ it },
{ null }
).let { model ->
AvatarData(name.first().toString(), model, size.toInt())
AvatarData(name.first().uppercase(), model, size)
}
}

157
features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt

@ -0,0 +1,157 @@ @@ -0,0 +1,157 @@
package io.element.android.x.features.roomlist.components
import Avatar
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.placeholder.material.placeholder
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.matrix.core.RoomId
@Composable
internal fun RoomItem(
modifier: Modifier = Modifier,
room: RoomListRoomSummary,
onClick: (RoomId) -> Unit
) {
if (room.isPlaceholder) {
return PlaceholderRoomItem(modifier = modifier, room = room)
}
Column(
modifier = modifier
.fillMaxWidth()
.clickable(
onClick = { onClick(room.roomId) },
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() }
),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.height(IntrinsicSize.Min),
verticalAlignment = CenterVertically
) {
Avatar(room.avatarData)
Column(
modifier = Modifier
.padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp)
.alignByBaseline()
.weight(1f)
) {
// Name
Text(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
text = room.name,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
// Last Message
Text(
text = room.lastMessage?.toString().orEmpty(),
color = MaterialTheme.colorScheme.secondary,
lineHeight = 20.sp,
fontSize = 15.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
// Timestamp and Unread
Column(
modifier = Modifier
.alignByBaseline(),
) {
Text(
fontSize = 12.sp,
text = room.timestamp ?: "",
color = MaterialTheme.colorScheme.secondary,
)
Spacer(modifier.size(4.dp))
val unreadIndicatorColor =
if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent
Box(
modifier = Modifier
.size(12.dp)
.clip(CircleShape)
.background(unreadIndicatorColor)
.align(Alignment.End),
)
}
}
}
}
@Composable
internal fun PlaceholderRoomItem(
modifier: Modifier = Modifier,
room: RoomListRoomSummary,
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = CenterVertically,
) {
Text(
modifier = Modifier
.size(room.avatarData.size.dp)
.clip(CircleShape)
.placeholder(true),
text = ""
)
Column(
modifier = Modifier
.padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp)
.weight(1f)
) {
Text(
modifier = Modifier
.size(width = 80.dp, height = 12.dp)
.placeholder(visible = true),
text = "",
)
Spacer(modifier = Modifier.size(4.dp))
Text(
modifier = Modifier
.size(width = 160.dp, height = 12.dp)
.placeholder(visible = true),
text = "",
)
}
Column {
Text(
modifier = Modifier
.size(width = 24.dp, height = 12.dp)
.placeholder(visible = true),
text = "",
color = MaterialTheme.colorScheme.secondary,
)
Spacer(Modifier.size(4.dp))
Box(
modifier = Modifier
.size(12.dp)
.clip(CircleShape)
.background(Color.Transparent)
.align(Alignment.End),
)
}
}
}

48
features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.x.features.roomlist.components
import Avatar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExitToApp
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontWeight
import io.element.android.x.core.data.LogCompositions
import io.element.android.x.features.roomlist.model.MatrixUser
@Composable
fun RoomListTopBar(
matrixUser: MatrixUser?,
onLogoutClicked: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
) {
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
MediumTopAppBar(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection),
title = {
Text(
fontWeight = FontWeight.Bold,
text = "All Chats"
)
},
navigationIcon = {
if (matrixUser != null) {
IconButton(onClick = {}) {
Avatar(matrixUser.avatarData)
}
}
},
actions = {
IconButton(
onClick = onLogoutClicked
) {
Icon(Icons.Default.ExitToApp, contentDescription = "logout")
}
},
scrollBehavior = scrollBehavior,
)
}

16
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt

@ -12,4 +12,18 @@ data class RoomListRoomSummary( @@ -12,4 +12,18 @@ data class RoomListRoomSummary(
val lastMessage: CharSequence? = null,
val avatarData: AvatarData = AvatarData(),
val isPlaceholder: Boolean = false,
)
) {
companion object {
fun placeholder(id: String): RoomListRoomSummary {
return RoomListRoomSummary(
id = id,
isPlaceholder = true,
name = "Short name",
timestamp = "hh:mm",
lastMessage = "Last message for placeholder",
avatarData = AvatarData("S")
)
}
}
}

25
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package io.element.android.x.features.roomlist.model
import io.element.android.x.designsystem.components.avatar.AvatarData
internal fun stubbedRoomSummaries(): List<RoomListRoomSummary> {
return listOf(
RoomListRoomSummary(
name = "Room",
hasUnread = true,
timestamp = "14:18",
lastMessage = "A very very very very long message which suites on two lines",
avatarData = AvatarData("R"),
id = "roomId"
),
RoomListRoomSummary(
name = "Room#2",
hasUnread = false,
timestamp = "14:16",
lastMessage = "A short message",
avatarData = AvatarData("Z"),
id = "roomId2"
),
RoomListRoomSummary.placeholder("roomId2")
)
}

1
gradle/libs.versions.toml

@ -57,6 +57,7 @@ accompanist_animation = { module = "com.google.accompanist:accompanist-navigatio @@ -57,6 +57,7 @@ accompanist_animation = { module = "com.google.accompanist:accompanist-navigatio
accompanist_permission = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
accompanist_material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" }
accompanist_systemui = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
accompanist_placeholder = { module = "com.google.accompanist:accompanist-placeholder-material", version.ref = "accompanist" }
# Test
test_junit = { module = "junit:junit", version.ref = "test_junit" }

1
libraries/avatar/.gitignore vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
/build

12
libraries/avatar/build.gradle.kts

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
plugins {
id("io.element.android-compose")
}
android {
namespace = "io.element.android.x.libraries.avatar"
}
dependencies {
implementation(project(":libraries:matrix"))
implementation(libs.coil.compose)
}

0
libraries/avatar/consumer-rules.pro

21
libraries/avatar/proguard-rules.pro vendored

@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

24
libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt

@ -1,24 +0,0 @@ @@ -1,24 +0,0 @@
package io.element.android.x.avatar
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.avatar.test", appContext.packageName)
}
}

4
libraries/avatar/src/main/AndroidManifest.xml

@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

33
libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt

@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
package io.element.android.x.avatar
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
/**
* TODO fallback Avatar
*/
@Composable
fun Avatar(avatarData: AvatarData) {
Image(
painter = rememberAsyncImagePainter(
model = avatarData.url,
onError = {
Log.e("TAG", "Error $it\n${it.result}", it.result.throwable)
}),
contentDescription = null,
modifier = Modifier
.size(avatarData.size)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
)
}

10
libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt

@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
package io.element.android.x.avatar
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
data class AvatarData(
val url: String,
val size: Dp = 48.dp
)

41
libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt

@ -1,41 +0,0 @@ @@ -1,41 +0,0 @@
package io.element.android.x.avatar
import coil.ImageLoader
import coil.fetch.FetchResult
import coil.fetch.Fetcher
import coil.request.Options
import io.element.android.x.matrix.MatrixClient
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
class AvatarFetcher(
private val matrixClient: MatrixClient,
private val avatarData: AvatarData,
private val options: Options,
private val imageLoader: ImageLoader
) :
Fetcher {
override suspend fun fetch(): FetchResult? {
val mediaSource = mediaSourceFromUrl(avatarData.url)
val mediaContent = matrixClient.loadMediaContentForSource(mediaSource)
return mediaContent.fold(
{ mediaContent ->
val byteArray = mediaContent.toUByteArray().toByteArray()
val fetcher = imageLoader.components.newFetcher(byteArray, options, imageLoader)
fetcher?.first?.fetch()
},
{null}
)
}
class Factory(private val matrixClient: MatrixClient) : Fetcher.Factory<AvatarData> {
override fun create(
data: AvatarData,
options: Options,
imageLoader: ImageLoader
): Fetcher? {
return AvatarFetcher(matrixClient, data, options, imageLoader)
}
}
}

17
libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt

@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
package io.element.android.x.avatar
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

24
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt

@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable @@ -8,6 +8,7 @@ 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.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
@ -20,18 +21,17 @@ import io.element.android.x.designsystem.components.avatar.AvatarData @@ -20,18 +21,17 @@ import io.element.android.x.designsystem.components.avatar.AvatarData
@Composable
fun Avatar(avatarData: AvatarData, modifier: Modifier = Modifier) {
val commonModifier = modifier
.size(avatarData.size.dp)
.clip(CircleShape)
if (avatarData.model == null) {
InitialsAvatar(
modifier = modifier
.size(avatarData.size.dp)
.clip(CircleShape),
modifier = commonModifier,
initials = avatarData.initials
)
} else {
ImageAvatar(
modifier = modifier
.size(avatarData.size.dp)
.clip(CircleShape),
modifier = commonModifier,
avatarData = avatarData
)
}
@ -50,8 +50,6 @@ private fun ImageAvatar( @@ -50,8 +50,6 @@ private fun ImageAvatar(
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = modifier
.size(avatarData.size.dp)
.clip(CircleShape)
)
}
@ -65,15 +63,15 @@ private fun InitialsAvatar( @@ -65,15 +63,15 @@ private fun InitialsAvatar(
listOf(
AvatarGradientStart,
AvatarGradientEnd,
)
),
start = Offset(0.0f, 100f),
end = Offset(100f, 0f)
)
Box(
modifier
.background(brush = initialsGradient)
modifier.background(brush = initialsGradient)
) {
Text(
modifier = Modifier
.align(Alignment.Center),
modifier = Modifier.align(Alignment.Center),
text = initials,
fontSize = 24.sp,
color = Color.White,

4
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt

@ -6,7 +6,7 @@ import androidx.compose.runtime.Stable @@ -6,7 +6,7 @@ import androidx.compose.runtime.Stable
data class AvatarData(
val initials: String = "",
val model: ByteArray? = null,
val size: Int = 0
val size: AvatarSize = AvatarSize.MEDIUM
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@ -27,7 +27,7 @@ data class AvatarData( @@ -27,7 +27,7 @@ data class AvatarData(
override fun hashCode(): Int {
var result = initials.hashCode()
result = 31 * result + (model?.contentHashCode() ?: 0)
result = 31 * result + size
result = 31 * result + size.value
return result
}

11
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
package io.element.android.x.designsystem.components.avatar
import androidx.compose.ui.unit.dp
enum class AvatarSize(val value: Int) {
SMALL(32),
MEDIUM(40),
BIG(48);
val dp = value.dp
}

1
settings.gradle.kts

@ -24,4 +24,3 @@ include(":features:login") @@ -24,4 +24,3 @@ include(":features:login")
include(":features:roomlist")
include(":features:messages")
include(":libraries:designsystem")
include(":libraries:avatar")

Loading…
Cancel
Save