wip, passing the image urls down to the matrix client layer

This commit is contained in:
Adam Brown 2022-06-08 20:31:07 +01:00
parent 92ad630e45
commit 6fed8a35ce
25 changed files with 316 additions and 76 deletions

View File

@ -0,0 +1,29 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -44,6 +44,7 @@ import app.dapk.st.matrix.sync.internal.room.MessageDecrypter
import app.dapk.st.messenger.MessengerActivity
import app.dapk.st.messenger.MessengerModule
import app.dapk.st.navigator.IntentFactory
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.notifications.NotificationsModule
import app.dapk.st.olm.DeviceKeyFactory
import app.dapk.st.olm.OlmPersistenceWrapper
@ -93,6 +94,11 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
override fun home(context: Context) = Intent(context, MainActivity::class.java)
override fun messenger(context: Context, roomId: RoomId) = MessengerActivity.newInstance(context, roomId)
override fun messengerShortcut(context: Context, roomId: RoomId) = MessengerActivity.newShortcutInstance(context, roomId)
override fun messengerAttachments(context: Context, roomId: RoomId, attachments: List<MessageAttachment>) = MessengerActivity.newMessageAttachment(
context,
roomId,
attachments
)
})
val featureModules = FeatureModules(
@ -236,6 +242,7 @@ internal class MatrixModules(
val result = serviceProvider.cryptoService().encrypt(
roomId = when (message) {
is MessageService.Message.TextMessage -> message.roomId
is MessageService.Message.ImageMessage -> message.roomId
},
credentials = credentialsStore.credentials()!!,
when (message) {
@ -250,6 +257,7 @@ internal class MatrixModules(
)
)
)
is MessageService.Message.ImageMessage -> TODO()
}
)

View File

@ -0,0 +1,5 @@
package app.dapk.st.core
sealed interface MimeType {
object Image: MimeType
}

View File

@ -33,11 +33,13 @@ class LocalEchoPersistence(
inMemoryEchos.value = echos.groupBy {
when (val message = it.message) {
is MessageService.Message.TextMessage -> message.roomId
is MessageService.Message.ImageMessage -> message.roomId
}
}.mapValues {
it.value.associateBy {
when (val message = it.message) {
is MessageService.Message.TextMessage -> message.localId
is MessageService.Message.ImageMessage -> message.localId
}
}
}

View File

@ -75,6 +75,7 @@ class DirectoryUseCase(
lastMessage = LastMessage(
content = when (val message = latestEcho.message) {
is MessageService.Message.TextMessage -> message.content.body
is MessageService.Message.ImageMessage -> "\uD83D\uDCF7"
},
utcTimestamp = latestEcho.timestampUtc,
author = member,

View File

@ -19,6 +19,7 @@ internal class LocalEchoMapper(private val metaMapper: MetaMapper) {
meta = metaMapper.toMeta(this)
)
}
is MessageService.Message.ImageMessage -> TODO()
}
}

View File

