First mapping of room. Still very dirty
This commit is contained in:
parent
d588dcb246
commit
501dd176e5
11 changed files with 165 additions and 30 deletions
|
|
@ -0,0 +1,14 @@
|
|||
package io.element.android.x.core.data
|
||||
|
||||
import android.util.Log
|
||||
|
||||
inline fun <A> tryOrNull(message: String? = null, operation: () -> A): A? {
|
||||
return try {
|
||||
operation()
|
||||
} catch (any: Throwable) {
|
||||
if (message != null) {
|
||||
Log.e("TAG", message, any)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(name: 'matrix-rust-sdk', ext: 'aar')
|
||||
api(name: 'matrix-rust-sdk', ext: 'aar')
|
||||
implementation "net.java.dev.jna:jna:5.10.0@aar"
|
||||
implementation 'androidx.datastore:datastore-core:1.0.0'
|
||||
implementation 'androidx.datastore:datastore-preferences:1.0.0'
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ class MatrixClient internal constructor(
|
|||
private val client: Client,
|
||||
private val sessionStore: SessionStore,
|
||||
) {
|
||||
private val roomWrapper = RoomWrapper(client)
|
||||
|
||||
fun startSync() {
|
||||
val clientDelegate = object : ClientDelegate {
|
||||
override fun didReceiveAuthError(isSoftLogout: Boolean) {
|
||||
|
|
@ -32,7 +34,7 @@ class MatrixClient internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun slidingSync(onSyncUpdate: (UpdateSummary) -> Unit): StoppableSpawn {
|
||||
fun slidingSync(listener: SlidingSyncListener): StoppableSpawn {
|
||||
val slidingSyncView = SlidingSyncViewBuilder()
|
||||
.timelineLimit(limit = 10u)
|
||||
.requiredState(requiredState = listOf(RequiredState(key = "m.room.avatar", value = "")))
|
||||
|
|
@ -49,7 +51,10 @@ class MatrixClient internal constructor(
|
|||
slidingSync.setObserver(object : SlidingSyncObserver {
|
||||
override fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||
Log.v(LOG_TAG, "didReceiveSyncUpdate=$summary")
|
||||
onSyncUpdate.invoke(summary)
|
||||
val rooms = summary.rooms.mapNotNull {
|
||||
roomWrapper.getRoom(it)
|
||||
}
|
||||
listener.onSyncUpdate(summary, rooms)
|
||||
}
|
||||
})
|
||||
return slidingSync.sync()
|
||||
|
|
@ -59,4 +64,11 @@ class MatrixClient internal constructor(
|
|||
client.logout()
|
||||
sessionStore.reset()
|
||||
}
|
||||
|
||||
fun username(): String = client.displayName()
|
||||
fun avatarUrl(): String = client.avatarUrl()
|
||||
|
||||
interface SlidingSyncListener {
|
||||
fun onSyncUpdate(summary: UpdateSummary, rooms: List<Room>)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package io.element.android.x.sdk.matrix
|
||||
|
||||
import android.util.Log
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
|
||||
class RoomWrapper(
|
||||
private val client: Client
|
||||
) {
|
||||
fun getRoom(roomId: String): Room? {
|
||||
val rooms = client.rooms()
|
||||
Log.d(LOG_TAG, "We have ${rooms.size} rooms")
|
||||
return rooms.firstOrNull { it.id() == roomId }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,13 @@ import androidx.activity.compose.setContent
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
|
|
@ -22,10 +22,10 @@ import com.airbnb.mvrx.compose.collectAsState
|
|||
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||
import io.element.android.x.ui.theme.ElementXTheme
|
||||
import io.element.android.x.ui.theme.components.VectorButton
|
||||
import io.element.android.x.ui.theme.components.VectorTextField
|
||||
|
||||
class LoginActivity : ComponentActivity() {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
|
@ -44,21 +44,34 @@ class LoginActivity : ComponentActivity() {
|
|||
val viewModel: LoginViewModel = mavericksViewModel()
|
||||
val state by viewModel.collectAsState()
|
||||
val isError = state.isLoggedIn is Fail
|
||||
VectorTextField(value = state.homeserver,
|
||||
OutlinedTextField(
|
||||
value = state.homeserver,
|
||||
onValueChange = {
|
||||
viewModel.handle(LoginActions.SetHomeserver(it))
|
||||
})
|
||||
VectorTextField(
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
),
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = state.login,
|
||||
onValueChange = {
|
||||
viewModel.handle(LoginActions.SetLogin(it))
|
||||
})
|
||||
VectorTextField(
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
),
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = state.password,
|
||||
onValueChange = {
|
||||
viewModel.handle(LoginActions.SetPassword(it))
|
||||
},
|
||||
isError = isError
|
||||
isError = isError,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Send,
|
||||
),
|
||||
)
|
||||
if (isError) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -58,4 +58,5 @@ dependencies {
|
|||
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
||||
|
||||
implementation 'com.airbnb.android:mavericks-compose:2.7.0'
|
||||
implementation 'io.coil-kt:coil-compose:2.2.1'
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package io.element.android.x.ui.screen.roomlist
|
||||
|
||||
data class MatrixUser(
|
||||
val username: String? = null,
|
||||
val avatarUrl: String? = null,
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package io.element.android.x.ui.screen.roomlist
|
||||
|
||||
sealed interface RoomListActions {
|
||||
object Logout : RoomListActions
|
||||
object Init : RoomListActions
|
||||
object LoadMore : RoomListActions
|
||||
object Logout : RoomListActions
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@ package io.element.android.x.ui.screen.roomlist
|
|||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExitToApp
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.compose.collectAsState
|
||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||
|
|
@ -17,12 +19,18 @@ import io.element.android.x.ui.theme.ElementXTheme
|
|||
|
||||
class RoomListActivity : ComponentActivity() {
|
||||
|
||||
private var initDone = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
ElementXTheme {
|
||||
val viewModel: RoomListViewModel = mavericksViewModel()
|
||||
if (!initDone) {
|
||||
initDone = true
|
||||
viewModel.handle(RoomListActions.Init)
|
||||
}
|
||||
val state = viewModel.collectAsState()
|
||||
// A surface container using the 'background' color from the theme
|
||||
Surface(
|
||||
|
|
@ -34,12 +42,13 @@ class RoomListActivity : ComponentActivity() {
|
|||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
OptionMenu(viewModel)
|
||||
/* TODO
|
||||
val state = viewModel.state.collectAsState().value
|
||||
RoomListHeader()
|
||||
RoomList()
|
||||
*/
|
||||
OptionMenu(state.value.user, viewModel)
|
||||
val rooms = state.value.rooms
|
||||
if (rooms is Success) {
|
||||
rooms().forEach {
|
||||
Text(text = "Room: ${it.name() ?: it.id()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.value.logoutAction is Success) {
|
||||
|
|
@ -51,14 +60,25 @@ class RoomListActivity : ComponentActivity() {
|
|||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun OptionMenu(viewModel: RoomListViewModel) {
|
||||
private fun OptionMenu(matrixUser: MatrixUser, viewModel: RoomListViewModel) {
|
||||
TopAppBar(
|
||||
title = { Text("Room List") },
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(matrixUser.avatarUrl),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
Text("${matrixUser.username}")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
Button(
|
||||
IconButton(
|
||||
onClick = { viewModel.handle(RoomListActions.Logout) }
|
||||
) {
|
||||
Text(text = "logout")
|
||||
Icon(Icons.Default.ExitToApp, contentDescription = "logout")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,22 +4,43 @@ import com.airbnb.mvrx.Fail
|
|||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MavericksViewModel
|
||||
import com.airbnb.mvrx.Success
|
||||
import io.element.android.x.core.data.tryOrNull
|
||||
import io.element.android.x.sdk.matrix.MatrixClient
|
||||
import io.element.android.x.sdk.matrix.MatrixInstance
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.StoppableSpawn
|
||||
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||
|
||||
class RoomListViewModel(initialState: RoomListViewState) :
|
||||
MavericksViewModel<RoomListViewState>(initialState) {
|
||||
MavericksViewModel<RoomListViewState>(initialState), MatrixClient.SlidingSyncListener {
|
||||
|
||||
private var sync: StoppableSpawn? = null
|
||||
private val matrix = MatrixInstance.getInstance()
|
||||
|
||||
fun handle(action: RoomListActions) {
|
||||
when (action) {
|
||||
RoomListActions.Init -> handleInit()
|
||||
RoomListActions.LoadMore -> TODO()
|
||||
RoomListActions.Logout -> handleLogout()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInit() {
|
||||
viewModelScope.launch {
|
||||
val client = getClient()
|
||||
setState {
|
||||
copy(
|
||||
user = MatrixUser(
|
||||
tryOrNull { client.username() } ?: "Room list",
|
||||
tryOrNull { client.avatarUrl() } ?: "https://previews.123rf.com/images/lkeskinen/lkeskinen1802/lkeskinen180208322/95731150-exemple-de-tampon-%C3%A9tiquette-typographique-timbre-ou-ic%C3%B4ne.jpg",
|
||||
)
|
||||
)
|
||||
}
|
||||
sync = client.slidingSync(listener = this@RoomListViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLogout() {
|
||||
viewModelScope.launch {
|
||||
setState { copy(logoutAction = Loading()) }
|
||||
|
|
@ -35,4 +56,32 @@ class RoomListViewModel(initialState: RoomListViewState) :
|
|||
private suspend fun getClient(): MatrixClient {
|
||||
return matrix.restoreSession()!!
|
||||
}
|
||||
|
||||
override fun onSyncUpdate(
|
||||
summary: UpdateSummary,
|
||||
rooms: List<Room>
|
||||
) = withState { state ->
|
||||
val list = state.rooms().orEmpty().toMutableList()
|
||||
rooms.forEach { room ->
|
||||
// Either replace or add the room
|
||||
val idx = list.indexOfFirst { it.id() == room.id() }
|
||||
if (idx == -1) {
|
||||
list.add(room)
|
||||
} else {
|
||||
list[idx] = room
|
||||
}
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
rooms = Success(list),
|
||||
summary = Success(summary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
sync?.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,13 @@ package io.element.android.x.ui.screen.roomlist
|
|||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||
|
||||
data class RoomListViewState(
|
||||
val list: List<String> = emptyList(),
|
||||
val user: MatrixUser = MatrixUser(),
|
||||
val rooms: Async<List<Room>> = Uninitialized,
|
||||
val summary: Async<UpdateSummary> = Uninitialized,
|
||||
val canLoadMore: Boolean = false,
|
||||
val logoutAction: Async<Unit> = Uninitialized,
|
||||
) : MavericksState
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue