Merge pull request #24 from vector-im/feature/bma/preferences
Preferences
This commit is contained in:
commit
509ce4bf17
40 changed files with 609 additions and 105 deletions
|
|
@ -151,6 +151,7 @@ knit {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:matrix"))
|
implementation(project(":libraries:matrix"))
|
||||||
|
implementation(project(":libraries:matrixui"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":features:onboarding"))
|
implementation(project(":features:onboarding"))
|
||||||
implementation(project(":features:login"))
|
implementation(project(":features:login"))
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,13 @@ package io.element.android.x.di
|
||||||
|
|
||||||
import com.squareup.anvil.annotations.ContributesTo
|
import com.squareup.anvil.annotations.ContributesTo
|
||||||
import io.element.android.x.matrix.Matrix
|
import io.element.android.x.matrix.Matrix
|
||||||
|
import io.element.android.x.matrix.ui.MatrixUi
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
@ContributesTo(AppScope::class)
|
@ContributesTo(AppScope::class)
|
||||||
interface AppBindings {
|
interface AppBindings {
|
||||||
fun coroutineScope(): CoroutineScope
|
fun coroutineScope(): CoroutineScope
|
||||||
fun matrix(): Matrix
|
fun matrix(): Matrix
|
||||||
|
fun matrixUi(): MatrixUi
|
||||||
fun sessionComponentsOwner(): SessionComponentsOwner
|
fun sessionComponentsOwner(): SessionComponentsOwner
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,19 +35,18 @@ class CoilInitializer : Initializer<Unit> {
|
||||||
|
|
||||||
private class ElementImageLoaderFactory(
|
private class ElementImageLoaderFactory(
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) :
|
) : ImageLoaderFactory {
|
||||||
ImageLoaderFactory {
|
|
||||||
override fun newImageLoader(): ImageLoader {
|
override fun newImageLoader(): ImageLoader {
|
||||||
return ImageLoader
|
return ImageLoader
|
||||||
.Builder(context)
|
.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
val appBindings = context.bindings<AppBindings>()
|
val appBindings = context.bindings<AppBindings>()
|
||||||
val matrix = appBindings.matrix()
|
val matrixUi = appBindings.matrixUi()
|
||||||
val matrixClientProvider = {
|
val matrixClientProvider = {
|
||||||
appBindings
|
appBindings
|
||||||
.sessionComponentsOwner().activeSessionComponent?.matrixClient()
|
.sessionComponentsOwner().activeSessionComponent?.matrixClient()
|
||||||
}
|
}
|
||||||
matrix.registerCoilComponents(this, matrixClientProvider)
|
matrixUi.registerCoilComponents(this, matrixClientProvider)
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ package io.element.android.x.initializer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.startup.Initializer
|
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.TracingConfigurations
|
||||||
import io.element.android.x.matrix.tracing.setupTracing
|
import io.element.android.x.matrix.tracing.setupTracing
|
||||||
import io.element.android.x.sdk.matrix.BuildConfig
|
|
||||||
|
|
||||||
class MatrixInitializer : Initializer<Unit> {
|
class MatrixInitializer : Initializer<Unit> {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,8 @@ allprojects {
|
||||||
"experimental:kdoc-wrapping",
|
"experimental:kdoc-wrapping",
|
||||||
// Ignore error "Redundant curly braces", since we use it to fix false positives, for instance in "elementLogs.${i}.txt"
|
// Ignore error "Redundant curly braces", since we use it to fix false positives, for instance in "elementLogs.${i}.txt"
|
||||||
"string-template",
|
"string-template",
|
||||||
|
// Not the same order than Android Studio formatter...
|
||||||
|
"import-ordering",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ dependencies {
|
||||||
implementation(project(":libraries:di"))
|
implementation(project(":libraries:di"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":libraries:matrix"))
|
implementation(project(":libraries:matrix"))
|
||||||
|
implementation(project(":libraries:matrixui"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:textcomposer"))
|
implementation(project(":libraries:textcomposer"))
|
||||||
implementation(libs.mavericks.compose)
|
implementation(libs.mavericks.compose)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package io.element.android.x.features.messages
|
package io.element.android.x.features.messages
|
||||||
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
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.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.x.features.messages.diff.CacheInvalidator
|
import io.element.android.x.features.messages.diff.CacheInvalidator
|
||||||
import io.element.android.x.features.messages.diff.MatrixTimelineItemsDiffCallback
|
import io.element.android.x.features.messages.diff.MatrixTimelineItemsDiffCallback
|
||||||
|
|
@ -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.MessagesTimelineItemTextContent
|
||||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
|
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
|
||||||
import io.element.android.x.features.messages.util.invalidateLast
|
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.media.MediaResolver
|
||||||
import io.element.android.x.matrix.room.MatrixRoom
|
import io.element.android.x.matrix.room.MatrixRoom
|
||||||
import io.element.android.x.matrix.timeline.MatrixTimelineItem
|
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.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
@ -52,9 +50,10 @@ import org.matrix.rustcomponents.sdk.FormattedBody
|
||||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||||
import org.matrix.rustcomponents.sdk.MessageType
|
import org.matrix.rustcomponents.sdk.MessageType
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class MessageTimelineItemStateFactory(
|
class MessageTimelineItemStateFactory(
|
||||||
private val client: MatrixClient,
|
private val matrixItemHelper: MatrixItemHelper,
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
private val dispatcher: CoroutineDispatcher,
|
private val dispatcher: CoroutineDispatcher,
|
||||||
) {
|
) {
|
||||||
|
|
@ -153,7 +152,11 @@ class MessageTimelineItemStateFactory(
|
||||||
val senderDisplayName = room.userDisplayName(currentSender).getOrNull()
|
val senderDisplayName = room.userDisplayName(currentSender).getOrNull()
|
||||||
val senderAvatarUrl = room.userAvatarUrl(currentSender).getOrNull()
|
val senderAvatarUrl = room.userAvatarUrl(currentSender).getOrNull()
|
||||||
val senderAvatarData =
|
val senderAvatarData =
|
||||||
loadAvatarData(senderDisplayName ?: currentSender, senderAvatarUrl)
|
matrixItemHelper.loadAvatarData(
|
||||||
|
name = senderDisplayName ?: currentSender,
|
||||||
|
url = senderAvatarUrl,
|
||||||
|
size = AvatarSize.SMALL
|
||||||
|
)
|
||||||
return MessagesTimelineItemState.MessageEvent(
|
return MessagesTimelineItemState.MessageEvent(
|
||||||
id = currentTimelineItem.uniqueId,
|
id = currentTimelineItem.uniqueId,
|
||||||
senderId = currentSender,
|
senderId = currentSender,
|
||||||
|
|
@ -243,14 +246,4 @@ class MessageTimelineItemStateFactory(
|
||||||
else -> MessagesItemGroupPosition.None
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
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.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.x.di.SessionScope
|
import io.element.android.x.di.SessionScope
|
||||||
import io.element.android.x.features.messages.model.MessagesItemAction
|
import io.element.android.x.features.messages.model.MessagesItemAction
|
||||||
|
|
@ -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.MessagesTimelineItemRedactedContent
|
||||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
|
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
|
||||||
import io.element.android.x.matrix.MatrixClient
|
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.MatrixTimeline
|
||||||
import io.element.android.x.matrix.timeline.MatrixTimelineItem
|
import io.element.android.x.matrix.timeline.MatrixTimelineItem
|
||||||
|
import io.element.android.x.matrix.ui.MatrixItemHelper
|
||||||
import io.element.android.x.textcomposer.MessageComposerMode
|
import io.element.android.x.textcomposer.MessageComposerMode
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
|
@ -53,9 +52,10 @@ class MessagesViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<MessagesViewModel, MessagesViewState> by daggerMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<MessagesViewModel, MessagesViewState> by daggerMavericksViewModelFactory()
|
||||||
|
|
||||||
|
private val matrixItemHelper = MatrixItemHelper(client)
|
||||||
private val room = client.getRoom(initialState.roomId)!!
|
private val room = client.getRoom(initialState.roomId)!!
|
||||||
private val messageTimelineItemStateFactory =
|
private val messageTimelineItemStateFactory =
|
||||||
MessageTimelineItemStateFactory(client, room, Dispatchers.Default)
|
MessageTimelineItemStateFactory(matrixItemHelper, room, Dispatchers.Default)
|
||||||
private val timeline = room.timeline()
|
private val timeline = room.timeline()
|
||||||
|
|
||||||
private val timelineCallback = object : MatrixTimeline.Callback {
|
private val timelineCallback = object : MatrixTimeline.Callback {
|
||||||
|
|
@ -161,7 +161,10 @@ class MessagesViewModel @AssistedInject constructor(
|
||||||
room.syncUpdateFlow()
|
room.syncUpdateFlow()
|
||||||
.onEach {
|
.onEach {
|
||||||
val avatarData =
|
val avatarData =
|
||||||
loadAvatarData(room.name ?: room.roomId.value, room.avatarUrl, AvatarSize.SMALL)
|
matrixItemHelper.loadAvatarData(
|
||||||
|
room = room,
|
||||||
|
size = AvatarSize.SMALL
|
||||||
|
)
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
roomName = room.name, roomAvatar = avatarData,
|
roomName = room.name, roomAvatar = avatarData,
|
||||||
|
|
@ -217,16 +220,6 @@ class MessagesViewModel @AssistedInject constructor(
|
||||||
setSnackbarContent("Not implemented yet!")
|
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() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
timeline.callback = null
|
timeline.callback = null
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ dependencies {
|
||||||
anvil(project(":anvilcodegen"))
|
anvil(project(":anvilcodegen"))
|
||||||
implementation(project(":libraries:di"))
|
implementation(project(":libraries:di"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
|
implementation(project(":libraries:matrixui"))
|
||||||
implementation(project(":features:rageshake"))
|
implementation(project(":features:rageshake"))
|
||||||
implementation(project(":features:logout"))
|
implementation(project(":features:logout"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
|
|
|
||||||
|
|
@ -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.designsystem.components.preferences.PreferenceScreen
|
||||||
import io.element.android.x.element.resources.R as ElementR
|
import io.element.android.x.element.resources.R as ElementR
|
||||||
import io.element.android.x.features.logout.LogoutPreference
|
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
|
import io.element.android.x.features.rageshake.preferences.RageshakePreferences
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -52,8 +53,9 @@ fun PreferencesContent(
|
||||||
onBackPressed = onBackPressed,
|
onBackPressed = onBackPressed,
|
||||||
title = stringResource(id = ElementR.string.settings)
|
title = stringResource(id = ElementR.string.settings)
|
||||||
) {
|
) {
|
||||||
LogoutPreference(onSuccessLogout = onSuccessLogout)
|
UserPreferences()
|
||||||
RageshakePreferences(onOpenRageShake = onOpenRageShake)
|
RageshakePreferences(onOpenRageShake = onOpenRageShake)
|
||||||
|
LogoutPreference(onSuccessLogout = onSuccessLogout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ dependencies {
|
||||||
implementation(project(":libraries:di"))
|
implementation(project(":libraries:di"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":libraries:matrix"))
|
implementation(project(":libraries:matrix"))
|
||||||
|
implementation(project(":libraries:matrixui"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.mavericks.compose)
|
implementation(libs.mavericks.compose)
|
||||||
|
|
|
||||||
|
|
@ -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.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.features.roomlist.components.RoomListTopBar
|
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.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.RoomListRoomSummary
|
||||||
import io.element.android.x.features.roomlist.model.RoomListViewState
|
import io.element.android.x.features.roomlist.model.RoomListViewState
|
||||||
import io.element.android.x.features.roomlist.model.stubbedRoomSummaries
|
import io.element.android.x.features.roomlist.model.stubbedRoomSummaries
|
||||||
import io.element.android.x.matrix.core.RoomId
|
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.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomListScreen(
|
fun RoomListScreen(
|
||||||
viewModel: RoomListViewModel = mavericksViewModel(),
|
viewModel: RoomListViewModel = mavericksViewModel(),
|
||||||
|
userViewModel: UserViewModel = mavericksViewModel(),
|
||||||
onRoomClicked: (RoomId) -> Unit = { },
|
onRoomClicked: (RoomId) -> Unit = { },
|
||||||
onOpenSettings: () -> Unit = { },
|
onOpenSettings: () -> Unit = { },
|
||||||
) {
|
) {
|
||||||
val filter by viewModel.collectAsState(RoomListViewState::filter)
|
val filter by viewModel.collectAsState(RoomListViewState::filter)
|
||||||
LogCompositions(tag = "RoomListScreen", msg = "Root")
|
LogCompositions(tag = "RoomListScreen", msg = "Root")
|
||||||
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
|
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
|
||||||
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
|
val matrixUser by userViewModel.collectAsState(UserViewState::user)
|
||||||
RoomListContent(
|
RoomListContent(
|
||||||
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
|
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
|
||||||
matrixUser = matrixUser(),
|
matrixUser = matrixUser(),
|
||||||
|
|
@ -154,7 +158,7 @@ fun PreviewableRoomListContent() {
|
||||||
ElementXTheme(darkTheme = false) {
|
ElementXTheme(darkTheme = false) {
|
||||||
RoomListContent(
|
RoomListContent(
|
||||||
roomSummaries = stubbedRoomSummaries(),
|
roomSummaries = stubbedRoomSummaries(),
|
||||||
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
|
||||||
onRoomClicked = {},
|
onRoomClicked = {},
|
||||||
filter = "filter",
|
filter = "filter",
|
||||||
onFilterChanged = {},
|
onFilterChanged = {},
|
||||||
|
|
@ -169,7 +173,7 @@ fun PreviewableDarkRoomListContent() {
|
||||||
ElementXTheme(darkTheme = true) {
|
ElementXTheme(darkTheme = true) {
|
||||||
RoomListContent(
|
RoomListContent(
|
||||||
roomSummaries = stubbedRoomSummaries(),
|
roomSummaries = stubbedRoomSummaries(),
|
||||||
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
|
||||||
onRoomClicked = {},
|
onRoomClicked = {},
|
||||||
filter = "filter",
|
filter = "filter",
|
||||||
onFilterChanged = {},
|
onFilterChanged = {},
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,14 @@ import dagger.assisted.AssistedInject
|
||||||
import io.element.android.x.anvilannotations.ContributesViewModel
|
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||||
import io.element.android.x.core.coroutine.parallelMap
|
import io.element.android.x.core.coroutine.parallelMap
|
||||||
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
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.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.x.di.SessionScope
|
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.RoomListRoomSummary
|
||||||
import io.element.android.x.features.roomlist.model.RoomListRoomSummaryPlaceholders
|
import io.element.android.x.features.roomlist.model.RoomListRoomSummaryPlaceholders
|
||||||
import io.element.android.x.features.roomlist.model.RoomListViewState
|
import io.element.android.x.features.roomlist.model.RoomListViewState
|
||||||
import io.element.android.x.matrix.MatrixClient
|
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.room.RoomSummary
|
||||||
|
import io.element.android.x.matrix.ui.MatrixItemHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
@ -53,6 +51,7 @@ class RoomListViewModel @AssistedInject constructor(
|
||||||
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
|
||||||
|
|
||||||
private val lastMessageFormatter = LastMessageFormatter()
|
private val lastMessageFormatter = LastMessageFormatter()
|
||||||
|
private val matrixUserHelper = MatrixItemHelper(client)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
handleInit()
|
handleInit()
|
||||||
|
|
@ -79,24 +78,6 @@ class RoomListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleInit() {
|
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
|
// Observe the room list and the filter
|
||||||
combine(
|
combine(
|
||||||
client.roomSummaryDataSource().roomSummaries()
|
client.roomSummaryDataSource().roomSummaries()
|
||||||
|
|
@ -136,9 +117,9 @@ class RoomListViewModel @AssistedInject constructor(
|
||||||
when (roomSummary) {
|
when (roomSummary) {
|
||||||
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
|
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
|
||||||
is RoomSummary.Filled -> {
|
is RoomSummary.Filled -> {
|
||||||
val avatarData = loadAvatarData(
|
val avatarData = matrixUserHelper.loadAvatarData(
|
||||||
roomSummary.details.name,
|
roomSummary = roomSummary,
|
||||||
roomSummary.details.avatarURLString
|
size = AvatarSize.MEDIUM
|
||||||
)
|
)
|
||||||
RoomListRoomSummary(
|
RoomListRoomSummary(
|
||||||
id = roomSummary.identifier(),
|
id = roomSummary.identifier(),
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.LogCompositions
|
||||||
import io.element.android.x.core.compose.textFieldState
|
import io.element.android.x.core.compose.textFieldState
|
||||||
import io.element.android.x.designsystem.components.avatar.Avatar
|
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
|
@Composable
|
||||||
fun RoomListTopBar(
|
fun RoomListTopBar(
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import com.airbnb.mvrx.Uninitialized
|
||||||
import io.element.android.x.matrix.core.RoomId
|
import io.element.android.x.matrix.core.RoomId
|
||||||
|
|
||||||
data class RoomListViewState(
|
data class RoomListViewState(
|
||||||
val user: Async<MatrixUser> = Uninitialized,
|
|
||||||
// Will contain the filtered rooms, using ::filter (if filter is not empty)
|
// Will contain the filtered rooms, using ::filter (if filter is not empty)
|
||||||
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
|
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
|
||||||
val filter: String = "",
|
val filter: String = "",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import io.element.android.x.designsystem.AvatarGradientEnd
|
import io.element.android.x.designsystem.AvatarGradientEnd
|
||||||
|
|
@ -42,13 +43,13 @@ fun Avatar(avatarData: AvatarData, modifier: Modifier = Modifier) {
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
if (avatarData.model == null) {
|
if (avatarData.model == null) {
|
||||||
InitialsAvatar(
|
InitialsAvatar(
|
||||||
|
avatarData = avatarData,
|
||||||
modifier = commonModifier,
|
modifier = commonModifier,
|
||||||
initials = avatarData.name.first().uppercase()
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ImageAvatar(
|
ImageAvatar(
|
||||||
|
avatarData = avatarData,
|
||||||
modifier = commonModifier,
|
modifier = commonModifier,
|
||||||
avatarData = avatarData
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +72,7 @@ private fun ImageAvatar(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun InitialsAvatar(
|
private fun InitialsAvatar(
|
||||||
initials: String,
|
avatarData: AvatarData,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val initialsGradient = Brush.linearGradient(
|
val initialsGradient = Brush.linearGradient(
|
||||||
|
|
@ -87,9 +88,15 @@ private fun InitialsAvatar(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
text = initials,
|
text = avatarData.name.first().uppercase(),
|
||||||
fontSize = 24.sp,
|
fontSize = (avatarData.size.value / 2).sp,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun InitialsAvatar() {
|
||||||
|
InitialsAvatar(AvatarData("A"))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import androidx.compose.ui.unit.dp
|
||||||
enum class AvatarSize(val value: Int) {
|
enum class AvatarSize(val value: Int) {
|
||||||
SMALL(32),
|
SMALL(32),
|
||||||
MEDIUM(40),
|
MEDIUM(40),
|
||||||
BIG(48);
|
BIG(48),
|
||||||
|
HUGE(96);
|
||||||
|
|
||||||
val dp = value.dp
|
val dp = value.dp
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,4 @@ package io.element.android.x.designsystem.components.preferences
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
internal val preferenceMinHeight = 80.dp
|
internal val preferenceMinHeight = 80.dp
|
||||||
|
internal val preferencePaddingEnd = 16.dp
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,13 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
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.padding
|
||||||
import androidx.compose.foundation.layout.statusBars
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
|
@ -47,7 +52,10 @@ fun PreferenceScreen(
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.systemBarsPadding()
|
||||||
|
.imePadding(),
|
||||||
contentWindowInsets = WindowInsets.statusBars,
|
contentWindowInsets = WindowInsets.statusBars,
|
||||||
topBar = {
|
topBar = {
|
||||||
PreferenceTopAppBar(
|
PreferenceTopAppBar(
|
||||||
|
|
@ -56,8 +64,13 @@ fun PreferenceScreen(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(it)
|
modifier = Modifier
|
||||||
|
.padding(it)
|
||||||
|
.verticalScroll(
|
||||||
|
state = scrollState,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -54,7 +55,9 @@ fun PreferenceSlide(
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
PreferenceIcon(icon = icon)
|
PreferenceIcon(icon = icon)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(end = preferencePaddingEnd),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Announcement
|
import androidx.compose.material.icons.filled.Announcement
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
|
|
@ -65,6 +66,7 @@ fun PreferenceSwitch(
|
||||||
text = title
|
text = title
|
||||||
)
|
)
|
||||||
Checkbox(
|
Checkbox(
|
||||||
|
modifier = Modifier.padding(end = preferencePaddingEnd),
|
||||||
checked = isChecked,
|
checked = isChecked,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onCheckedChange = onCheckedChange
|
onCheckedChange = onCheckedChange
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -53,7 +54,8 @@ fun PreferenceText(
|
||||||
PreferenceIcon(icon = icon)
|
PreferenceIcon(icon = icon)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f)
|
||||||
|
.padding(end = preferencePaddingEnd),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
text = title
|
text = title
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.element.android.x.sdk.matrix"
|
namespace = "io.element.android.x.matrix"
|
||||||
}
|
}
|
||||||
|
|
||||||
anvil {
|
anvil {
|
||||||
|
|
@ -33,7 +33,6 @@ dependencies {
|
||||||
implementation(project(":libraries:di"))
|
implementation(project(":libraries:di"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation("net.java.dev.jna:jna:5.12.1@aar")
|
implementation("net.java.dev.jna:jna:5.12.1@aar")
|
||||||
implementation(libs.coil.compose)
|
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
implementation(libs.serialization.json)
|
implementation(libs.serialization.json)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,10 @@
|
||||||
package io.element.android.x.matrix
|
package io.element.android.x.matrix
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import coil.ComponentRegistry
|
|
||||||
import io.element.android.x.core.coroutine.CoroutineDispatchers
|
import io.element.android.x.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.x.di.AppScope
|
import io.element.android.x.di.AppScope
|
||||||
import io.element.android.x.di.ApplicationContext
|
import io.element.android.x.di.ApplicationContext
|
||||||
import io.element.android.x.di.SingleIn
|
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.session.SessionStore
|
||||||
import io.element.android.x.matrix.util.logError
|
import io.element.android.x.matrix.util.logError
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -58,14 +55,6 @@ class Matrix @Inject constructor(
|
||||||
return sessionStore.isLoggedIn()
|
return sessionStore.isLoggedIn()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerCoilComponents(
|
|
||||||
builder: ComponentRegistry.Builder,
|
|
||||||
activeClientProvider: () -> MatrixClient?
|
|
||||||
) {
|
|
||||||
builder.add(MediaKeyer())
|
|
||||||
builder.add(MediaFetcher.Factory(activeClientProvider))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
||||||
sessionStore.getLatestSession()
|
sessionStore.getLatestSession()
|
||||||
?.let { session ->
|
?.let { session ->
|
||||||
|
|
|
||||||
|
|
@ -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.room.RustRoomSummaryDataSource
|
||||||
import io.element.android.x.matrix.session.SessionStore
|
import io.element.android.x.matrix.session.SessionStore
|
||||||
import io.element.android.x.matrix.sync.SlidingSyncObserverProxy
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.Client
|
import org.matrix.rustcomponents.sdk.Client
|
||||||
|
|
@ -38,6 +35,9 @@ import org.matrix.rustcomponents.sdk.SlidingSyncMode
|
||||||
import org.matrix.rustcomponents.sdk.SlidingSyncViewBuilder
|
import org.matrix.rustcomponents.sdk.SlidingSyncViewBuilder
|
||||||
import org.matrix.rustcomponents.sdk.StoppableSpawn
|
import org.matrix.rustcomponents.sdk.StoppableSpawn
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
class MatrixClient internal constructor(
|
class MatrixClient internal constructor(
|
||||||
private val client: Client,
|
private val client: Client,
|
||||||
|
|
@ -157,6 +157,7 @@ class MatrixClient internal constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun userId(): UserId = UserId(client.userId())
|
fun userId(): UserId = UserId(client.userId())
|
||||||
|
|
||||||
suspend fun loadUserDisplayName(): Result<String> = withContext(dispatchers.io) {
|
suspend fun loadUserDisplayName(): Result<String> = withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
client.displayName()
|
client.displayName()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.x.matrix.core
|
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
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,11 @@ class MatrixRoom(
|
||||||
return slidingSyncRoom.name()
|
return slidingSyncRoom.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val bestName: String
|
||||||
|
get() {
|
||||||
|
return name?.takeIf { it.isNotEmpty() } ?: room.id()
|
||||||
|
}
|
||||||
|
|
||||||
val displayName: String
|
val displayName: String
|
||||||
get() {
|
get() {
|
||||||
return room.displayName()
|
return room.displayName()
|
||||||
|
|
|
||||||
38
libraries/matrixui/build.gradle.kts
Normal file
38
libraries/matrixui/build.gradle.kts
Normal file
|
|
@ -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
Normal file
18
libraries/matrixui/src/main/AndroidManifest.xml
Normal file
|
|
@ -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/>
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,13 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.x.matrix.media
|
package io.element.android.x.matrix.ui.media
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.fetch.FetchResult
|
import coil.fetch.FetchResult
|
||||||
import coil.fetch.Fetcher
|
import coil.fetch.Fetcher
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
import io.element.android.x.matrix.MatrixClient
|
import io.element.android.x.matrix.MatrixClient
|
||||||
|
import io.element.android.x.matrix.media.MediaResolver
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
internal class MediaFetcher(
|
internal class MediaFetcher(
|
||||||
|
|
@ -14,10 +14,11 @@
|
||||||
* limitations under the License.
|
* 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.key.Keyer
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
|
import io.element.android.x.matrix.media.MediaResolver
|
||||||
|
|
||||||
internal class MediaKeyer : Keyer<MediaResolver.Meta> {
|
internal class MediaKeyer : Keyer<MediaResolver.Meta> {
|
||||||
override fun key(data: MediaResolver.Meta, options: Options): String? {
|
override fun key(data: MediaResolver.Meta, options: Options): String? {
|
||||||
|
|
@ -14,14 +14,20 @@
|
||||||
* limitations under the License.
|
* 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 androidx.compose.runtime.Stable
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
|
import io.element.android.x.matrix.core.UserId
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class MatrixUser(
|
data class MatrixUser(
|
||||||
|
val id: UserId,
|
||||||
val username: String? = null,
|
val username: String? = null,
|
||||||
val avatarUrl: String? = null,
|
val avatarUrl: String? = null,
|
||||||
val avatarData: AvatarData = AvatarData(),
|
val avatarData: AvatarData = AvatarData(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun MatrixUser.getBestName(): String {
|
||||||
|
return username?.takeIf { it.isNotEmpty() } ?: id.value
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -37,6 +37,7 @@ include(":app")
|
||||||
include(":libraries:core")
|
include(":libraries:core")
|
||||||
include(":libraries:rustsdk")
|
include(":libraries:rustsdk")
|
||||||
include(":libraries:matrix")
|
include(":libraries:matrix")
|
||||||
|
include(":libraries:matrixui")
|
||||||
include(":libraries:textcomposer")
|
include(":libraries:textcomposer")
|
||||||
include(":libraries:elementresources")
|
include(":libraries:elementresources")
|
||||||
include(":features:onboarding")
|
include(":features:onboarding")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue