Browse Source

Merge pull request #24 from vector-im/feature/bma/preferences

Preferences
feature/bma/flipper
Benoit Marty 2 years ago committed by GitHub
parent
commit
218113e0ca
  1. 1
      app/build.gradle.kts
  2. 2
      app/src/main/java/io/element/android/x/di/AppBindings.kt
  3. 7
      app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt
  4. 2
      app/src/main/java/io/element/android/x/initializer/MatrixInitializer.kt
  5. 2
      build.gradle.kts
  6. 1
      features/messages/build.gradle.kts
  7. 23
      features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt
  8. 21
      features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt
  9. 1
      features/preferences/build.gradle.kts
  10. 4
      features/preferences/src/main/java/io/element/android/x/features/preferences/PreferencesScreen.kt
  11. 44
      features/preferences/src/main/java/io/element/android/x/features/preferences/user/UserPreferences.kt
  12. 1
      features/roomlist/build.gradle.kts
  13. 12
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt
  14. 39
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt
  15. 2
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt
  16. 1
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListViewState.kt
  17. 17
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt
  18. 3
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt
  19. 1
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/Config.kt
  20. 17
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt
  21. 5
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt
  22. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt
  23. 4
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceText.kt
  24. 3
      libraries/matrix/build.gradle.kts
  25. 11
      libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt
  26. 7
      libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt
  27. 2
      libraries/matrix/src/main/java/io/element/android/x/matrix/core/MatrixPatterns.kt
  28. 5
      libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt
  29. 38
      libraries/matrixui/build.gradle.kts
  30. 18
      libraries/matrixui/src/main/AndroidManifest.xml
  31. 81
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt
  32. 34
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixUi.kt
  33. 112
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt
  34. 101
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/components/MatrixUserRow.kt
  35. 3
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/media/MediaFetcher.kt
  36. 3
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/media/MediaKeyer.kt
  37. 8
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/model/MatrixUser.kt
  38. 49
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/viewmodels/user/UserViewModel.kt
  39. 26
      libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/viewmodels/user/UserViewState.kt
  40. 1
      settings.gradle.kts

1
app/build.gradle.kts

