Skip to content

Commit

Permalink
Merge pull request #182 from ouchadam/feature/avoid-local-echo-size-c…
Browse files Browse the repository at this point in the history
…hange

Avoid local echo image size change
  • Loading branch information
ouchadam authored Oct 2, 2022
2 parents 0b457fe + df26745 commit d74db39
Show file tree
Hide file tree
Showing 14 changed files with 71 additions and 30 deletions.
10 changes: 7 additions & 3 deletions app/src/main/kotlin/app/dapk/st/graph/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
private val workModule = WorkModule(context)
private val imageLoaderModule = ImageLoaderModule(context)

private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver, base64, buildMeta)
private val imageContentReader by unsafeLazy { AndroidImageContentReader(context.contentResolver) }
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, imageContentReader, base64, buildMeta)

val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context, coroutineDispatchers)

val coreAndroidModule = CoreAndroidModule(
Expand Down Expand Up @@ -133,6 +135,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
trackingModule,
coreAndroidModule,
imageLoaderModule,
imageContentReader,
context,
buildMeta,
deviceMeta,
Expand All @@ -149,6 +152,7 @@ internal class FeatureModules internal constructor(
private val trackingModule: TrackingModule,
private val coreAndroidModule: CoreAndroidModule,
imageLoaderModule: ImageLoaderModule,
imageContentReader: ImageContentReader,
context: Context,
buildMeta: BuildMeta,
deviceMeta: DeviceMeta,
Expand Down Expand Up @@ -185,6 +189,7 @@ internal class FeatureModules internal constructor(
clock,
context,
base64,
imageContentReader,
)
}
val homeModule by unsafeLazy { HomeModule(storeModule.value, matrixModules.profile, matrixModules.sync, buildMeta) }
Expand Down Expand Up @@ -238,7 +243,7 @@ internal class MatrixModules(
private val workModule: WorkModule,
private val logger: MatrixLogger,
private val coroutineDispatchers: CoroutineDispatchers,
private val contentResolver: ContentResolver,
private val imageContentReader: ImageContentReader,
private val base64: Base64,
private val buildMeta: BuildMeta,
) {
Expand Down Expand Up @@ -280,7 +285,6 @@ internal class MatrixModules(
base64 = base64,
coroutineDispatchers = coroutineDispatchers,
)
val imageContentReader = AndroidImageContentReader(contentResolver)
installMessageService(
store.localEchoStore,
BackgroundWorkAdapter(workModule.workScheduler()),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package app.dapk.st.core.extensions

import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.*

@OptIn(InternalCoroutinesApi::class)
suspend fun <T> Flow<T>.firstOrNull(count: Int, predicate: suspend (T) -> Boolean): T? {
var counter = 0

Expand All @@ -21,4 +17,6 @@ suspend fun <T> Flow<T>.firstOrNull(count: Int, predicate: suspend (T) -> Boolea
}

return result
}
}

fun <T> Flow<T>.startAndIgnoreEmissions(): Flow<Boolean> = this.map { false }.onStart { emit(true) }.filter { it }
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ import app.dapk.st.matrix.sync.RoomStore
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOneNotNull
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.*
import kotlinx.serialization.json.Json

private val json = Json
Expand Down Expand Up @@ -54,12 +51,13 @@ internal class RoomPersistence(
override fun latest(roomId: RoomId): Flow<RoomState> {
val overviewFlow = database.overviewStateQueries.selectRoom(roomId.value).asFlow().mapToOneNotNull().map {
json.decodeFromString(RoomOverview.serializer(), it)
}
}.distinctUntilChanged()

return database.roomEventQueries.selectRoom(roomId.value)
.asFlow()
.mapToList()
.map { it.map { json.decodeFromString(RoomEvent.serializer(), it) } }
.distinctUntilChanged()
.combine(overviewFlow) { events, overview ->
RoomState(overview, events)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal class LocalEchoMapper(private val metaMapper: MetaMapper) {
author = member,
utcTimestamp = message.timestampUtc,
meta = metaMapper.toMeta(this),
imageMeta = RoomEvent.Image.ImageMeta(100, 100, message.content.uri, null),
imageMeta = RoomEvent.Image.ImageMeta(message.content.meta.width, message.content.meta.height, message.content.uri, null),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import app.dapk.st.core.ProvidableModule
import app.dapk.st.matrix.common.CredentialsStore
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomStore
import app.dapk.st.matrix.sync.SyncService
Expand All @@ -20,10 +21,11 @@ class MessengerModule(
private val clock: Clock,
private val context: Context,
private val base64: Base64,
private val imageMetaReader: ImageContentReader,
) : ProvidableModule {

internal fun messengerViewModel(): MessengerViewModel {
return MessengerViewModel(messageService, roomService, roomStore, credentialsStore, timelineUseCase(), LocalIdFactory(), clock)
return MessengerViewModel(messageService, roomService, roomStore, credentialsStore, timelineUseCase(), LocalIdFactory(), imageMetaReader, clock)
}

private fun timelineUseCase(): TimelineUseCaseImpl {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomStore
Expand All @@ -28,6 +29,7 @@ internal class MessengerViewModel(
private val credentialsStore: CredentialsStore,
private val observeTimeline: ObserveTimelineUseCase,
private val localIdFactory: LocalIdFactory,
private val imageContentReader: ImageContentReader,
private val clock: Clock,
factory: MutableStateFactory<MessengerScreenState> = defaultStateFactory(),
) : DapkViewModel<MessengerScreenState, MessengerEvent>(
Expand Down Expand Up @@ -147,9 +149,20 @@ internal class MessengerViewModel(
state.roomState.takeIfContent()?.let { content ->
val roomState = content.roomState
viewModelScope.launch {
val imageUri = copy.values.first().uri.value
val meta = imageContentReader.meta(imageUri)

messageService.scheduleMessage(
MessageService.Message.ImageMessage(
MessageService.Message.Content.ApiImageContent(uri = copy.values.first().uri.value),
MessageService.Message.Content.ImageContent(
uri = imageUri, MessageService.Message.Content.ImageContent.Meta(
height = meta.height,
width = meta.width,
size = meta.size,
fileName = meta.fileName,
mimeType = meta.mimeType,
)
),
roomId = roomState.roomOverview.roomId,
sendEncrypted = roomState.roomOverview.isEncrypted,
localId = localIdFactory.create(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.dapk.st.messenger

import app.dapk.st.core.extensions.startAndIgnoreEmissions
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.common.UserId
Expand All @@ -9,8 +10,6 @@ import app.dapk.st.matrix.sync.RoomState
import app.dapk.st.matrix.sync.SyncService
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart

internal typealias ObserveTimelineUseCase = (RoomId, UserId) -> Flow<MessengerState>

Expand All @@ -25,7 +24,7 @@ internal class TimelineUseCaseImpl(
return combine(
roomDatasource(roomId),
messageService.localEchos(roomId),
syncService.events()
syncService.events(roomId)
) { roomState, localEchos, events ->
MessengerState(
roomState = when {
Expand All @@ -45,7 +44,7 @@ internal class TimelineUseCaseImpl(
}

private fun roomDatasource(roomId: RoomId) = combine(
syncService.startSyncing().map { false }.onStart { emit(true) },
syncService.startSyncing().startAndIgnoreEmissions(),
syncService.room(roomId)
) { _, room -> room }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomState
import app.dapk.st.matrix.sync.SyncService
Expand Down Expand Up @@ -47,6 +48,7 @@ class MessengerViewModelTest {
fakeCredentialsStore,
fakeObserveTimelineUseCase,
localIdFactory = FakeLocalIdFactory().also { it.givenCreate().returns(A_LOCAL_ID) }.instance,
imageContentReader = FakeImageContentReader(),
clock = fixedClock(A_CURRENT_TIMESTAMP),
factory = runViewModelTest.testMutableStateFactory(),
)
Expand Down Expand Up @@ -150,3 +152,5 @@ class FakeRoomService : RoomService by mockk() {
}

fun fixedClock(timestamp: Long = 0) = Clock.fixed(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC)

class FakeImageContentReader: ImageContentReader by mockk()
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ interface MessageService : MatrixService {
@Serializable
@SerialName("image_message")
data class ImageMessage(
@SerialName("content") val content: Content.ApiImageContent,
@SerialName("content") val content: Content.ImageContent,
@SerialName("send_encrypted") val sendEncrypted: Boolean,
@SerialName("room_id") val roomId: RoomId,
@SerialName("local_id") val localId: String,
Expand All @@ -70,9 +70,21 @@ interface MessageService : MatrixService {
) : Content()

@Serializable
data class ApiImageContent(
data class ImageContent(
@SerialName("uri") val uri: String,
) : Content()
@SerialName("meta") val meta: Meta,
) : Content() {

@Serializable
data class Meta(
val height: Int,
val width: Int,
val size: Long,
val fileName: String,
val mimeType: String,
)

}

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal class SendMessageUseCase(
}

private suspend fun imageMessageRequest(message: Message.ImageMessage): HttpRequest<ApiSendResponse> {
val imageMeta = imageContentReader.meta(message.content.uri)
val imageMeta = message.content.meta

return when (message.sendEncrypted) {
true -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ interface SyncService : MatrixService {
fun overview(): Flow<OverviewState>
fun room(roomId: RoomId): Flow<RoomState>
fun startSyncing(): Flow<Unit>
fun events(): Flow<List<SyncEvent>>
fun events(roomId: RoomId? = null): Flow<List<SyncEvent>>
suspend fun observeEvent(eventId: EventId): Flow<EventId>
suspend fun forceManualRefresh(roomIds: List<RoomId>)

@JvmInline
value class FilterId(val value: String)

sealed interface SyncEvent {
data class Typing(val roomId: RoomId, val members: List<RoomMember>) : SyncEvent
val roomId: RoomId

data class Typing(override val roomId: RoomId, val members: List<RoomMember>) : SyncEvent
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ internal class DefaultSyncService(
override fun invites() = overviewStore.latestInvites()
override fun overview() = overviewStore.latest()
override fun room(roomId: RoomId) = roomStore.latest(roomId)
override fun events() = syncEventsFlow
override fun events(roomId: RoomId?) = roomId?.let { syncEventsFlow.map { it.filter { it.roomId == roomId } }.distinctUntilChanged() } ?: syncEventsFlow
override suspend fun observeEvent(eventId: EventId) = roomStore.observeEvent(eventId)
override suspend fun forceManualRefresh(roomIds: List<RoomId>) {
coroutineDispatchers.withIoContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ class FakeSyncService : SyncService by mockk() {

fun givenRoom(roomId: RoomId) = every { room(roomId) }.delegateReturn()

fun givenEvents(roomId: RoomId) = every { events() }.delegateReturn()
fun givenEvents(roomId: RoomId) = every { events(roomId) }.delegateReturn()

}
11 changes: 10 additions & 1 deletion test-harness/src/test/kotlin/test/Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,17 @@ class MatrixTestScope(private val testScope: TestScope) {
println("sending ${file.name}")
this.client.messageService().scheduleMessage(
MessageService.Message.ImageMessage(
content = MessageService.Message.Content.ApiImageContent(
content = MessageService.Message.Content.ImageContent(
uri = file.absolutePath,
meta = JavaImageContentReader().meta(file.absolutePath).let {
MessageService.Message.Content.ImageContent.Meta(
height = it.height,
width = it.width,
size = it.size,
fileName = it.fileName,
mimeType = it.mimeType,
)
}
),
roomId = roomId,
sendEncrypted = isEncrypted,
Expand Down

0 comments on commit d74db39

Please sign in to comment.