@ -9,11 +9,10 @@ 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.core.*
import app.dapk.st.design.components.SmallTalkTheme
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.navigator.MessageAttachment
import kotlinx.parcelize.Parcelize
class MessengerActivity : DapkActivity() {
@ -34,15 +33,22 @@ class MessengerActivity : DapkActivity() {
putExtra("shortcut_key", roomId.value)
}
}
fun newMessageAttachment(context: Context, roomId: RoomId, attachments: List<MessageAttachment>): Intent {
return Intent(context, MessengerActivity::class.java).apply {
putExtra("key", MessagerActivityPayload(roomId.value, attachments))
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val payload = readPayload<MessagerActivityPayload>()
log(AppLogTag.ERROR_NON_FATAL, payload)
setContent {
SmallTalkTheme {
Surface(Modifier.fillMaxSize()) {
MessengerScreen(RoomId(payload.roomId), viewModel, navigator)
MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
}
}
}
@ -51,7 +57,8 @@ class MessengerActivity : DapkActivity() {
@Parcelize
data class MessagerActivityPayload(
val roomId: String
val roomId: String,
val attachments: List<MessageAttachment>? = null
) : Parcelable
fun <T : Parcelable> Activity.readPayload(): T = intent.getParcelableExtra("key") ?: intent.getStringExtra("shortcut_key")!!.let {

View File

@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import androidx.core.net.toUri
import app.dapk.st.core.Lce
import app.dapk.st.core.LifecycleEffect
import app.dapk.st.core.StartObserving
@ -39,18 +40,19 @@ import app.dapk.st.matrix.sync.MessageMeta
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomEvent.Message
import app.dapk.st.matrix.sync.RoomState
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.navigator.Navigator
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import kotlinx.coroutines.launch
@Composable
internal fun MessengerScreen(roomId: RoomId, viewModel: MessengerViewModel, navigator: Navigator) {
internal fun MessengerScreen(roomId: RoomId, attachments: List<MessageAttachment>?, viewModel: MessengerViewModel, navigator: Navigator) {
val state = viewModel.state
viewModel.ObserveEvents()
LifecycleEffect(
onStart = { viewModel.post(MessengerAction.OnMessengerVisible(roomId)) },
onStart = { viewModel.post(MessengerAction.OnMessengerVisible(roomId, attachments)) },
onStop = { viewModel.post(MessengerAction.OnMessengerGone) }
)
@ -70,12 +72,19 @@ internal fun MessengerScreen(roomId: RoomId, viewModel: MessengerViewModel, navi
Room(state.roomState)
when (state.composerState) {
is ComposerState.Text -> {
Composer(
state.composerState.value,
TextComposer(
state.composerState,
onTextChange = { viewModel.post(MessengerAction.ComposerTextUpdate(it)) },
onSend = { viewModel.post(MessengerAction.ComposerSendText) },
)
}
is ComposerState.Attachments -> {
AttachmentComposer(
state.composerState,
onSend = { viewModel.post(MessengerAction.ComposerSendText) },
onCancel = { viewModel.post(MessengerAction.ComposerClear) }
)
}
}
}
}
@ -524,7 +533,7 @@ private fun RowScope.SendStatus(message: RoomEvent) {
}
@Composable
private fun Composer(message: String, onTextChange: (String) -> Unit, onSend: () -> Unit) {
private fun TextComposer(state: ComposerState.Text, onTextChange: (String) -> Unit, onSend: () -> Unit) {
Row(
Modifier
.fillMaxWidth()
@ -541,12 +550,12 @@ private fun Composer(message: String, onTextChange: (String) -> Unit, onSend: ()
contentAlignment = Alignment.TopStart,
) {
Box(Modifier.padding(14.dp)) {
if (message.isEmpty()) {
if (state.value.isEmpty()) {
Text("Message")
}
BasicTextField(
modifier = Modifier.fillMaxWidth(),
value = message,
value = state.value,
onValueChange = { onTextChange(it) },
cursorBrush = SolidColor(MaterialTheme.colors.primary),
textStyle = LocalTextStyle.current.copy(color = LocalContentColor.current.copy(LocalContentAlpha.current)),
@ -557,10 +566,10 @@ private fun Composer(message: String, onTextChange: (String) -> Unit, onSend: ()
Spacer(modifier = Modifier.width(6.dp))
var size by remember { mutableStateOf(IntSize(0, 0)) }
IconButton(
enabled = message.isNotEmpty(),
enabled = state.value.isNotEmpty(),
modifier = Modifier
.clip(CircleShape)
.background(if (message.isEmpty()) Color.DarkGray else MaterialTheme.colors.primary)
.background(if (state.value.isEmpty()) Color.DarkGray else MaterialTheme.colors.primary)
.run {
if (size.height == 0 || size.width == 0) {
this
@ -584,3 +593,65 @@ private fun Composer(message: String, onTextChange: (String) -> Unit, onSend: ()
}
}
}
@Composable
private fun AttachmentComposer(state: ComposerState.Attachments, onSend: () -> Unit, onCancel: () -> Unit) {
Row(
Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp)
.fillMaxWidth()
.height(IntrinsicSize.Min), verticalAlignment = Alignment.Bottom
) {
Box(
modifier = Modifier
.align(Alignment.Bottom)
.weight(1f)
.fillMaxHeight()
.background(MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity), RoundedCornerShape(24.dp)),
contentAlignment = Alignment.TopStart,
) {
Box(Modifier.padding(14.dp)) {
val context = LocalContext.current
Image(
modifier = Modifier.size(50.dp, 50.dp),
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.data(state.values.first().uri.value.toUri())
.build()
),
contentDescription = null,
)
}
}
Spacer(modifier = Modifier.width(6.dp))
var size by remember { mutableStateOf(IntSize(0, 0)) }
IconButton(
enabled = true,
modifier = Modifier
.clip(CircleShape)
.background(MaterialTheme.colors.primary)
.run {
if (size.height == 0 || size.width == 0) {
this
.onSizeChanged {
size = it
}
.fillMaxHeight()
} else {
with(LocalDensity.current) {
size(size.width.toDp(), size.height.toDp())
}
}
},
onClick = onSend,
) {
Icon(
imageVector = Icons.Filled.Send,
contentDescription = "",
tint = MaterialTheme.colors.onPrimary,
)
}
}
}

View File

@ -2,6 +2,7 @@ package app.dapk.st.messenger
import app.dapk.st.core.Lce
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.navigator.MessageAttachment
data class MessengerScreenState(
val roomId: RoomId?,
@ -17,4 +18,8 @@ sealed interface ComposerState {
val value: String,
) : ComposerState
data class Attachments(
val values: List<MessageAttachment>,
) : ComposerState
}

View File

@ -11,6 +11,7 @@ import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomStore
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.viewmodel.DapkViewModel
import app.dapk.st.viewmodel.MutableStateFactory
import app.dapk.st.viewmodel.defaultStateFactory
@ -46,18 +47,19 @@ internal class MessengerViewModel(
MessengerAction.OnMessengerGone -> syncJob?.cancel()
is MessengerAction.ComposerTextUpdate -> updateState { copy(composerState = ComposerState.Text(action.newValue)) }
MessengerAction.ComposerSendText -> sendMessage()
MessengerAction.ComposerClear -> updateState { copy(composerState = ComposerState.Text("")) }
}
}
private fun start(action: MessengerAction.OnMessengerVisible) {
updateState { copy(roomId = action.roomId) }
updateState { copy(roomId = action.roomId, composerState = action.attachments?.let { ComposerState.Attachments(it) } ?: composerState) }
syncJob = viewModelScope.launch {
roomStore.markRead(action.roomId)
val credentials = credentialsStore.credentials()!!
var lastKnownReadEvent: EventId? = null
observeTimeline.invoke(action.roomId, credentials.userId).distinctUntilChanged().onEach { state ->
state.lastestMessageEventFromOthers(self = credentials.userId)?.let {
state.latestMessageEventFromOthers(self = credentials.userId)?.let {
if (lastKnownReadEvent != it) {
updateRoomReadStateAsync(latestReadEvent = it, state)
lastKnownReadEvent = it
@ -98,12 +100,32 @@ internal class MessengerViewModel(
}
}
}
is ComposerState.Attachments -> {
val copy = composerState.copy()
updateState { copy(composerState = ComposerState.Text("")) }
state.roomState.takeIfContent()?.let { content ->
val roomState = content.roomState
viewModelScope.launch {
messageService.scheduleMessage(
MessageService.Message.ImageMessage(
MessageService.Message.Content.ImageContent(uri = copy.values.first().uri.value),
roomId = roomState.roomOverview.roomId,
sendEncrypted = roomState.roomOverview.isEncrypted,
localId = localIdFactory.create(),
timestampUtc = clock.millis(),
)
)
}
}
}
}
}
}
private fun MessengerState.lastestMessageEventFromOthers(self: UserId) = this.roomState.events
private fun MessengerState.latestMessageEventFromOthers(self: UserId) = this.roomState.events
.filterIsInstance<RoomEvent.Message>()
.filterNot { it.author.id == self }
.firstOrNull()
@ -112,6 +134,7 @@ private fun MessengerState.lastestMessageEventFromOthers(self: UserId) = this.ro
sealed interface MessengerAction {
data class ComposerTextUpdate(val newValue: String) : MessengerAction
object ComposerSendText : MessengerAction
data class OnMessengerVisible(val roomId: RoomId) : MessengerAction
object ComposerClear : MessengerAction
data class OnMessengerVisible(val roomId: RoomId, val attachments: List<MessageAttachment>?) : MessengerAction
object OnMessengerGone : MessengerAction
}

View File

@ -36,7 +36,7 @@ class RoomSettingsActivity : DapkActivity() {
setContent {
SmallTalkTheme {
Surface(Modifier.fillMaxSize()) {
MessengerScreen(RoomId(payload.roomId), viewModel, navigator)
// MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
}
}
}

View File

@ -1,4 +1,5 @@
applyAndroidLibraryModule(project)
apply plugin: 'kotlin-parcelize'
dependencies {
implementation project(":core")

View File

@ -3,7 +3,13 @@ package app.dapk.st.navigator
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Parcel
import android.os.Parcelable
import app.dapk.st.core.AndroidUri
import app.dapk.st.core.MimeType
import app.dapk.st.matrix.common.RoomId
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
@ -29,8 +35,9 @@ interface Navigator {
activity.navigateUpTo(intentFactory.home(activity))
}
fun toMessenger(roomId: RoomId) {
intentFactory.messenger(activity, roomId)
fun toMessenger(roomId: RoomId, attachments: List<MessageAttachment>) {
val intent = intentFactory.messengerAttachments(activity, roomId, attachments)
activity.startActivity(intent)
}
fun toFilePicker(requestCode: Int) {
@ -47,6 +54,7 @@ interface IntentFactory {
fun home(context: Context): Intent
fun messenger(context: Context, roomId: RoomId): Intent
fun messengerShortcut(context: Context, roomId: RoomId): Intent
fun messengerAttachments(context: Context, roomId: RoomId, attachments: List<MessageAttachment>): Intent
}
@ -65,3 +73,24 @@ private class DefaultNavigator(activity: Activity, intentFactory: IntentFactory)
override val navigate: Navigator.Dsl = Navigator.Dsl(activity, intentFactory)
}
@Parcelize
data class MessageAttachment(val uri: AndroidUri, val type: MimeType) : Parcelable {
private companion object : Parceler<MessageAttachment> {
override fun create(parcel: Parcel): MessageAttachment {
val uri = AndroidUri(parcel.readString()!!)
val type = when(parcel.readString()!!) {
"mimetype-image" -> MimeType.Image
else -> throw IllegalStateException()
}
return MessageAttachment(uri, type)
}
override fun MessageAttachment.write(parcel: Parcel, flags: Int) {
parcel.writeString(uri.value)
when (type) {
MimeType.Image -> parcel.writeString("mimetype-image")
}
}
}
}

View File

@ -9,4 +9,5 @@ dependencies {
implementation project(':matrix:services:message')
implementation project(":core")
implementation project(":design-library")
implementation project(":features:navigator")
}

View File

@ -0,0 +1,22 @@
package app.dapk.st.share
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.SyncService
import kotlinx.coroutines.flow.first
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 }
)
}
}
}

View File

@ -19,11 +19,11 @@ class ShareEntryActivity : DapkActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val urisToShare = intent.readSendUrisOrNull() ?: throw IllegalArgumentException("")
val urisToShare = intent.readSendUrisOrNull() ?: throw IllegalArgumentException("Expected deeplink uris but they were missing")
setContent {
SmallTalkTheme {
Surface(Modifier.fillMaxSize()) {
ShareEntryScreen(viewModel)
ShareEntryScreen(navigator, viewModel)
}
}
}

View File

@ -11,37 +11,36 @@ 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.MimeType
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.navigator.MessageAttachment
import app.dapk.st.navigator.Navigator
import app.dapk.st.share.DirectoryScreenState.*
@Composable
fun ShareEntryScreen(viewModel: ShareEntryViewModel) {
fun ShareEntryScreen(navigator: Navigator, viewModel: ShareEntryViewModel) {
val state = viewModel.state
val listState: LazyListState = rememberLazyListState(
initialFirstVisibleItemIndex = 0,
)
viewModel.ObserveEvents(listState)
viewModel.ObserveEvents(navigator)
LifecycleEffect(
onStart = { viewModel.start() },
onStop = { viewModel.stop() }
)
val listState: LazyListState = rememberLazyListState(
initialFirstVisibleItemIndex = 0,
)
Box(modifier = Modifier.fillMaxSize()) {
Toolbar(title = "Send to...")
when (state) {
@ -50,18 +49,21 @@ fun ShareEntryScreen(viewModel: ShareEntryViewModel) {
is Error -> GenericError {
// TODO
}
is Content -> Content(listState, state)
is Content -> Content(listState, state) {
viewModel.onRoomSelected(it)
}
}
}
}
@Composable
private fun ShareEntryViewModel.ObserveEvents(listState: LazyListState) {
val context = LocalContext.current
private fun ShareEntryViewModel.ObserveEvents(navigator: Navigator) {
StartObserving {
this@ObserveEvents.events.launch {
when (it) {
is DirectoryEvent.SelectRoom -> TODO()
is DirectoryEvent.SelectRoom -> {
navigator.navigate.toMessenger(it.item.id, it.uris.map { MessageAttachment(it, MimeType.Image) })
}
}
}
}
@ -69,33 +71,27 @@ private fun ShareEntryViewModel.ObserveEvents(listState: LazyListState) {
@Composable
private fun Content(listState: LazyListState, state: Content) {
val context = LocalContext.current
val navigateToRoom = { roomId: RoomId ->
// todo
// context.startActivity(MessengerActivity.newInstance(context, roomId))
}
private fun Content(listState: LazyListState, state: Content, onClick: (Item) -> Unit) {
LazyColumn(Modifier.fillMaxSize(), state = listState, contentPadding = PaddingValues(top = 72.dp)) {
items(
items = state.items,
key = { it.id.value },
) {
DirectoryItem(it, onClick = navigateToRoom)
DirectoryItem(it, onClick = onClick)
}
}
}
@Composable
private fun DirectoryItem(item: Item, onClick: (RoomId) -> Unit) {
private fun DirectoryItem(item: Item, onClick: (Item) -> Unit) {
val roomName = item.roomName
Box(
Modifier
.height(IntrinsicSize.Min)
.fillMaxWidth()
.clickable {
onClick(item.id)
}) {
.clickable { onClick(item) }
) {
Row(Modifier.padding(20.dp)) {
val secondaryText = MaterialTheme.colors.onBackground.copy(alpha = 0.5f)

View File

@ -1,5 +1,6 @@
package app.dapk.st.share
import app.dapk.st.core.AndroidUri
import app.dapk.st.matrix.common.AvatarUrl
import app.dapk.st.matrix.common.RoomId
@ -13,7 +14,7 @@ sealed interface DirectoryScreenState {
}
sealed interface DirectoryEvent {
data class SelectRoom(val item: Item) : DirectoryEvent
data class SelectRoom(val item: Item, val uris: List<AndroidUri>) : DirectoryEvent
}
data class Item(val id: RoomId, val roomAvatarUrl: AvatarUrl?, val roomName: String, val members: List<String>)

View File

@ -3,15 +3,10 @@ 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(
@ -22,6 +17,7 @@ class ShareEntryViewModel(
factory,
) {
private var urisToShare: List<AndroidUri>? = null
private var syncJob: Job? = null
fun start() {
@ -34,31 +30,14 @@ class ShareEntryViewModel(
syncJob?.cancel()
}
fun sendAttachment() {
}
fun withUris(urisToShare: List<Uri>) {
// TODO("Not yet implemented")
this.urisToShare = urisToShare.map { AndroidUri(it.toString()) }
}
fun onRoomSelected(item: Item) {
viewModelScope.launch {
_events.emit(DirectoryEvent.SelectRoom(item, uris = urisToShare ?: throw IllegalArgumentException("Not uris set")))
}
}
}
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 }
)
}
}
}

View File

@ -14,5 +14,6 @@ enum class EventType(val value: String) {
}
enum class MessageType(val value: String) {
TEXT("m.text")
TEXT("m.text"),
IMAGE("m.image"),
}

View File

@ -46,6 +46,16 @@ interface MessageService : MatrixService {
@SerialName("timestamp") val timestampUtc: Long,
) : Message()
@Serializable
@SerialName("image_message")
data class ImageMessage(
@SerialName("content") val content: Content.ImageContent,
@SerialName("send_encrypted") val sendEncrypted: Boolean,
@SerialName("room_id") val roomId: RoomId,
@SerialName("local_id") val localId: String,
@SerialName("timestamp") val timestampUtc: Long,
) : Message()
@Serializable
sealed class Content {
@Serializable
@ -53,6 +63,12 @@ interface MessageService : MatrixService {
@SerialName("body") val body: String,
@SerialName("msgtype") val type: String = MessageType.TEXT.value,
) : Content()
@Serializable
data class ImageContent(
@SerialName("uri") val uri: String,
@SerialName("msgtype") val type: String = MessageType.IMAGE.value,
) : Content()
}
}
@ -66,16 +82,19 @@ interface MessageService : MatrixService {
@Transient
val timestampUtc = when (message) {
is Message.TextMessage -> message.timestampUtc
is Message.ImageMessage -> message.timestampUtc
}
@Transient
val roomId = when (message) {
is Message.TextMessage -> message.roomId
is Message.ImageMessage -> message.roomId
}
@Transient
val localId = when (message) {
is Message.TextMessage -> message.localId
is Message.ImageMessage -> message.localId
}
@Serializable

View File

@ -13,6 +13,7 @@ import java.net.SocketException
import java.net.UnknownHostException
private const val MATRIX_MESSAGE_TASK_TYPE = "matrix-text-message"
private const val MATRIX_IMAGE_MESSAGE_TASK_TYPE = "matrix-image-message"
internal class DefaultMessageService(
httpClient: MatrixHttpClient,
@ -50,6 +51,7 @@ internal class DefaultMessageService(
localEchoStore.markSending(message)
val localId = when (message) {
is MessageService.Message.TextMessage -> message.localId
is MessageService.Message.ImageMessage -> message.localId
}
backgroundScheduler.schedule(key = localId, message.toTask())
}
@ -68,6 +70,10 @@ internal class DefaultMessageService(
Json.encodeToString(MessageService.Message.TextMessage.serializer(), this)
)
}
is MessageService.Message.ImageMessage -> BackgroundScheduler.Task(
type = MATRIX_IMAGE_MESSAGE_TASK_TYPE,
Json.encodeToString(MessageService.Message.ImageMessage.serializer(), this)
)
}
}

View File

@ -34,6 +34,36 @@ internal class SendMessageUseCase(
}
httpClient.execute(request).eventId
}
is MessageService.Message.ImageMessage -> {
// upload image, then send message
// POST /_matrix/media/v3/upload
// message.content.uri
/**
* {
"content": {
"body": "filename.jpg",
"info": {
"h": 398,
"mimetype": "image/jpeg",
"size": 31037,
"w": 394
},
"msgtype": "m.image",
"url": "mxc://example.org/JWEIFJgwEIhweiWJE"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}
*/
TODO()
}
}
}

View File

@ -16,6 +16,7 @@ internal fun sendRequest(roomId: RoomId, eventType: EventType, txId: String, con
method = MatrixHttpClient.Method.PUT,
body = when (content) {
is Message.Content.TextContent -> jsonBody(Message.Content.TextContent.serializer(), content, MatrixHttpClient.jsonWithDefaults)
is Message.Content.ImageContent -> jsonBody(Message.Content.ImageContent.serializer(), content, MatrixHttpClient.jsonWithDefaults)
}
)

View File

@ -115,6 +115,7 @@ class TestMatrix(
val result = serviceProvider.cryptoService().encrypt(
roomId = when (message) {
is MessageService.Message.TextMessage -> message.roomId
is MessageService.Message.ImageMessage -> message.roomId
},
credentials = storeModule.credentialsStore().credentials()!!,
when (message) {
@ -128,6 +129,7 @@ class TestMatrix(
)
)
)
is MessageService.Message.ImageMessage -> TODO()
}
)