@ -151,6 +151,7 @@ knit { @@ -151,6 +151,7 @@ knit {
dependencies {
implementation(project(":libraries:designsystem"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:matrixui"))
implementation(project(":libraries:core"))
implementation(project(":features:onboarding"))
implementation(project(":features:login"))

2
app/src/main/java/io/element/android/x/di/AppBindings.kt

@ -18,11 +18,13 @@ package io.element.android.x.di @@ -18,11 +18,13 @@ package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.x.matrix.Matrix
import io.element.android.x.matrix.ui.MatrixUi
import kotlinx.coroutines.CoroutineScope
@ContributesTo(AppScope::class)
interface AppBindings {
fun coroutineScope(): CoroutineScope
fun matrix(): Matrix
fun matrixUi(): MatrixUi
fun sessionComponentsOwner(): SessionComponentsOwner
}

7
app/src/main/java/io/element/android/x/initializer/CoilInitializer.kt

@ -35,19 +35,18 @@ class CoilInitializer : Initializer<Unit> { @@ -35,19 +35,18 @@ class CoilInitializer : Initializer<Unit> {
private class ElementImageLoaderFactory(
private val context: Context
) :
ImageLoaderFactory {
) : ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader
.Builder(context)
.components {
val appBindings = context.bindings<AppBindings>()
val matrix = appBindings.matrix()
val matrixUi = appBindings.matrixUi()
val matrixClientProvider = {
appBindings
.sessionComponentsOwner().activeSessionComponent?.matrixClient()
}
matrix.registerCoilComponents(this, matrixClientProvider)
matrixUi.registerCoilComponents(this, matrixClientProvider)
}
.build()
}

2
app/src/main/java/io/element/android/x/initializer/MatrixInitializer.kt

@ -18,9 +18,9 @@ package io.element.android.x.initializer @@ -18,9 +18,9 @@ package io.element.android.x.initializer
import android.content.Context
import androidx.startup.Initializer
import io.element.android.x.BuildConfig
import io.element.android.x.matrix.tracing.TracingConfigurations
import io.element.android.x.matrix.tracing.setupTracing
import io.element.android.x.sdk.matrix.BuildConfig
class MatrixInitializer : Initializer<Unit> {

2
build.gradle.kts

@ -95,6 +95,8 @@ allprojects { @@ -95,6 +95,8 @@ allprojects {
"experimental:kdoc-wrapping",
// Ignore error "Redundant curly braces", since we use it to fix false positives, for instance in "elementLogs.${i}.txt"
"string-template",
// Not the same order than Android Studio formatter...
"import-ordering",
)
)
}

1
features/messages/build.gradle.kts

@ -34,6 +34,7 @@ dependencies { @@ -34,6 +34,7 @@ dependencies {
implementation(project(":libraries:di"))
implementation(project(":libraries:core"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:matrixui"))
implementation(project(":libraries:designsystem"))
implementation(project(":libraries:textcomposer"))
implementation(libs.mavericks.compose)

23
features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
package io.element.android.x.features.messages
import androidx.recyclerview.widget.DiffUtil
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.features.messages.diff.CacheInvalidator
import io.element.android.x.features.messages.diff.MatrixTimelineItemsDiffCallback
@ -34,11 +33,10 @@ import io.element.android.x.features.messages.model.content.MessagesTimelineItem @@ -34,11 +33,10 @@ import io.element.android.x.features.messages.model.content.MessagesTimelineItem
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
import io.element.android.x.features.messages.util.invalidateLast
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.MatrixRoom
import io.element.android.x.matrix.timeline.MatrixTimelineItem
import kotlin.system.measureTimeMillis
import io.element.android.x.matrix.ui.MatrixItemHelper
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -52,9 +50,10 @@ import org.matrix.rustcomponents.sdk.FormattedBody @@ -52,9 +50,10 @@ import org.matrix.rustcomponents.sdk.FormattedBody
import org.matrix.rustcomponents.sdk.MessageFormat
import org.matrix.rustcomponents.sdk.MessageType
import timber.log.Timber
import kotlin.system.measureTimeMillis
class MessageTimelineItemStateFactory(
private val client: MatrixClient,
private val matrixItemHelper: MatrixItemHelper,
private val room: MatrixRoom,
private val dispatcher: CoroutineDispatcher,
) {
@ -153,7 +152,11 @@ class MessageTimelineItemStateFactory( @@ -153,7 +152,11 @@ class MessageTimelineItemStateFactory(
val senderDisplayName = room.userDisplayName(currentSender).getOrNull()
val senderAvatarUrl = room.userAvatarUrl(currentSender).getOrNull()
val senderAvatarData =
loadAvatarData(senderDisplayName ?: currentSender, senderAvatarUrl)
matrixItemHelper.loadAvatarData(
name = senderDisplayName ?: currentSender,
url = senderAvatarUrl,
size = AvatarSize.SMALL
)
return MessagesTimelineItemState.MessageEvent(
id = currentTimelineItem.uniqueId,
senderId = currentSender,
@ -243,14 +246,4 @@ class MessageTimelineItemStateFactory( @@ -243,14 +246,4 @@ class MessageTimelineItemStateFactory(
else -> MessagesItemGroupPosition.None
}
}
private suspend fun loadAvatarData(
name: String,
url: String?,
size: AvatarSize = AvatarSize.SMALL
): AvatarData {
val model = client.mediaResolver()
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
}

21
features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt

@ -23,7 +23,6 @@ import dagger.assisted.Assisted @@ -23,7 +23,6 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.x.anvilannotations.ContributesViewModel
import io.element.android.x.core.di.daggerMavericksViewModelFactory
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.di.SessionScope
import io.element.android.x.features.messages.model.MessagesItemAction
@ -33,9 +32,9 @@ import io.element.android.x.features.messages.model.MessagesViewState @@ -33,9 +32,9 @@ import io.element.android.x.features.messages.model.MessagesViewState
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.timeline.MatrixTimeline
import io.element.android.x.matrix.timeline.MatrixTimelineItem
import io.element.android.x.matrix.ui.MatrixItemHelper
import io.element.android.x.textcomposer.MessageComposerMode
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
@ -53,9 +52,10 @@ class MessagesViewModel @AssistedInject constructor( @@ -53,9 +52,10 @@ class MessagesViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<MessagesViewModel, MessagesViewState> by daggerMavericksViewModelFactory()
private val matrixItemHelper = MatrixItemHelper(client)
private val room = client.getRoom(initialState.roomId)!!
private val messageTimelineItemStateFactory =
MessageTimelineItemStateFactory(client, room, Dispatchers.Default)
MessageTimelineItemStateFactory(matrixItemHelper, room, Dispatchers.Default)
private val timeline = room.timeline()
private val timelineCallback = object : MatrixTimeline.Callback {
@ -161,7 +161,10 @@ class MessagesViewModel @AssistedInject constructor( @@ -161,7 +161,10 @@ class MessagesViewModel @AssistedInject constructor(
room.syncUpdateFlow()
.onEach {
val avatarData =
loadAvatarData(room.name ?: room.roomId.value, room.avatarUrl, AvatarSize.SMALL)
matrixItemHelper.loadAvatarData(
room = room,
size = AvatarSize.SMALL
)
setState {
copy(
roomName = room.name, roomAvatar = avatarData,
@ -217,16 +220,6 @@ class MessagesViewModel @AssistedInject constructor( @@ -217,16 +220,6 @@ class MessagesViewModel @AssistedInject constructor(
setSnackbarContent("Not implemented yet!")
}
private suspend fun loadAvatarData(
name: String,
url: String?,
size: AvatarSize = AvatarSize.MEDIUM
): AvatarData {
val model = client.mediaResolver()
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
override fun onCleared() {
super.onCleared()
timeline.callback = null

1
features/preferences/build.gradle.kts

@ -33,6 +33,7 @@ dependencies { @@ -33,6 +33,7 @@ dependencies {
anvil(project(":anvilcodegen"))
implementation(project(":libraries:di"))
implementation(project(":libraries:core"))
implementation(project(":libraries:matrixui"))
implementation(project(":features:rageshake"))
implementation(project(":features:logout"))
implementation(project(":libraries:designsystem"))

4
features/preferences/src/main/java/io/element/android/x/features/preferences/PreferencesScreen.kt

@ -23,6 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview @@ -23,6 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.x.designsystem.components.preferences.PreferenceScreen
import io.element.android.x.element.resources.R as ElementR
import io.element.android.x.features.logout.LogoutPreference
import io.element.android.x.features.preferences.user.UserPreferences
import io.element.android.x.features.rageshake.preferences.RageshakePreferences
@Composable
@ -52,8 +53,9 @@ fun PreferencesContent( @@ -52,8 +53,9 @@ fun PreferencesContent(
onBackPressed = onBackPressed,
title = stringResource(id = ElementR.string.settings)
) {
LogoutPreference(onSuccessLogout = onSuccessLogout)
UserPreferences()
RageshakePreferences(onOpenRageShake = onOpenRageShake)
LogoutPreference(onSuccessLogout = onSuccessLogout)
}
}

44
features/preferences/src/main/java/io/element/android/x/features/preferences/user/UserPreferences.kt

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.features.preferences.user
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.matrix.ui.components.MatrixUserHeader
import io.element.android.x.matrix.ui.viewmodels.user.UserViewModel
import io.element.android.x.matrix.ui.viewmodels.user.UserViewState
@Composable
fun UserPreferences(
modifier: Modifier = Modifier,
viewModel: UserViewModel = mavericksViewModel(),
) {
val user by viewModel.collectAsState(UserViewState::user)
when (user()) {
null -> Spacer(modifier = modifier.height(1.dp))
else -> MatrixUserHeader(
modifier = modifier,
matrixUser = user.invoke()!!
)
}
}

1
features/roomlist/build.gradle.kts

@ -34,6 +34,7 @@ dependencies { @@ -34,6 +34,7 @@ dependencies {
implementation(project(":libraries:di"))
implementation(project(":libraries:core"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:matrixui"))
implementation(project(":libraries:designsystem"))
implementation(project(":libraries:elementresources"))
implementation(libs.mavericks.compose)

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

@ -44,24 +44,28 @@ import io.element.android.x.designsystem.ElementXTheme @@ -44,24 +44,28 @@ import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.roomlist.components.RoomListTopBar
import io.element.android.x.features.roomlist.components.RoomSummaryRow
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
import io.element.android.x.matrix.core.UserId
import io.element.android.x.matrix.ui.model.MatrixUser
import io.element.android.x.matrix.ui.viewmodels.user.UserViewModel
import io.element.android.x.matrix.ui.viewmodels.user.UserViewState
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Composable
fun RoomListScreen(
viewModel: RoomListViewModel = mavericksViewModel(),
userViewModel: UserViewModel = mavericksViewModel(),
onRoomClicked: (RoomId) -> Unit = { },
onOpenSettings: () -> Unit = { },
) {
val filter by viewModel.collectAsState(RoomListViewState::filter)
LogCompositions(tag = "RoomListScreen", msg = "Root")
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
val matrixUser by userViewModel.collectAsState(UserViewState::user)
RoomListContent(
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
matrixUser = matrixUser(),
@ -154,7 +158,7 @@ fun PreviewableRoomListContent() { @@ -154,7 +158,7 @@ fun PreviewableRoomListContent() {
ElementXTheme(darkTheme = false) {
RoomListContent(
roomSummaries = stubbedRoomSummaries(),
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
filter = "filter",
onFilterChanged = {},
@ -169,7 +173,7 @@ fun PreviewableDarkRoomListContent() { @@ -169,7 +173,7 @@ fun PreviewableDarkRoomListContent() {
ElementXTheme(darkTheme = true) {
RoomListContent(
roomSummaries = stubbedRoomSummaries(),
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
filter = "filter",
onFilterChanged = {},

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

@ -25,16 +25,14 @@ import dagger.assisted.AssistedInject @@ -25,16 +25,14 @@ import dagger.assisted.AssistedInject
import io.element.android.x.anvilannotations.ContributesViewModel
import io.element.android.x.core.coroutine.parallelMap
import io.element.android.x.core.di.daggerMavericksViewModelFactory
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.di.SessionScope
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.RoomListRoomSummaryPlaceholders
import io.element.android.x.features.roomlist.model.RoomListViewState
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.RoomSummary
import io.element.android.x.matrix.ui.MatrixItemHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@ -53,6 +51,7 @@ class RoomListViewModel @AssistedInject constructor( @@ -53,6 +51,7 @@ class RoomListViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
private val lastMessageFormatter = LastMessageFormatter()
private val matrixUserHelper = MatrixItemHelper(client)
init {
handleInit()
@ -79,24 +78,6 @@ class RoomListViewModel @AssistedInject constructor( @@ -79,24 +78,6 @@ class RoomListViewModel @AssistedInject constructor(
}
private fun handleInit() {
suspend {
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull()
val avatarData =
loadAvatarData(
userDisplayName ?: client.userId().value,
userAvatarUrl,
AvatarSize.SMALL
)
MatrixUser(
username = userDisplayName ?: client.userId().value,
avatarUrl = userAvatarUrl,
avatarData = avatarData,
)
}.execute {
copy(user = it)
}
// Observe the room list and the filter
combine(
client.roomSummaryDataSource().roomSummaries()
@ -136,9 +117,9 @@ class RoomListViewModel @AssistedInject constructor( @@ -136,9 +117,9 @@ class RoomListViewModel @AssistedInject constructor(
when (roomSummary) {
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
is RoomSummary.Filled -> {
val avatarData = loadAvatarData(
roomSummary.details.name,
roomSummary.details.avatarURLString
val avatarData = matrixUserHelper.loadAvatarData(
roomSummary = roomSummary,
size = AvatarSize.MEDIUM
)
RoomListRoomSummary(
id = roomSummary.identifier(),
@ -152,14 +133,4 @@ class RoomListViewModel @AssistedInject constructor( @@ -152,14 +133,4 @@ class RoomListViewModel @AssistedInject constructor(
}
}
}
private suspend fun loadAvatarData(
name: String,
url: String?,
size: AvatarSize = AvatarSize.MEDIUM
): AvatarData {
val model = client.mediaResolver()
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
}

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

@ -54,7 +54,7 @@ import androidx.compose.ui.unit.sp @@ -54,7 +54,7 @@ import androidx.compose.ui.unit.sp
import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.core.compose.textFieldState
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.matrix.ui.model.MatrixUser
@Composable
fun RoomListTopBar(

1
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListViewState.kt

@ -22,7 +22,6 @@ import com.airbnb.mvrx.Uninitialized @@ -22,7 +22,6 @@ import com.airbnb.mvrx.Uninitialized
import io.element.android.x.matrix.core.RoomId
data class RoomListViewState(
val user: Async<MatrixUser> = Uninitialized,
// Will contain the filtered rooms, using ::filter (if filter is not empty)
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
val filter: String = "",

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

@ -29,6 +29,7 @@ import androidx.compose.ui.geometry.Offset @@ -29,6 +29,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import io.element.android.x.designsystem.AvatarGradientEnd
@ -42,13 +43,13 @@ fun Avatar(avatarData: AvatarData, modifier: Modifier = Modifier) { @@ -42,13 +43,13 @@ fun Avatar(avatarData: AvatarData, modifier: Modifier = Modifier) {
.clip(CircleShape)
if (avatarData.model == null) {
InitialsAvatar(
avatarData = avatarData,
modifier = commonModifier,
initials = avatarData.name.first().uppercase()
)
} else {
ImageAvatar(
avatarData = avatarData,
modifier = commonModifier,
avatarData = avatarData
)
}
}
@ -71,7 +72,7 @@ private fun ImageAvatar( @@ -71,7 +72,7 @@ private fun ImageAvatar(
@Composable
private fun InitialsAvatar(
initials: String,
avatarData: AvatarData,
modifier: Modifier = Modifier,
) {
val initialsGradient = Brush.linearGradient(
@ -87,9 +88,15 @@ private fun InitialsAvatar( @@ -87,9 +88,15 @@ private fun InitialsAvatar(
) {
Text(
modifier = Modifier.align(Alignment.Center),
text = initials,
fontSize = 24.sp,
text = avatarData.name.first().uppercase(),
fontSize = (avatarData.size.value / 2).sp,
color = Color.White,
)
}
}
@Preview
@Composable
fun InitialsAvatar() {
InitialsAvatar(AvatarData("A"))
}

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

@ -21,7 +21,8 @@ import androidx.compose.ui.unit.dp @@ -21,7 +21,8 @@ import androidx.compose.ui.unit.dp
enum class AvatarSize(val value: Int) {
SMALL(32),
MEDIUM(40),
BIG(48);
BIG(48),
HUGE(96);
val dp = value.dp
}

1
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/Config.kt

@ -19,3 +19,4 @@ package io.element.android.x.designsystem.components.preferences @@ -19,3 +19,4 @@ package io.element.android.x.designsystem.components.preferences
import androidx.compose.ui.unit.dp
internal val preferenceMinHeight = 80.dp
internal val preferencePaddingEnd = 16.dp

17
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceScreen.kt

@ -20,8 +20,13 @@ import androidx.compose.foundation.layout.Column @@ -20,8 +20,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
@ -47,7 +52,10 @@ fun PreferenceScreen( @@ -47,7 +52,10 @@ fun PreferenceScreen(
content: @Composable ColumnScope.() -> Unit,
) {
Scaffold(
modifier = modifier,
modifier = modifier
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
contentWindowInsets = WindowInsets.statusBars,
topBar = {
PreferenceTopAppBar(
@ -56,8 +64,13 @@ fun PreferenceScreen( @@ -56,8 +64,13 @@ fun PreferenceScreen(
)
},
content = {
val scrollState = rememberScrollState()
Column(
modifier = Modifier.padding(it)
modifier = Modifier
.padding(it)
.verticalScroll(
state = scrollState,
)
) {
content()
}

5
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceSlide.kt

@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Column @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
@ -54,7 +55,9 @@ fun PreferenceSlide( @@ -54,7 +55,9 @@ fun PreferenceSlide(
Row(modifier = Modifier.fillMaxWidth()) {
PreferenceIcon(icon = icon)
Column(
modifier = Modifier.weight(1f),
modifier = Modifier
.weight(1f)
.padding(end = preferencePaddingEnd),
) {
Text(
modifier = Modifier.fillMaxWidth(),

2
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceSwitch.kt

@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Announcement
import androidx.compose.material3.Checkbox
@ -65,6 +66,7 @@ fun PreferenceSwitch( @@ -65,6 +66,7 @@ fun PreferenceSwitch(
text = title
)
Checkbox(
modifier = Modifier.padding(end = preferencePaddingEnd),
checked = isChecked,
enabled = enabled,
onCheckedChange = onCheckedChange

4
libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/preferences/PreferenceText.kt

@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.MaterialTheme
@ -53,7 +54,8 @@ fun PreferenceText( @@ -53,7 +54,8 @@ fun PreferenceText(
PreferenceIcon(icon = icon)
Text(
modifier = Modifier
.weight(1f),
.weight(1f)
.padding(end = preferencePaddingEnd),
style = MaterialTheme.typography.bodyLarge,
text = title
)

3
libraries/matrix/build.gradle.kts

@ -21,7 +21,7 @@ plugins { @@ -21,7 +21,7 @@ plugins {
}
android {
namespace = "io.element.android.x.sdk.matrix"
namespace = "io.element.android.x.matrix"
}
anvil {
@ -33,7 +33,6 @@ dependencies { @@ -33,7 +33,6 @@ dependencies {
implementation(project(":libraries:di"))
implementation(project(":libraries:core"))
implementation("net.java.dev.jna:jna:5.12.1@aar")
implementation(libs.coil.compose)
implementation(libs.androidx.datastore.preferences)
implementation(libs.serialization.json)
}

11
libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt

@ -17,13 +17,10 @@ @@ -17,13 +17,10 @@
package io.element.android.x.matrix
import android.content.Context
import coil.ComponentRegistry
import io.element.android.x.core.coroutine.CoroutineDispatchers
import io.element.android.x.di.AppScope
import io.element.android.x.di.ApplicationContext
import io.element.android.x.di.SingleIn
import io.element.android.x.matrix.media.MediaFetcher
import io.element.android.x.matrix.media.MediaKeyer
import io.element.android.x.matrix.session.SessionStore
import io.element.android.x.matrix.util.logError
import java.io.File
@ -58,14 +55,6 @@ class Matrix @Inject constructor( @@ -58,14 +55,6 @@ class Matrix @Inject constructor(
return sessionStore.isLoggedIn()
}
fun registerCoilComponents(
builder: ComponentRegistry.Builder,
activeClientProvider: () -> MatrixClient?
) {
builder.add(MediaKeyer())
builder.add(MediaFetcher.Factory(activeClientProvider))
}
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
sessionStore.getLatestSession()
?.let { session ->

7
libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt

@ -25,9 +25,6 @@ import io.element.android.x.matrix.room.RoomSummaryDataSource @@ -25,9 +25,6 @@ import io.element.android.x.matrix.room.RoomSummaryDataSource
import io.element.android.x.matrix.room.RustRoomSummaryDataSource
import io.element.android.x.matrix.session.SessionStore
import io.element.android.x.matrix.sync.SlidingSyncObserverProxy
import java.io.Closeable
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.Client
@ -38,6 +35,9 @@ import org.matrix.rustcomponents.sdk.SlidingSyncMode @@ -38,6 +35,9 @@ import org.matrix.rustcomponents.sdk.SlidingSyncMode
import org.matrix.rustcomponents.sdk.SlidingSyncViewBuilder
import org.matrix.rustcomponents.sdk.StoppableSpawn
import timber.log.Timber
import java.io.Closeable
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
class MatrixClient internal constructor(
private val client: Client,
@ -157,6 +157,7 @@ class MatrixClient internal constructor( @@ -157,6 +157,7 @@ class MatrixClient internal constructor(
}
fun userId(): UserId = UserId(client.userId())
suspend fun loadUserDisplayName(): Result<String> = withContext(dispatchers.io) {
runCatching {
client.displayName()

2
libraries/matrix/src/main/java/io/element/android/x/matrix/core/MatrixPatterns.kt

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
package io.element.android.x.matrix.core
import io.element.android.x.sdk.matrix.BuildConfig
import io.element.android.x.matrix.BuildConfig
import timber.log.Timber
/**

5
libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt

@ -65,6 +65,11 @@ class MatrixRoom( @@ -65,6 +65,11 @@ class MatrixRoom(
return slidingSyncRoom.name()
}
val bestName: String
get() {
return name?.takeIf { it.isNotEmpty() } ?: room.id()
}
val displayName: String
get() {
return room.displayName()

38
libraries/matrixui/build.gradle.kts

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
}
android {
namespace = "io.element.android.x.matrix.ui"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(project(":anvilannotations"))
anvil(project(":anvilcodegen"))
implementation(project(":libraries:di"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:designsystem"))
implementation(project(":libraries:core"))
implementation(libs.coil.compose)
}

18
libraries/matrixui/src/main/AndroidManifest.xml

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest/>

81
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.MatrixRoom
import io.element.android.x.matrix.room.RoomSummary
import io.element.android.x.matrix.ui.model.MatrixUser
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
class MatrixItemHelper(
private val client: MatrixClient
) {
/**
* TODO Make username and avatar live...
*/
@OptIn(FlowPreview::class)
fun getCurrentUserData(avatarSize: AvatarSize): Flow<MatrixUser> {
return suspend {
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull()
val avatarData =
loadAvatarData(
userDisplayName ?: client.userId().value,
userAvatarUrl,
avatarSize
)
MatrixUser(
id = client.userId(),
username = userDisplayName,
avatarUrl = userAvatarUrl,
avatarData = avatarData,
)
}.asFlow()
}
suspend fun loadAvatarData(room: MatrixRoom, size: AvatarSize): AvatarData {
return loadAvatarData(
name = room.bestName,
url = room.avatarUrl,
size = size
)
}
suspend fun loadAvatarData(roomSummary: RoomSummary.Filled, size: AvatarSize): AvatarData {
return loadAvatarData(
name = roomSummary.details.name,
url = roomSummary.details.avatarURLString,
size = size
)
}
suspend fun loadAvatarData(
name: String,
url: String?,
size: AvatarSize
): AvatarData {
val model = client.mediaResolver()
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
}

34
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixUi.kt

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui
import coil.ComponentRegistry
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.ui.media.MediaFetcher
import io.element.android.x.matrix.ui.media.MediaKeyer
import javax.inject.Inject
class MatrixUi @Inject constructor() {
fun registerCoilComponents(
builder: ComponentRegistry.Builder,
activeClientProvider: () -> MatrixClient?
) {
builder.add(MediaKeyer())
builder.add(MediaFetcher.Factory(activeClientProvider))
}
}

112
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/components/MatrixUserHeader.kt

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.matrix.core.UserId
import io.element.android.x.matrix.ui.model.MatrixUser
import io.element.android.x.matrix.ui.model.getBestName
@Composable
fun MatrixUserHeader(
matrixUser: MatrixUser,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
Column(
modifier = modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(all = 16.dp)
.height(IntrinsicSize.Min),
horizontalAlignment = Alignment.CenterHorizontally
) {
Avatar(
matrixUser.avatarData.copy(size = AvatarSize.HUGE),
)
Spacer(modifier = Modifier.height(16.dp))
// Name
Text(
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
text = matrixUser.getBestName(),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
// Id
if (matrixUser.username.isNullOrEmpty().not()) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = matrixUser.id.value,
color = MaterialTheme.colorScheme.secondary,
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
@Preview
@Composable
fun MatrixUserHeaderPreview() {
ElementXTheme {
MatrixUserHeader(
MatrixUser(
id = UserId("@alice:server.org"),
username = "Alice",
avatarUrl = null,
avatarData = AvatarData("Alice")
)
)
}
}
@Preview
@Composable
fun MatrixUserHeaderNoUsernamePreview() {
ElementXTheme {
MatrixUserHeader(
MatrixUser(
id = UserId("@alice:server.org"),
username = null,
avatarUrl = null,
avatarData = AvatarData("Alice")
)
)
}
}

101
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/components/MatrixUserRow.kt

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.matrix.core.UserId
import io.element.android.x.matrix.ui.model.MatrixUser
import io.element.android.x.matrix.ui.model.getBestName
@Composable
fun MatrixUserRow(
matrixUser: MatrixUser,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
Row(
modifier = modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(horizontal = 16.dp)
.height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(
matrixUser.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 = matrixUser.getBestName(),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
// Id
if (matrixUser.username.isNullOrEmpty().not()) {
Text(
text = matrixUser.id.value,
color = MaterialTheme.colorScheme.secondary,
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
@Preview
@Composable
fun MatrixUserRowPreview() {
ElementXTheme {
MatrixUserRow(
MatrixUser(
id = UserId("@alice:server.org"),
username = "Alice",
avatarUrl = null,
avatarData = AvatarData("Alice")
)
)
}
}

3
libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaFetcher.kt → libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/media/MediaFetcher.kt

@ -14,13 +14,14 @@ @@ -14,13 +14,14 @@
* limitations under the License.
*/
package io.element.android.x.matrix.media
package io.element.android.x.matrix.ui.media
import coil.ImageLoader
import coil.fetch.FetchResult
import coil.fetch.Fetcher
import coil.request.Options
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import java.nio.ByteBuffer
internal class MediaFetcher(

3
libraries/matrix/src/main/java/io/element/android/x/matrix/media/MediaKeyer.kt → libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/media/MediaKeyer.kt

@ -14,10 +14,11 @@ @@ -14,10 +14,11 @@
* limitations under the License.
*/
package io.element.android.x.matrix.media
package io.element.android.x.matrix.ui.media
import coil.key.Keyer
import coil.request.Options
import io.element.android.x.matrix.media.MediaResolver
internal class MediaKeyer : Keyer<MediaResolver.Meta> {
override fun key(data: MediaResolver.Meta, options: Options): String? {

8
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/MatrixUser.kt → libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/model/MatrixUser.kt

@ -14,14 +14,20 @@ @@ -14,14 +14,20 @@
* limitations under the License.
*/
package io.element.android.x.features.roomlist.model
package io.element.android.x.matrix.ui.model
import androidx.compose.runtime.Stable
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.matrix.core.UserId
@Stable
data class MatrixUser(
val id: UserId,
val username: String? = null,
val avatarUrl: String? = null,
val avatarData: AvatarData = AvatarData(),
)
fun MatrixUser.getBestName(): String {
return username?.takeIf { it.isNotEmpty() } ?: id.value
}

49
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/viewmodels/user/UserViewModel.kt

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui.viewmodels.user
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.x.anvilannotations.ContributesViewModel
import io.element.android.x.core.di.daggerMavericksViewModelFactory
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.di.SessionScope
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.ui.MatrixItemHelper
@ContributesViewModel(SessionScope::class)
class UserViewModel @AssistedInject constructor(
client: MatrixClient,
@Assisted initialState: UserViewState
) : MavericksViewModel<UserViewState>(initialState) {
companion object : MavericksViewModelFactory<UserViewModel, UserViewState> by daggerMavericksViewModelFactory()
private val matrixUserHelper = MatrixItemHelper(client)
init {
handleInit()
}
private fun handleInit() {
matrixUserHelper.getCurrentUserData(avatarSize = AvatarSize.SMALL).execute {
copy(user = it)
}
}
}

26
libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/viewmodels/user/UserViewState.kt

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.matrix.ui.viewmodels.user
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
import io.element.android.x.matrix.ui.model.MatrixUser
data class UserViewState(
val user: Async<MatrixUser> = Uninitialized,
) : MavericksState

1
settings.gradle.kts

@ -37,6 +37,7 @@ include(":app") @@ -37,6 +37,7 @@ include(":app")
include(":libraries:core")
include(":libraries:rustsdk")
include(":libraries:matrix")
include(":libraries:matrixui")
include(":libraries:textcomposer")
include(":libraries:elementresources")
include(":features:onboarding")

Loading…
Cancel
Save