mirror of
https://github.com/ouchadam/small-talk.git
synced 2025-03-27 01:10:16 +01:00
fetching rooms and displaying when starting a share flow
This commit is contained in:
parent
2db9ff3baf
commit
e6bd0f80dc
@ -19,6 +19,7 @@ import app.dapk.st.notifications.NotificationsModule
|
||||
import app.dapk.st.notifications.PushAndroidService
|
||||
import app.dapk.st.profile.ProfileModule
|
||||
import app.dapk.st.settings.SettingsModule
|
||||
import app.dapk.st.share.ShareEntryModule
|
||||
import app.dapk.st.work.TaskRunnerModule
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
@ -75,6 +76,7 @@ class SmallTalkApplication : Application(), ModuleProvider {
|
||||
MessengerModule::class -> featureModules.messengerModule
|
||||
TaskRunnerModule::class -> appModule.domainModules.taskRunnerModule
|
||||
CoreAndroidModule::class -> appModule.coreAndroidModule
|
||||
ShareEntryModule::class -> featureModules.shareEntryModule
|
||||
else -> throw IllegalArgumentException("Unknown: $klass")
|
||||
} as T
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import app.dapk.st.olm.OlmWrapper
|
||||
import app.dapk.st.profile.ProfileModule
|
||||
import app.dapk.st.push.PushModule
|
||||
import app.dapk.st.settings.SettingsModule
|
||||
import app.dapk.st.share.ShareEntryModule
|
||||
import app.dapk.st.tracking.TrackingModule
|
||||
import app.dapk.st.work.TaskRunnerModule
|
||||
import app.dapk.st.work.WorkModule
|
||||
@ -178,6 +179,10 @@ internal class FeatureModules internal constructor(
|
||||
)
|
||||
}
|
||||
|
||||
val shareEntryModule by unsafeLazy {
|
||||
ShareEntryModule(matrixModules.sync, matrixModules.room)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class MatrixModules(
|
||||
@ -358,6 +363,7 @@ internal class MatrixModules(
|
||||
val roomService = services.roomService()
|
||||
object : RoomMembersService {
|
||||
override suspend fun find(roomId: RoomId, userIds: List<UserId>) = roomService.findMembers(roomId, userIds)
|
||||
override suspend fun findSummary(roomId: RoomId) = roomService.findMembersSummary(roomId)
|
||||
override suspend fun insert(roomId: RoomId, members: List<RoomMember>) = roomService.insertMembers(roomId, members)
|
||||
}
|
||||
},
|
||||
|
4
core/src/main/kotlin/app/dapk/st/core/AndroidUri.kt
Normal file
4
core/src/main/kotlin/app/dapk/st/core/AndroidUri.kt
Normal file
@ -0,0 +1,4 @@
|
||||
package app.dapk.st.core
|
||||
|
||||
@JvmInline
|
||||
value class AndroidUri(val value: String)
|
@ -35,4 +35,12 @@ class MemberPersistence(
|
||||
.map { Json.decodeFromString(RoomMember.serializer(), it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun query(roomId: RoomId, limit: Int): List<RoomMember> {
|
||||
return coroutineDispatchers.withIoContext {
|
||||
database.roomMemberQueries.selectMembersByRoom(roomId.value, limit.toLong())
|
||||
.executeAsList()
|
||||
.map { Json.decodeFromString(RoomMember.serializer(), it) }
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,13 @@ SELECT blob
|
||||
FROM dbRoomMember
|
||||
WHERE room_id = ? AND user_id IN ?;
|
||||
|
||||
selectMembersByRoom:
|
||||
SELECT blob
|
||||
FROM dbRoomMember
|
||||
WHERE room_id = ?
|
||||
LIMIT ?;
|
||||
|
||||
|
||||
insert:
|
||||
INSERT OR REPLACE INTO dbRoomMember(user_id, room_id, blob)
|
||||
VALUES (?, ?, ?);
|
@ -29,6 +29,10 @@ interface Navigator {
|
||||
activity.navigateUpTo(intentFactory.home(activity))
|
||||
}
|
||||
|
||||
fun toMessenger(roomId: RoomId) {
|
||||
intentFactory.messenger(activity, roomId)
|
||||
}
|
||||
|
||||
fun toFilePicker(requestCode: Int) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
@ -4,6 +4,9 @@ dependencies {
|
||||
implementation project(":domains:android:core")
|
||||
implementation project(":domains:android:viewmodel")
|
||||
implementation project(':domains:store')
|
||||
implementation project(':matrix:services:sync')
|
||||
implementation project(':matrix:services:room')
|
||||
implementation project(':matrix:services:message')
|
||||
implementation project(":core")
|
||||
implementation project(":design-library")
|
||||
}
|
@ -4,14 +4,30 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.dapk.st.core.DapkActivity
|
||||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.viewModel
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
|
||||
class ShareEntryActivity : DapkActivity() {
|
||||
|
||||
private val viewModel by viewModel { module<ShareEntryModule>().shareEntryViewModel() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val urisToShare = intent.readSendUrisOrNull() ?: throw IllegalArgumentException("")
|
||||
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
ShareEntryScreen(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewModel.withUris(urisToShare)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
package app.dapk.st.share
|
||||
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
import app.dapk.st.matrix.room.RoomService
|
||||
import app.dapk.st.matrix.sync.SyncService
|
||||
|
||||
class ShareEntryModule(
|
||||
private val syncService: SyncService,
|
||||
private val roomService: RoomService,
|
||||
) : ProvidableModule {
|
||||
|
||||
fun shareEntryViewModel(): ShareEntryViewModel {
|
||||
return ShareEntryViewModel(FetchRoomsUseCase(syncService, roomService))
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package app.dapk.st.share
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import app.dapk.st.core.LifecycleEffect
|
||||
import app.dapk.st.core.StartObserving
|
||||
import app.dapk.st.core.components.CenteredLoading
|
||||
import app.dapk.st.design.components.CircleishAvatar
|
||||
import app.dapk.st.design.components.GenericEmpty
|
||||
import app.dapk.st.design.components.GenericError
|
||||
import app.dapk.st.design.components.Toolbar
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.share.DirectoryScreenState.*
|
||||
|
||||
@Composable
|
||||
fun ShareEntryScreen(viewModel: ShareEntryViewModel) {
|
||||
val state = viewModel.state
|
||||
|
||||
val listState: LazyListState = rememberLazyListState(
|
||||
initialFirstVisibleItemIndex = 0,
|
||||
)
|
||||
|
||||
viewModel.ObserveEvents(listState)
|
||||
|
||||
LifecycleEffect(
|
||||
onStart = { viewModel.start() },
|
||||
onStop = { viewModel.stop() }
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Toolbar(title = "Send to...")
|
||||
when (state) {
|
||||
EmptyLoading -> CenteredLoading()
|
||||
Empty -> GenericEmpty()
|
||||
is Error -> GenericError {
|
||||
// TODO
|
||||
}
|
||||
is Content -> Content(listState, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShareEntryViewModel.ObserveEvents(listState: LazyListState) {
|
||||
val context = LocalContext.current
|
||||
StartObserving {
|
||||
this@ObserveEvents.events.launch {
|
||||
when (it) {
|
||||
is DirectoryEvent.SelectRoom -> TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun Content(listState: LazyListState, state: Content) {
|
||||
val context = LocalContext.current
|
||||
val navigateToRoom = { roomId: RoomId ->
|
||||
// todo
|
||||
// context.startActivity(MessengerActivity.newInstance(context, roomId))
|
||||
}
|
||||
LazyColumn(Modifier.fillMaxSize(), state = listState, contentPadding = PaddingValues(top = 72.dp)) {
|
||||
items(
|
||||
items = state.items,
|
||||
key = { it.id.value },
|
||||
) {
|
||||
DirectoryItem(it, onClick = navigateToRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DirectoryItem(item: Item, onClick: (RoomId) -> Unit) {
|
||||
val roomName = item.roomName
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.height(IntrinsicSize.Min)
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
onClick(item.id)
|
||||
}) {
|
||||
Row(Modifier.padding(20.dp)) {
|
||||
val secondaryText = MaterialTheme.colors.onBackground.copy(alpha = 0.5f)
|
||||
|
||||
Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
|
||||
CircleishAvatar(item.roomAvatarUrl?.value, roomName, size = 50.dp)
|
||||
}
|
||||
Spacer(Modifier.width(20.dp))
|
||||
Column {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 1,
|
||||
fontSize = 18.sp,
|
||||
text = roomName,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
fontFamily = FontFamily.SansSerif,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
}
|
||||
Text(text = item.members.joinToString(), color = secondaryText, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package app.dapk.st.share
|
||||
|
||||
import app.dapk.st.matrix.common.AvatarUrl
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
|
||||
sealed interface DirectoryScreenState {
|
||||
|
||||
object EmptyLoading : DirectoryScreenState
|
||||
object Empty : DirectoryScreenState
|
||||
data class Content(
|
||||
val items: List<Item>,
|
||||
) : DirectoryScreenState
|
||||
}
|
||||
|
||||
sealed interface DirectoryEvent {
|
||||
data class SelectRoom(val item: Item) : DirectoryEvent
|
||||
}
|
||||
|
||||
data class Item(val id: RoomId, val roomAvatarUrl: AvatarUrl?, val roomName: String, val members: List<String>)
|
@ -0,0 +1,64 @@
|
||||
package app.dapk.st.share
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.dapk.st.core.AndroidUri
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.matrix.message.MessageService
|
||||
import app.dapk.st.matrix.room.RoomService
|
||||
import app.dapk.st.matrix.sync.SyncService
|
||||
import app.dapk.st.viewmodel.DapkViewModel
|
||||
import app.dapk.st.viewmodel.MutableStateFactory
|
||||
import app.dapk.st.viewmodel.defaultStateFactory
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShareEntryViewModel(
|
||||
private val fetchRoomsUseCase: FetchRoomsUseCase,
|
||||
factory: MutableStateFactory<DirectoryScreenState> = defaultStateFactory(),
|
||||
) : DapkViewModel<DirectoryScreenState, DirectoryEvent>(
|
||||
initialState = DirectoryScreenState.EmptyLoading,
|
||||
factory,
|
||||
) {
|
||||
|
||||
private var syncJob: Job? = null
|
||||
|
||||
fun start() {
|
||||
syncJob = viewModelScope.launch {
|
||||
state = DirectoryScreenState.Content(fetchRoomsUseCase.bar())
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
syncJob?.cancel()
|
||||
}
|
||||
|
||||
|
||||
fun sendAttachment() {
|
||||
|
||||
}
|
||||
|
||||
fun withUris(urisToShare: List<Uri>) {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FetchRoomsUseCase(
|
||||
private val syncSyncService: SyncService,
|
||||
private val roomService: RoomService,
|
||||
) {
|
||||
|
||||
suspend fun bar(): List<Item> {
|
||||
return syncSyncService.overview().first().map {
|
||||
Item(
|
||||
it.roomId,
|
||||
it.roomAvatarUrl,
|
||||
it.roomName ?: "",
|
||||
roomService.findMembersSummary(it.roomId).map { it.displayName ?: it.id.value }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ interface RoomService : MatrixService {
|
||||
|
||||
suspend fun findMember(roomId: RoomId, userId: UserId): RoomMember?
|
||||
suspend fun findMembers(roomId: RoomId, userIds: List<UserId>): List<RoomMember>
|
||||
suspend fun findMembersSummary(roomId: RoomId): List<RoomMember>
|
||||
suspend fun insertMembers(roomId: RoomId, members: List<RoomMember>)
|
||||
|
||||
suspend fun createDm(userId: UserId, encrypted: Boolean): RoomId
|
||||
@ -50,6 +51,7 @@ fun MatrixServiceProvider.roomService(): RoomService = this.getService(key = SER
|
||||
interface MemberStore {
|
||||
suspend fun insert(roomId: RoomId, members: List<RoomMember>)
|
||||
suspend fun query(roomId: RoomId, userIds: List<UserId>): List<RoomMember>
|
||||
suspend fun query(roomId: RoomId, limit: Int): List<RoomMember>
|
||||
}
|
||||
|
||||
interface RoomMessenger {
|
||||
|
@ -39,6 +39,10 @@ class DefaultRoomService(
|
||||
return roomMembers.findMembers(roomId, userIds)
|
||||
}
|
||||
|
||||
override suspend fun findMembersSummary(roomId: RoomId): List<RoomMember> {
|
||||
return roomMembers.findMembersSummary(roomId)
|
||||
}
|
||||
|
||||
override suspend fun insertMembers(roomId: RoomId, members: List<RoomMember>) {
|
||||
roomMembers.insert(roomId, members)
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ class RoomMembers(private val memberStore: MemberStore, private val membersCache
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findMembersSummary(roomId: RoomId) = memberStore.query(roomId, limit = 8)
|
||||
|
||||
suspend fun insert(roomId: RoomId, members: List<RoomMember>) {
|
||||
membersCache.insert(roomId, members)
|
||||
memberStore.insert(roomId, members)
|
||||
|
@ -128,6 +128,7 @@ internal object NoOpKeySharer : KeySharer {
|
||||
|
||||
interface RoomMembersService {
|
||||
suspend fun find(roomId: RoomId, userIds: List<UserId>): List<RoomMember>
|
||||
suspend fun findSummary(roomId: RoomId): List<RoomMember>
|
||||
suspend fun insert(roomId: RoomId, members: List<RoomMember>)
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,7 @@ class TestMatrix(
|
||||
val roomService = services.roomService()
|
||||
object : RoomMembersService {
|
||||
override suspend fun find(roomId: RoomId, userIds: List<UserId>) = roomService.findMembers(roomId, userIds)
|
||||
override suspend fun findSummary(roomId: RoomId) = roomService.findMembersSummary(roomId)
|
||||
override suspend fun insert(roomId: RoomId, members: List<RoomMember>) = roomService.insertMembers(roomId, members)
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user