diff --git a/core/datastore-proto/src/androidMain/AndroidManifest.xml b/core/datastore-proto/src/androidMain/AndroidManifest.xml deleted file mode 100644 index 961389810..000000000 --- a/core/datastore-proto/src/androidMain/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/core/datastore-proto/src/androidMain/proto/org.mifospay.core.data/user_preferences.proto b/core/datastore-proto/src/androidMain/proto/org.mifospay.core.data/user_preferences.proto deleted file mode 100644 index 03791d71b..000000000 --- a/core/datastore-proto/src/androidMain/proto/org.mifospay.core.data/user_preferences.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -option java_package = "org.mifospay.core.datastore.proto"; -option java_multiple_files = true; - -message UserPreferences { - string auth_token = 1; - string user = 2; - string user_email = 3; - string client = 4; - - // NEXT AVAILABLE ID: 5 -} diff --git a/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/ClientPreferences.kt b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/ClientPreferences.kt new file mode 100644 index 000000000..f3d4a9bf3 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/ClientPreferences.kt @@ -0,0 +1,24 @@ +package org.mifospay.core.datastore.proto + +import kotlinx.serialization.Serializable + +@Serializable +data class ClientPreferences( + val name: String, + val image: String, + val externalId: String, + val clientId: Long, + val displayName: String, + val mobileNo: String, +) { + companion object { + val DEFAULT = ClientPreferences( + name = "", + image = "", + externalId = "", + clientId = 0, + displayName = "", + mobileNo = "", + ) + } +} \ No newline at end of file diff --git a/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/RolePreferences.kt b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/RolePreferences.kt new file mode 100644 index 000000000..5568d8e46 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/RolePreferences.kt @@ -0,0 +1,20 @@ +package org.mifospay.core.datastore.proto + +import kotlinx.serialization.Serializable + +@Serializable +data class RolePreferences( + val id: String, + val name: String, + val description: String, + val disabled: Boolean +) { + companion object { + val DEFAULT = RolePreferences( + id = "", + name = "", + description = "", + disabled = false + ) + } +} diff --git a/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserInfoPreferences.kt b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserInfoPreferences.kt new file mode 100644 index 000000000..179ca3c3b --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserInfoPreferences.kt @@ -0,0 +1,34 @@ +package org.mifospay.core.datastore.proto + +import kotlinx.serialization.Serializable + +@Serializable +data class UserInfoPreferences( + val username: String, + val userId: Int, + val base64EncodedAuthenticationKey: String, + val authenticated: Boolean, + val officeId: Int, + val officeName: String, + val roles: List, + val permissions: List, + val clients: List, + val shouldRenewPassword: Boolean, + val isTwoFactorAuthenticationRequired: Boolean +) { + companion object { + val DEFAULT = UserInfoPreferences( + username = "", + userId = 0, + base64EncodedAuthenticationKey = "", + authenticated = false, + officeId = 0, + officeName = "", + roles = emptyList(), + permissions = emptyList(), + clients = emptyList(), + shouldRenewPassword = false, + isTwoFactorAuthenticationRequired = false + ) + } +} diff --git a/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserPreferences.kt b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserPreferences.kt new file mode 100644 index 000000000..07113fc78 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/org/mifospay/core/datastore/proto/UserPreferences.kt @@ -0,0 +1,36 @@ +package org.mifospay.core.datastore.proto + +import kotlinx.serialization.Serializable + +@Serializable +data class UserPreferences( + val token: String, + val name: String, + val username: String, + val email: String, + val mobileNo: String, + val userId: Int, + val clientId: Int, + val clientVpa: String, + val accountId: Int, + val firebaseRegId: String, + val client: ClientPreferences, + val user: UserInfoPreferences, +) { + companion object { + val DEFAULT = UserPreferences( + token = "", + name = "", + username = "", + email = "", + mobileNo = "", + userId = 0, + clientId = 0, + clientVpa = "", + accountId = 0, + firebaseRegId = "", + client = ClientPreferences.DEFAULT, + user = UserInfoPreferences.DEFAULT, + ) + } +} diff --git a/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/client_info.proto b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/client_info.proto new file mode 100644 index 000000000..f845e980c --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/client_info.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +option java_package = "org.mifospay.core.datastore.proto"; +option java_multiple_files = true; + +message Client { + string name = 1; + string image = 2; + string external_id = 3; + int64 client_id = 4; + string display_name = 5; + string mobile_no = 6; +} \ No newline at end of file diff --git a/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/role_info.proto b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/role_info.proto new file mode 100644 index 000000000..b9756aa52 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/role_info.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +option java_package = "org.mifospay.core.datastore.proto"; +option java_multiple_files = true; + +message Role { + string id = 1; + string name = 2; + string description = 3; + bool disabled = 4; +} \ No newline at end of file diff --git a/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_info.proto b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_info.proto new file mode 100644 index 000000000..9d38f3d91 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_info.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +import "proto/org/mifospay/core/datastore/proto/role_info.proto"; + +option java_package = "org.mifospay.core.datastore.proto"; +option java_multiple_files = true; + +message User { + string username = 1; + int64 userId = 2; + string base64EncodedAuthenticationKey = 3; + bool authenticated = 4; + int32 officeId = 5; + string officeName = 6; + repeated Role roles = 7; + repeated string permissions = 8; + repeated int64 clients = 9; + bool shouldRenewPassword = 10; + bool isTwoFactorAuthenticationRequired = 11; +} \ No newline at end of file diff --git a/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_preference.proto b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_preference.proto new file mode 100644 index 000000000..762a78145 --- /dev/null +++ b/core/datastore-proto/src/commonMain/kotlin/proto/org/mifospay/core/datastore/proto/user_preference.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +import "org.mifospay.core.datastore.proto.client_info.proto"; +import "org.mifospay.core.datastore.proto.user_info.proto"; + +option java_package = "org.mifospay.core.datastore.proto"; +option java_multiple_files = true; + +message UserPreferences { + string token = 1; + string name = 2; + string username = 3; + string email = 4; + string mobile_no = 5; + int32 user_id = 6; + int32 client_id = 7; + string client_vpa = 8; + int32 account_id = 9; + string firebase_reg_id = 10; + Client client = 11; + User user = 12; +} \ No newline at end of file diff --git a/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/PreferencesMapper.kt b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/PreferencesMapper.kt new file mode 100644 index 000000000..a1fc33861 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/PreferencesMapper.kt @@ -0,0 +1,116 @@ +package org.mifospay.core.datastore + +import org.mifospay.core.datastore.proto.ClientPreferences +import org.mifospay.core.datastore.proto.RolePreferences +import org.mifospay.core.datastore.proto.UserInfoPreferences +import org.mifospay.core.datastore.proto.UserPreferences +import org.mifospay.core.model.ClientInfo +import org.mifospay.core.model.RoleInfo +import org.mifospay.core.model.UserData +import org.mifospay.core.model.UserInfo + +fun ClientPreferences.toClientInfo(): ClientInfo { + return ClientInfo( + name = name, + image = image, + externalId = externalId, + clientId = clientId, + displayName = displayName, + mobileNo = mobileNo, + ) +} + +fun ClientInfo.toClientPreferences(): ClientPreferences { + return ClientPreferences( + name = name, + image = image, + externalId = externalId, + clientId = clientId, + displayName = displayName, + mobileNo = mobileNo, + ) +} + +fun RolePreferences.toRoleInfo(): RoleInfo { + return RoleInfo( + id = id, + name = name, + description = description, + disabled = disabled, + ) +} + +fun RoleInfo.toRolePreferences(): RolePreferences { + return RolePreferences( + id = id, + name = name, + description = description, + disabled = disabled, + ) +} + +fun UserInfoPreferences.toUserInfo(): UserInfo { + return UserInfo( + username = username, + userId = userId, + base64EncodedAuthenticationKey = base64EncodedAuthenticationKey, + authenticated = authenticated, + officeId = officeId, + officeName = officeName, + roles = roles.map { it.toRoleInfo() }, + permissions = permissions, + clients = clients, + shouldRenewPassword = shouldRenewPassword, + isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired, + ) +} + +fun UserInfo.toUserInfoPreferences(): UserInfoPreferences { + return UserInfoPreferences( + username = username, + userId = userId, + base64EncodedAuthenticationKey = base64EncodedAuthenticationKey, + authenticated = authenticated, + officeId = officeId, + officeName = officeName, + roles = roles.map { it.toRolePreferences() }, + permissions = permissions, + clients = clients, + shouldRenewPassword = shouldRenewPassword, + isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired, + ) +} + +fun UserPreferences.toUserData(): UserData { + return UserData( + token = token, + name = name, + username = username, + email = email, + mobileNo = mobileNo, + userId = userId, + clientId = clientId, + clientVpa = clientVpa, + accountId = accountId, + firebaseRegId = firebaseRegId, + client = client.toClientInfo(), + user = user.toUserInfo(), + ) +} + +fun UserData.toUserPreferences(): UserPreferences { + return UserPreferences( + token = token, + name = name, + username = username, + email = email, + mobileNo = mobileNo, + userId = userId, + clientId = clientId, + clientVpa = clientVpa, + accountId = accountId, + firebaseRegId = firebaseRegId, + client = client.toClientPreferences(), + user = user.toUserInfoPreferences(), + ) +} \ No newline at end of file diff --git a/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesDataSource.kt b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesDataSource.kt new file mode 100644 index 000000000..a15d60c56 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/UserPreferencesDataSource.kt @@ -0,0 +1,150 @@ +@file:OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class) + +package org.mifospay.core.datastore + +import com.russhwolf.settings.ExperimentalSettingsApi +import com.russhwolf.settings.Settings +import com.russhwolf.settings.serialization.decodeValue +import com.russhwolf.settings.serialization.decodeValueOrNull +import com.russhwolf.settings.serialization.encodeValue +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import org.mifospay.core.datastore.proto.ClientPreferences +import org.mifospay.core.datastore.proto.RolePreferences +import org.mifospay.core.datastore.proto.UserInfoPreferences +import org.mifospay.core.datastore.proto.UserPreferences +import org.mifospay.core.model.ClientInfo +import org.mifospay.core.model.RoleInfo +import org.mifospay.core.model.UserData +import org.mifospay.core.model.UserInfo + +private const val USER_DATA_KEY = "userData" +private const val USER_INFO_KEY = "userInfo" +private const val CLIENT_INFO_KEY = "clientInfo" +private const val ROLE_INFO_KEY = "roleInfo" + +class UserPreferencesDataSource( + private val settings: Settings, + private val dispatcher: CoroutineDispatcher, +) { + private val _userData = MutableStateFlow( + settings.decodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + defaultValue = settings.decodeValueOrNull( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + ) ?: UserPreferences.DEFAULT, + ), + ) + + private val _userInfo = MutableStateFlow( + settings.decodeValue( + key = USER_INFO_KEY, + serializer = UserInfoPreferences.serializer(), + defaultValue = settings.decodeValueOrNull( + key = USER_INFO_KEY, + serializer = UserInfoPreferences.serializer(), + ) ?: UserInfoPreferences.DEFAULT, + ), + ) + + private val _clientInfo = MutableStateFlow( + settings.decodeValue( + key = CLIENT_INFO_KEY, + serializer = ClientPreferences.serializer(), + defaultValue = settings.decodeValueOrNull( + key = CLIENT_INFO_KEY, + serializer = ClientPreferences.serializer(), + ) ?: ClientPreferences.DEFAULT, + ), + ) + + private val _roleInfo = MutableStateFlow( + settings.decodeValue( + key = ROLE_INFO_KEY, + serializer = RolePreferences.serializer(), + defaultValue = settings.decodeValueOrNull( + key = ROLE_INFO_KEY, + serializer = RolePreferences.serializer(), + ) ?: RolePreferences.DEFAULT, + ), + ) + + val userData = _userData.map(UserPreferences::toUserData) + val token = _userData.map(UserPreferences::toUserData).map { it.token } + val userInfo = _userInfo.map(UserInfoPreferences::toUserInfo) + val clientInfo = _clientInfo.map(ClientPreferences::toClientInfo) + val roleInfo = _roleInfo.map(RolePreferences::toRoleInfo) + + suspend fun updateRoleInfo(roleInfo: RoleInfo) { + withContext(dispatcher) { + settings.putRolePreference(roleInfo.toRolePreferences()) + _roleInfo.value = roleInfo.toRolePreferences() + } + } + + suspend fun updateClientInfo(clientInfo: ClientInfo) { + withContext(dispatcher) { + settings.putClientPreference(clientInfo.toClientPreferences()) + _clientInfo.value = clientInfo.toClientPreferences() + } + } + + suspend fun updateUserInfo(userInfo: UserInfo) { + withContext(dispatcher) { + settings.putUserInfoPreference(userInfo.toUserInfoPreferences()) + _userInfo.value = userInfo.toUserInfoPreferences() + } + } + + suspend fun updateUserData(userData: UserData) { + withContext(dispatcher) { + settings.putUserPreference(userData.toUserPreferences()) + _userData.value = userData.toUserPreferences() + } + } +} + +private fun Settings.putClientPreference(preference: ClientPreferences) { + encodeValue( + key = CLIENT_INFO_KEY, + serializer = ClientPreferences.serializer(), + value = preference, + ) +} + +private fun Settings.putRolePreference(preference: RolePreferences) { + encodeValue( + key = CLIENT_INFO_KEY, + serializer = RolePreferences.serializer(), + value = preference, + ) +} + +private fun Settings.putUserInfoPreference(preference: UserInfoPreferences) { + encodeValue( + key = USER_INFO_KEY, + serializer = UserInfoPreferences.serializer(), + value = preference, + ) +} + +private fun Settings.putUserPreference(preference: UserPreferences) { + encodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + value = preference, + ) +} + +private fun Settings.getUserPreference(): UserPreferences { + return decodeValue( + key = USER_DATA_KEY, + serializer = UserPreferences.serializer(), + defaultValue = UserPreferences.DEFAULT, + ) +} diff --git a/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/di/PreferenceModule.kt b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/di/PreferenceModule.kt new file mode 100644 index 000000000..fb8139122 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/org/mifospay/core/datastore/di/PreferenceModule.kt @@ -0,0 +1,12 @@ +package org.mifospay.core.datastore.di + +import com.russhwolf.settings.Settings +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifospay.core.datastore.UserPreferencesDataSource + +val PreferencesModule = module { + factory { Settings() } + // Use the IO dispatcher name - MifosDispatchers.IO.name + factory { UserPreferencesDataSource(get(), get(named("IO"))) } +} \ No newline at end of file diff --git a/core/datastore/src/test/java/org/mifospay/core/datastore/ExampleUnitTest.kt b/core/datastore/src/test/java/org/mifospay/core/datastore/ExampleUnitTest.kt deleted file mode 100644 index e4d40ddc7..000000000 --- a/core/datastore/src/test/java/org/mifospay/core/datastore/ExampleUnitTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Mifos Initiative - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md - */ -package org.mifospay.core.datastore - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/core/model/src/commonMain/kotlin/org/mifospay/core/model/ClientInfo.kt b/core/model/src/commonMain/kotlin/org/mifospay/core/model/ClientInfo.kt new file mode 100644 index 000000000..54213d788 --- /dev/null +++ b/core/model/src/commonMain/kotlin/org/mifospay/core/model/ClientInfo.kt @@ -0,0 +1,10 @@ +package org.mifospay.core.model + +data class ClientInfo( + val name: String, + val image: String, + val externalId: String, + val clientId: Long, + val displayName: String, + val mobileNo: String, +) diff --git a/core/model/src/commonMain/kotlin/org/mifospay/core/model/RoleInfo.kt b/core/model/src/commonMain/kotlin/org/mifospay/core/model/RoleInfo.kt new file mode 100644 index 000000000..c100f8e69 --- /dev/null +++ b/core/model/src/commonMain/kotlin/org/mifospay/core/model/RoleInfo.kt @@ -0,0 +1,8 @@ +package org.mifospay.core.model + +data class RoleInfo( + val id: String, + val name: String, + val description: String, + val disabled: Boolean +) diff --git a/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserData.kt b/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserData.kt index 180afee4a..93e6a2875 100644 --- a/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserData.kt +++ b/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserData.kt @@ -10,7 +10,16 @@ package org.mifospay.core.model data class UserData( - val isAuthenticated: Boolean, - val userName: String, - val clientId: Long, + val token: String, + val name: String, + val username: String, + val email: String, + val mobileNo: String, + val userId: Int, + val clientId: Int, + val clientVpa: String, + val accountId: Int, + val firebaseRegId: String, + val client: ClientInfo, + val user: UserInfo, ) diff --git a/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserInfo.kt b/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserInfo.kt new file mode 100644 index 000000000..c5274d25d --- /dev/null +++ b/core/model/src/commonMain/kotlin/org/mifospay/core/model/UserInfo.kt @@ -0,0 +1,15 @@ +package org.mifospay.core.model + +data class UserInfo( + val username: String, + val userId: Int, + val base64EncodedAuthenticationKey: String, + val authenticated: Boolean, + val officeId: Int, + val officeName: String, + val roles: List, + val permissions: List, + val clients: List, + val shouldRenewPassword: Boolean, + val isTwoFactorAuthenticationRequired: Boolean +)