diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..bd2a505
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt b/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt
index f5ea076..2fc928e 100644
--- a/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt
+++ b/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt
@@ -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) = 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()
}
)
diff --git a/core/src/main/kotlin/app/dapk/st/core/MimeType.kt b/core/src/main/kotlin/app/dapk/st/core/MimeType.kt
new file mode 100644
index 0000000..4d24bf8
--- /dev/null
+++ b/core/src/main/kotlin/app/dapk/st/core/MimeType.kt
@@ -0,0 +1,5 @@
+package app.dapk.st.core
+
+sealed interface MimeType {
+ object Image: MimeType
+}
\ No newline at end of file
diff --git a/domains/store/src/main/kotlin/app/dapk/st/domain/localecho/LocalEchoPersistence.kt b/domains/store/src/main/kotlin/app/dapk/st/domain/localecho/LocalEchoPersistence.kt
index bb28e31..f560c1c 100644
--- a/domains/store/src/main/kotlin/app/dapk/st/domain/localecho/LocalEchoPersistence.kt
+++ b/domains/store/src/main/kotlin/app/dapk/st/domain/localecho/LocalEchoPersistence.kt
@@ -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
}
}
}
diff --git a/features/directory/src/main/kotlin/app/dapk/st/directory/DirectoryUseCase.kt b/features/directory/src/main/kotlin/app/dapk/st/directory/DirectoryUseCase.kt
index 7e327fc..0be9307 100644
--- a/features/directory/src/main/kotlin/app/dapk/st/directory/DirectoryUseCase.kt
+++ b/features/directory/src/main/kotlin/app/dapk/st/directory/DirectoryUseCase.kt
@@ -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,
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/LocalEchoMapper.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/LocalEchoMapper.kt
index 6f445ef..478a654 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/LocalEchoMapper.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/LocalEchoMapper.kt
@@ -19,6 +19,7 @@ internal class LocalEchoMapper(private val metaMapper: MetaMapper) {
meta = metaMapper.toMeta(this)
)
}
+ is MessageService.Message.ImageMessage -> TODO()
}
}
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerActivity.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerActivity.kt
index 712842f..6f707f9 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerActivity.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerActivity.kt
@@ -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): 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()
+ 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? = null
) : Parcelable
fun Activity.readPayload(): T = intent.getParcelableExtra("key") ?: intent.getStringExtra("shortcut_key")!!.let {
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt
index f0f2a9a..d465ea8 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt
@@ -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?, 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,
+ )
+ }
+ }
+}
+
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerState.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerState.kt
index 80e1aa1..17bcd11 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerState.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerState.kt
@@ -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,
+ ) : ComposerState
+
}
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerViewModel.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerViewModel.kt
index 2a4621b..47e7676 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerViewModel.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerViewModel.kt
@@ -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()
.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?) : MessengerAction
object OnMessengerGone : MessengerAction
}
\ No newline at end of file
diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/roomsettings/RoomSettingsActivity.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/roomsettings/RoomSettingsActivity.kt
index 79f8fe9..fe564b8 100644
--- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/roomsettings/RoomSettingsActivity.kt
+++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/roomsettings/RoomSettingsActivity.kt
@@ -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)
}
}
}
diff --git a/features/navigator/build.gradle b/features/navigator/build.gradle
index c6b5dba..06e2a88 100644
--- a/features/navigator/build.gradle
+++ b/features/navigator/build.gradle
@@ -1,4 +1,5 @@
applyAndroidLibraryModule(project)
+apply plugin: 'kotlin-parcelize'
dependencies {
implementation project(":core")
diff --git a/features/navigator/src/main/kotlin/app/dapk/st/navigator/Navigator.kt b/features/navigator/src/main/kotlin/app/dapk/st/navigator/Navigator.kt
index a73e2fa..a996466 100644
--- a/features/navigator/src/main/kotlin/app/dapk/st/navigator/Navigator.kt
+++ b/features/navigator/src/main/kotlin/app/dapk/st/navigator/Navigator.kt
@@ -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) {
+ 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): 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 {
+ 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")
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/features/share-entry/build.gradle b/features/share-entry/build.gradle
index 3e36d64..542fb16 100644
--- a/features/share-entry/build.gradle
+++ b/features/share-entry/build.gradle
@@ -9,4 +9,5 @@ dependencies {
implementation project(':matrix:services:message')
implementation project(":core")
implementation project(":design-library")
+ implementation project(":features:navigator")
}
\ No newline at end of file
diff --git a/features/share-entry/src/main/kotlin/app/dapk/st/share/FetchRoomsUseCase.kt b/features/share-entry/src/main/kotlin/app/dapk/st/share/FetchRoomsUseCase.kt
new file mode 100644
index 0000000..18aca0c
--- /dev/null
+++ b/features/share-entry/src/main/kotlin/app/dapk/st/share/FetchRoomsUseCase.kt
@@ -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- {
+ return syncSyncService.overview().first().map {
+ Item(
+ it.roomId,
+ it.roomAvatarUrl,
+ it.roomName ?: "",
+ roomService.findMembersSummary(it.roomId).map { it.displayName ?: it.id.value }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryActivity.kt b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryActivity.kt
index b4651d5..da44704 100644
--- a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryActivity.kt
+++ b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryActivity.kt
@@ -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)
}
}
}
diff --git a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryScreen.kt b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryScreen.kt
index 634c6dd..85ddb3b 100644
--- a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryScreen.kt
+++ b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryScreen.kt
@@ -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)
diff --git a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryState.kt b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryState.kt
index 17a4737..57a21f3 100644
--- a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryState.kt
+++ b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryState.kt
@@ -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) : DirectoryEvent
}
data class Item(val id: RoomId, val roomAvatarUrl: AvatarUrl?, val roomName: String, val members: List)
diff --git a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryViewModel.kt b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryViewModel.kt
index db3e9cc..ddd6fbe 100644
--- a/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryViewModel.kt
+++ b/features/share-entry/src/main/kotlin/app/dapk/st/share/ShareEntryViewModel.kt
@@ -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? = null
private var syncJob: Job? = null
fun start() {
@@ -34,31 +30,14 @@ class ShareEntryViewModel(
syncJob?.cancel()
}
-
- fun sendAttachment() {
-
- }
-
fun withUris(urisToShare: List) {
-// TODO("Not yet implemented")
+ this.urisToShare = urisToShare.map { AndroidUri(it.toString()) }
}
-}
-
-class FetchRoomsUseCase(
- private val syncSyncService: SyncService,
- private val roomService: RoomService,
-) {
-
- suspend fun bar(): List
- {
- return syncSyncService.overview().first().map {
- Item(
- it.roomId,
- it.roomAvatarUrl,
- it.roomName ?: "",
- roomService.findMembersSummary(it.roomId).map { it.displayName ?: it.id.value }
- )
+ fun onRoomSelected(item: Item) {
+ viewModelScope.launch {
+ _events.emit(DirectoryEvent.SelectRoom(item, uris = urisToShare ?: throw IllegalArgumentException("Not uris set")))
}
}
-}
+}
diff --git a/matrix/common/src/main/kotlin/app/dapk/st/matrix/common/EventType.kt b/matrix/common/src/main/kotlin/app/dapk/st/matrix/common/EventType.kt
index 4f1c309..85a6e4e 100644
--- a/matrix/common/src/main/kotlin/app/dapk/st/matrix/common/EventType.kt
+++ b/matrix/common/src/main/kotlin/app/dapk/st/matrix/common/EventType.kt
@@ -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"),
}
\ No newline at end of file
diff --git a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/MessageService.kt b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/MessageService.kt
index 6103470..7ef72a5 100644
--- a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/MessageService.kt
+++ b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/MessageService.kt
@@ -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
diff --git a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/DefaultMessageService.kt b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/DefaultMessageService.kt
index 4a30b43..c02ab8d 100644
--- a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/DefaultMessageService.kt
+++ b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/DefaultMessageService.kt
@@ -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)
+ )
}
}
diff --git a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt
index 6a6e2be..e35fb0f 100644
--- a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt
+++ b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt
@@ -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()
+ }
}
}
diff --git a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendRequest.kt b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendRequest.kt
index 53df1c3..5183f72 100644
--- a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendRequest.kt
+++ b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendRequest.kt
@@ -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)
}
)
diff --git a/test-harness/src/test/kotlin/test/TestMatrix.kt b/test-harness/src/test/kotlin/test/TestMatrix.kt
index c324929..6b1c505 100644
--- a/test-harness/src/test/kotlin/test/TestMatrix.kt
+++ b/test-harness/src/test/kotlin/test/TestMatrix.kt
@@ -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()
}
)