From 492d7df9ac1c6d12928b7ed9160c0921f36756ae Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 17:24:05 +0100 Subject: [PATCH] moving login and home modules to the chat engine --- .../kotlin/app/dapk/st/graph/AppModule.kt | 7 +- chat-engine/build.gradle | 4 -- .../kotlin/app/dapk/st/engine/ChatEngine.kt | 42 ++---------- .../main/kotlin/app/dapk/st/engine/Models.kt | 64 +++++++++++++++++++ features/home/build.gradle | 4 +- .../kotlin/app/dapk/st/home/HomeModule.kt | 9 +-- .../main/kotlin/app/dapk/st/home/HomeState.kt | 4 +- .../kotlin/app/dapk/st/home/HomeViewModel.kt | 12 ++-- features/login/build.gradle | 4 +- .../kotlin/app/dapk/st/login/LoginModule.kt | 8 +-- .../app/dapk/st/login/LoginViewModel.kt | 20 +++--- .../app/dapk/st/engine/MappingExtensions.kt | 39 ++++++++++- .../kotlin/app/dapk/st/engine/MatrixEngine.kt | 61 +++++++++++------- 13 files changed, 174 insertions(+), 104 deletions(-) create mode 100644 chat-engine/src/main/kotlin/app/dapk/st/engine/Models.kt 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 1bc4ddd..28d6b80 100644 --- a/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt +++ b/app/src/main/kotlin/app/dapk/st/graph/AppModule.kt @@ -25,7 +25,6 @@ import app.dapk.st.imageloader.ImageLoaderModule import app.dapk.st.login.LoginModule import app.dapk.st.matrix.MatrixClient import app.dapk.st.matrix.auth.DeviceDisplayNameGenerator -import app.dapk.st.matrix.auth.authService import app.dapk.st.matrix.auth.installAuthService import app.dapk.st.matrix.common.* import app.dapk.st.matrix.crypto.RoomMembersProvider @@ -171,9 +170,8 @@ internal class FeatureModules internal constructor( } val loginModule by unsafeLazy { LoginModule( - matrixModules.auth, + matrixModules.engine, domainModules.pushModule, - matrixModules.profile, trackingModule.errorTracker ) } @@ -191,7 +189,7 @@ internal class FeatureModules internal constructor( storeModule.value.messageStore(), ) } - val homeModule by unsafeLazy { HomeModule(storeModule.value, matrixModules.profile, matrixModules.sync, buildMeta) } + val homeModule by unsafeLazy { HomeModule(matrixModules.engine, storeModule.value, buildMeta) } val settingsModule by unsafeLazy { SettingsModule( storeModule.value, @@ -473,7 +471,6 @@ internal class MatrixModules( } } - val auth by unsafeLazy { matrix.authService() } val push by unsafeLazy { matrix.pushService() } val sync by unsafeLazy { matrix.syncService() } val message by unsafeLazy { matrix.messageService() } diff --git a/chat-engine/build.gradle b/chat-engine/build.gradle index ee08897..05013e0 100644 --- a/chat-engine/build.gradle +++ b/chat-engine/build.gradle @@ -5,8 +5,4 @@ plugins { dependencies { api Dependencies.mavenCentral.kotlinCoroutinesCore api project(":matrix:common") - - implementation project(":matrix:services:sync") - implementation project(":matrix:services:message") - implementation project(":matrix:services:room") } \ No newline at end of file diff --git a/chat-engine/src/main/kotlin/app/dapk/st/engine/ChatEngine.kt b/chat-engine/src/main/kotlin/app/dapk/st/engine/ChatEngine.kt index 556b478..339ba79 100644 --- a/chat-engine/src/main/kotlin/app/dapk/st/engine/ChatEngine.kt +++ b/chat-engine/src/main/kotlin/app/dapk/st/engine/ChatEngine.kt @@ -1,46 +1,14 @@ package app.dapk.st.engine -import app.dapk.st.matrix.common.AvatarUrl -import app.dapk.st.matrix.common.EventId -import app.dapk.st.matrix.common.RoomId -import app.dapk.st.matrix.common.RoomMember import kotlinx.coroutines.flow.Flow interface ChatEngine { fun directory(): Flow + fun invites(): Flow + + suspend fun login(request: LoginRequest): LoginResult + + suspend fun me(forceRefresh: Boolean): Me } - -typealias DirectoryState = List -typealias OverviewState = List - -data class DirectoryItem( - val overview: RoomOverview, - val unreadCount: UnreadCount, - val typing: Typing? -) - -data class RoomOverview( - val roomId: RoomId, - val roomCreationUtc: Long, - val roomName: String?, - val roomAvatarUrl: AvatarUrl?, - val lastMessage: LastMessage?, - val isGroup: Boolean, - val readMarker: EventId?, - val isEncrypted: Boolean, -) { - - data class LastMessage( - val content: String, - val utcTimestamp: Long, - val author: RoomMember, - ) - -} - -@JvmInline -value class UnreadCount(val value: Int) - -data class Typing(val roomId: RoomId, val members: List) diff --git a/chat-engine/src/main/kotlin/app/dapk/st/engine/Models.kt b/chat-engine/src/main/kotlin/app/dapk/st/engine/Models.kt new file mode 100644 index 0000000..14d1189 --- /dev/null +++ b/chat-engine/src/main/kotlin/app/dapk/st/engine/Models.kt @@ -0,0 +1,64 @@ +package app.dapk.st.engine + +import app.dapk.st.matrix.common.* + +typealias DirectoryState = List +typealias OverviewState = List +typealias InviteState = List + +data class DirectoryItem( + val overview: RoomOverview, + val unreadCount: UnreadCount, + val typing: Typing? +) + +data class RoomOverview( + val roomId: RoomId, + val roomCreationUtc: Long, + val roomName: String?, + val roomAvatarUrl: AvatarUrl?, + val lastMessage: LastMessage?, + val isGroup: Boolean, + val readMarker: EventId?, + val isEncrypted: Boolean, +) { + + data class LastMessage( + val content: String, + val utcTimestamp: Long, + val author: RoomMember, + ) + +} + +data class RoomInvite( + val from: RoomMember, + val roomId: RoomId, + val inviteMeta: InviteMeta, +) { + sealed class InviteMeta { + object DirectMessage : InviteMeta() + data class Room(val roomName: String? = null) : InviteMeta() + } + +} + +@JvmInline +value class UnreadCount(val value: Int) + +data class Typing(val roomId: RoomId, val members: List) + +data class LoginRequest(val userName: String, val password: String, val serverUrl: String?) + +sealed interface LoginResult { + data class Success(val userCredentials: UserCredentials) : LoginResult + object MissingWellKnown : LoginResult + data class Error(val cause: Throwable) : LoginResult +} + +data class Me( + val userId: UserId, + val displayName: String?, + val avatarUrl: AvatarUrl?, + val homeServerUrl: HomeServerUrl, +) diff --git a/features/home/build.gradle b/features/home/build.gradle index 0422dfa..41343e5 100644 --- a/features/home/build.gradle +++ b/features/home/build.gradle @@ -1,9 +1,7 @@ applyAndroidComposeLibraryModule(project) dependencies { - implementation project(":matrix:services:profile") - implementation project(":matrix:services:crypto") - implementation project(":matrix:services:sync") + implementation project(":chat-engine") implementation project(":features:directory") implementation project(":features:login") implementation project(":features:settings") diff --git a/features/home/src/main/kotlin/app/dapk/st/home/HomeModule.kt b/features/home/src/main/kotlin/app/dapk/st/home/HomeModule.kt index b49c5cc..210b5aa 100644 --- a/features/home/src/main/kotlin/app/dapk/st/home/HomeModule.kt +++ b/features/home/src/main/kotlin/app/dapk/st/home/HomeModule.kt @@ -4,31 +4,28 @@ import app.dapk.st.core.BuildMeta import app.dapk.st.core.ProvidableModule import app.dapk.st.directory.DirectoryViewModel import app.dapk.st.domain.StoreModule +import app.dapk.st.engine.ChatEngine import app.dapk.st.login.LoginViewModel -import app.dapk.st.matrix.room.ProfileService -import app.dapk.st.matrix.sync.SyncService import app.dapk.st.profile.ProfileViewModel class HomeModule( + private val chatEngine: ChatEngine, private val storeModule: StoreModule, - private val profileService: ProfileService, - private val syncService: SyncService, private val buildMeta: BuildMeta, ) : ProvidableModule { fun homeViewModel(directory: DirectoryViewModel, login: LoginViewModel, profileViewModel: ProfileViewModel): HomeViewModel { return HomeViewModel( + chatEngine, storeModule.credentialsStore(), directory, login, profileViewModel, - profileService, storeModule.cacheCleaner(), BetaVersionUpgradeUseCase( storeModule.applicationStore(), buildMeta, ), - syncService, ) } diff --git a/features/home/src/main/kotlin/app/dapk/st/home/HomeState.kt b/features/home/src/main/kotlin/app/dapk/st/home/HomeState.kt index 7bf0114..c7d74a3 100644 --- a/features/home/src/main/kotlin/app/dapk/st/home/HomeState.kt +++ b/features/home/src/main/kotlin/app/dapk/st/home/HomeState.kt @@ -4,13 +4,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Settings import androidx.compose.ui.graphics.vector.ImageVector -import app.dapk.st.matrix.room.ProfileService +import app.dapk.st.engine.Me sealed interface HomeScreenState { object Loading : HomeScreenState object SignedOut : HomeScreenState - data class SignedIn(val page: Page, val me: ProfileService.Me, val invites: Int) : HomeScreenState + data class SignedIn(val page: Page, val me: Me, val invites: Int) : HomeScreenState enum class Page(val icon: ImageVector) { Directory(Icons.Filled.Menu), diff --git a/features/home/src/main/kotlin/app/dapk/st/home/HomeViewModel.kt b/features/home/src/main/kotlin/app/dapk/st/home/HomeViewModel.kt index 728c6c0..456f0a0 100644 --- a/features/home/src/main/kotlin/app/dapk/st/home/HomeViewModel.kt +++ b/features/home/src/main/kotlin/app/dapk/st/home/HomeViewModel.kt @@ -3,12 +3,11 @@ package app.dapk.st.home import androidx.lifecycle.viewModelScope import app.dapk.st.directory.DirectoryViewModel import app.dapk.st.domain.StoreCleaner +import app.dapk.st.engine.ChatEngine import app.dapk.st.home.HomeScreenState.* import app.dapk.st.login.LoginViewModel import app.dapk.st.matrix.common.CredentialsStore import app.dapk.st.matrix.common.isSignedIn -import app.dapk.st.matrix.room.ProfileService -import app.dapk.st.matrix.sync.SyncService import app.dapk.st.profile.ProfileViewModel import app.dapk.st.viewmodel.DapkViewModel import kotlinx.coroutines.CoroutineScope @@ -20,14 +19,13 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch class HomeViewModel( + private val chatEngine: ChatEngine, private val credentialsProvider: CredentialsStore, private val directoryViewModel: DirectoryViewModel, private val loginViewModel: LoginViewModel, private val profileViewModel: ProfileViewModel, - private val profileService: ProfileService, private val cacheCleaner: StoreCleaner, private val betaVersionUpgradeUseCase: BetaVersionUpgradeUseCase, - private val syncService: SyncService, ) : DapkViewModel( initialState = Loading ) { @@ -56,8 +54,8 @@ class HomeViewModel( } private suspend fun initialHomeContent(): SignedIn { - val me = profileService.me(forceRefresh = false) - val initialInvites = syncService.invites().first().size + val me = chatEngine.me(forceRefresh = false) + val initialInvites = chatEngine.invites().first().size return SignedIn(Page.Directory, me, invites = initialInvites) } @@ -70,7 +68,7 @@ class HomeViewModel( private fun CoroutineScope.listenForInviteChanges() { listenForInvitesJob?.cancel() - listenForInvitesJob = syncService.invites() + listenForInvitesJob = chatEngine.invites() .onEach { invites -> when (val currentState = state) { is SignedIn -> updateState { currentState.copy(invites = invites.size) } diff --git a/features/login/build.gradle b/features/login/build.gradle index 21dcd44..ac3340b 100644 --- a/features/login/build.gradle +++ b/features/login/build.gradle @@ -1,12 +1,10 @@ applyAndroidComposeLibraryModule(project) dependencies { + implementation project(":chat-engine") implementation project(":domains:android:compose-core") implementation project(":domains:android:push") implementation project(":domains:android:viewmodel") - implementation project(":matrix:services:auth") - implementation project(":matrix:services:profile") - implementation project(":matrix:services:crypto") implementation project(":design-library") implementation project(":core") } \ No newline at end of file diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginModule.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginModule.kt index 9180e52..c745f9f 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginModule.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginModule.kt @@ -2,18 +2,16 @@ package app.dapk.st.login import app.dapk.st.core.ProvidableModule import app.dapk.st.core.extensions.ErrorTracker -import app.dapk.st.matrix.auth.AuthService -import app.dapk.st.matrix.room.ProfileService +import app.dapk.st.engine.ChatEngine import app.dapk.st.push.PushModule class LoginModule( - private val authService: AuthService, + private val chatEngine: ChatEngine, private val pushModule: PushModule, - private val profileService: ProfileService, private val errorTracker: ErrorTracker, ) : ProvidableModule { fun loginViewModel(): LoginViewModel { - return LoginViewModel(authService, pushModule.pushTokenRegistrar(), profileService, errorTracker) + return LoginViewModel(chatEngine, pushModule.pushTokenRegistrar(), errorTracker) } } \ No newline at end of file diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt index f82c80d..c8efb13 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt @@ -3,10 +3,11 @@ package app.dapk.st.login import androidx.lifecycle.viewModelScope import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.core.logP +import app.dapk.st.engine.ChatEngine +import app.dapk.st.engine.LoginRequest +import app.dapk.st.engine.LoginResult import app.dapk.st.login.LoginEvent.LoginComplete import app.dapk.st.login.LoginScreenState.* -import app.dapk.st.matrix.auth.AuthService -import app.dapk.st.matrix.room.ProfileService import app.dapk.st.push.PushTokenRegistrar import app.dapk.st.viewmodel.DapkViewModel import kotlinx.coroutines.async @@ -14,9 +15,8 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch class LoginViewModel( - private val authService: AuthService, + private val chatEngine: ChatEngine, private val pushTokenRegistrar: PushTokenRegistrar, - private val profileService: ProfileService, private val errorTracker: ErrorTracker, ) : DapkViewModel( initialState = Content(showServerUrl = false) @@ -28,8 +28,8 @@ class LoginViewModel( state = Loading viewModelScope.launch { logP("login") { - when (val result = authService.login(AuthService.LoginRequest(userName, password, serverUrl.takeIfNotEmpty()))) { - is AuthService.LoginResult.Success -> { + when (val result = chatEngine.login(LoginRequest(userName, password, serverUrl.takeIfNotEmpty()))) { + is LoginResult.Success -> { runCatching { listOf( async { pushTokenRegistrar.registerCurrentToken() }, @@ -38,11 +38,13 @@ class LoginViewModel( } _events.tryEmit(LoginComplete) } - is AuthService.LoginResult.Error -> { + + is LoginResult.Error -> { errorTracker.track(result.cause) state = Error(result.cause) } - AuthService.LoginResult.MissingWellKnown -> { + + LoginResult.MissingWellKnown -> { _events.tryEmit(LoginEvent.WellKnownMissing) state = Content(showServerUrl = true) } @@ -51,7 +53,7 @@ class LoginViewModel( } } - private suspend fun preloadMe() = profileService.me(forceRefresh = false) + private suspend fun preloadMe() = chatEngine.me(forceRefresh = false) fun start() { val showServerUrl = previousState?.let { it is Content && it.showServerUrl } ?: false diff --git a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MappingExtensions.kt b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MappingExtensions.kt index 0486699..6e26cc4 100644 --- a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MappingExtensions.kt +++ b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MappingExtensions.kt @@ -1,6 +1,12 @@ package app.dapk.st.engine +import app.dapk.st.matrix.auth.AuthService +import app.dapk.st.matrix.sync.InviteMeta +import app.dapk.st.matrix.auth.AuthService.LoginRequest as MatrixLoginRequest +import app.dapk.st.matrix.auth.AuthService.LoginResult as MatrixLoginResult +import app.dapk.st.matrix.room.ProfileService.Me as MatrixMe import app.dapk.st.matrix.sync.LastMessage as MatrixLastMessage +import app.dapk.st.matrix.sync.RoomInvite as MatrixRoomInvite import app.dapk.st.matrix.sync.RoomOverview as MatrixRoomOverview import app.dapk.st.matrix.sync.SyncService.SyncEvent.Typing as MatrixTyping @@ -24,4 +30,35 @@ fun MatrixLastMessage.engine() = RoomOverview.LastMessage( fun MatrixTyping.engine() = Typing( this.roomId, this.members, -) \ No newline at end of file +) + +fun LoginRequest.engine() = MatrixLoginRequest( + this.userName, + this.password, + this.serverUrl +) + +fun MatrixLoginResult.engine() = when (this) { + is AuthService.LoginResult.Error -> LoginResult.Error(this.cause) + AuthService.LoginResult.MissingWellKnown -> LoginResult.MissingWellKnown + is AuthService.LoginResult.Success -> LoginResult.Success(this.userCredentials) +} + +fun MatrixMe.engine() = Me( + this.userId, + this.displayName, + this.avatarUrl, + this.homeServerUrl, +) + +fun MatrixRoomInvite.engine() = RoomInvite( + this.from, + this.roomId, + this.inviteMeta.engine(), +) + +fun InviteMeta.engine() = when (this) { + InviteMeta.DirectMessage -> RoomInvite.InviteMeta.DirectMessage + is InviteMeta.Room -> RoomInvite.InviteMeta.Room(this.roomName) +} + diff --git a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MatrixEngine.kt b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MatrixEngine.kt index e58235d..9caefa5 100644 --- a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MatrixEngine.kt +++ b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/MatrixEngine.kt @@ -7,6 +7,7 @@ import app.dapk.st.core.SingletonFlows import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.matrix.MatrixClient import app.dapk.st.matrix.auth.DeviceDisplayNameGenerator +import app.dapk.st.matrix.auth.authService import app.dapk.st.matrix.auth.installAuthService import app.dapk.st.matrix.common.* import app.dapk.st.matrix.crypto.RoomMembersProvider @@ -27,13 +28,27 @@ import app.dapk.st.matrix.sync.internal.room.MessageDecrypter import app.dapk.st.olm.DeviceKeyFactory import app.dapk.st.olm.OlmStore import app.dapk.st.olm.OlmWrapper +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import java.time.Clock class MatrixEngine internal constructor( private val directoryUseCase: Lazy, + private val matrix: Lazy, ) : ChatEngine { override fun directory() = directoryUseCase.value.state() + override fun invites(): Flow { + return matrix.value.syncService().invites().map { it.map { it.engine() } } + } + + override suspend fun login(request: LoginRequest): LoginResult { + return matrix.value.authService().login(request.engine()).engine() + } + + override suspend fun me(forceRefresh: Boolean): Me { + return matrix.value.profileService().me(forceRefresh).engine() + } class Factory { @@ -57,28 +72,30 @@ class MatrixEngine internal constructor( knownDeviceStore: KnownDeviceStore, olmStore: OlmStore, ): ChatEngine { - val matrix = MatrixFactory.createMatrix( - base64, - buildMeta, - logger, - nameGenerator, - coroutineDispatchers, - errorTracker, - imageContentReader, - backgroundScheduler, - memberStore, - roomStore, - profileStore, - syncStore, - overviewStore, - filterStore, - localEchoStore, - credentialsStore, - knownDeviceStore, - olmStore - ) - + val lazyMatrix = unsafeLazy { + MatrixFactory.createMatrix( + base64, + buildMeta, + logger, + nameGenerator, + coroutineDispatchers, + errorTracker, + imageContentReader, + backgroundScheduler, + memberStore, + roomStore, + profileStore, + syncStore, + overviewStore, + filterStore, + localEchoStore, + credentialsStore, + knownDeviceStore, + olmStore + ) + } val directoryUseCase = unsafeLazy { + val matrix = lazyMatrix.value DirectoryUseCase( matrix.syncService(), matrix.messageService(), @@ -88,7 +105,7 @@ class MatrixEngine internal constructor( ) } - return MatrixEngine(directoryUseCase) + return MatrixEngine(directoryUseCase, lazyMatrix) }